
package notification

import (
	"bytes"
	"encoding/json"
	"fmt"
	"net/http"
	"strings"
	"text/template"
	"time"
)

// WebhookService handles webhook notifications
type WebhookService struct{}

// NewWebhookService creates a new webhook notification service
func NewWebhookService() *WebhookService {
	return &WebhookService{}
}

// WebhookPayload represents the payload for webhook
type WebhookPayload struct {
	Message     string `json:"message"`
	NotifyName  string `json:"notify_name"`
	Timestamp   string `json:"timestamp"`
	ServiceName string `json:"service_name,omitempty"`
	Status      string `json:"status,omitempty"`
	Host        string `json:"host,omitempty"`
	URL         string `json:"url,omitempty"`
	Port        int    `json:"port,omitempty"`
	ServiceType string `json:"service_type,omitempty"`
	ResponseTime int64 `json:"response_time,omitempty"`
	// Server monitoring fields
	CPUUsage     string `json:"cpu_usage,omitempty"`
	RAMUsage     string `json:"ram_usage,omitempty"`
	DiskUsage    string `json:"disk_usage,omitempty"`
	NetworkUsage string `json:"network_usage,omitempty"`
	CPUTemp      string `json:"cpu_temp,omitempty"`
	DiskIO       string `json:"disk_io,omitempty"`
	Threshold    string `json:"threshold,omitempty"`
	// SSL fields
	CertificateName string `json:"certificate_name,omitempty"`
	ExpiryDate      string `json:"expiry_date,omitempty"`
	DaysLeft        string `json:"days_left,omitempty"`
	IssuerCN        string `json:"issuer_cn,omitempty"`
}

// DiscordWebhookPayload represents Discord-specific webhook payload
type DiscordWebhookPayload struct {
	Content string `json:"content"`
}

// SendNotification sends a notification via webhook
func (ws *WebhookService) SendNotification(config *AlertConfiguration, message string) error {
	// fmt.Printf("📡 [WEBHOOK] Attempting to send notification...\n")
	// fmt.Printf("📡 [WEBHOOK] Config - URL: %s, Notify Name: %s\n", config.WebhookURL, config.NotifyName)
	// fmt.Printf("📡 [WEBHOOK] Message: %s\n", message)

	// Use the webhook_url field from alert_configurations
	if config.WebhookURL == "" {
		return fmt.Errorf("webhook URL is required")
	}

	// Create payload using webhook_payload_template if available
	var jsonData []byte
	var err error

	if config.WebhookPayloadTemplate != "" && strings.TrimSpace(config.WebhookPayloadTemplate) != "" {
		// Use custom payload template
		jsonData, err = ws.generateCustomPayload(config, message)
		if err != nil {
			// fmt.Printf("❌ [WEBHOOK] Custom payload generation error: %v\n", err)
			return err
		}
	} else {
		// Detect Discord webhook and use appropriate payload
		if strings.Contains(strings.ToLower(config.WebhookURL), "discord") {
			jsonData, err = ws.generateDiscordPayload(message)
		} else {
			// Use default payload structure for other webhooks
			jsonData, err = ws.generateDefaultPayload(config, message)
		}
		
		if err != nil {
			// fmt.Printf("❌ [WEBHOOK] JSON marshal error: %v\n", err)
			return err
		}
	}

	// fmt.Printf("📡 [WEBHOOK] Payload: %s\n", string(jsonData))
	// fmt.Printf("📡 [WEBHOOK] Sending POST request...\n")
	
	resp, err := http.Post(config.WebhookURL, "application/json", bytes.NewBuffer(jsonData))
	if err != nil {
		// fmt.Printf("❌ [WEBHOOK] HTTP error: %v\n", err)
		return err
	}
	defer resp.Body.Close()

	if resp.StatusCode < 200 || resp.StatusCode >= 300 {
		// fmt.Printf("❌ [WEBHOOK] API error, status: %d\n", resp.StatusCode)
		return fmt.Errorf("webhook error, status: %d", resp.StatusCode)
	}

	// fmt.Printf("✅ [WEBHOOK] Message sent successfully!\n")
	return nil
}

// generateDiscordPayload generates a Discord-compatible webhook payload
func (ws *WebhookService) generateDiscordPayload(message string) ([]byte, error) {
	payload := DiscordWebhookPayload{
		Content: message,
	}
	return json.Marshal(payload)
}

// generateDefaultPayload generates the default webhook payload
func (ws *WebhookService) generateDefaultPayload(config *AlertConfiguration, message string) ([]byte, error) {
	payload := WebhookPayload{
		Message:    message,
		NotifyName: config.NotifyName,
		Timestamp:  time.Now().Format(time.RFC3339),
	}
	return json.Marshal(payload)
}

// generateCustomPayload generates payload using the custom template
func (ws *WebhookService) generateCustomPayload(config *AlertConfiguration, message string) ([]byte, error) {
	// Parse the template
	tmpl, err := template.New("webhook").Parse(config.WebhookPayloadTemplate)
	if err != nil {
		return nil, fmt.Errorf("failed to parse webhook payload template: %v", err)
	}

	// Create template data
	templateData := map[string]interface{}{
		"message":     message,
		"notify_name": config.NotifyName,
		"timestamp":   time.Now().Format(time.RFC3339),
	}

	// Execute template
	var buf bytes.Buffer
	err = tmpl.Execute(&buf, templateData)
	if err != nil {
		return nil, fmt.Errorf("failed to execute webhook payload template: %v", err)
	}

	// Validate that the result is valid JSON
	var jsonCheck interface{}
	if err := json.Unmarshal(buf.Bytes(), &jsonCheck); err != nil {
		return nil, fmt.Errorf("webhook payload template did not generate valid JSON: %v", err)
	}

	return buf.Bytes(), nil
}

// SendServerNotification sends a server-specific notification via webhook
func (ws *WebhookService) SendServerNotification(config *AlertConfiguration, payload *NotificationPayload, template *ServerNotificationTemplate, resourceType string) error {
	message := ws.generateServerMessage(payload, template, resourceType)
	return ws.SendNotification(config, message)
}

// SendServiceNotification sends a service-specific notification via webhook
func (ws *WebhookService) SendServiceNotification(config *AlertConfiguration, payload *NotificationPayload, template *ServiceNotificationTemplate) error {
	message := ws.generateServiceMessage(payload, template)
	return ws.SendNotification(config, message)
}

// generateServerMessage creates a message for server notifications using server template
func (ws *WebhookService) generateServerMessage(payload *NotificationPayload, template *ServerNotificationTemplate, resourceType string) string {
	var templateMessage string
	
	// Select appropriate template message based on status and resource type
	switch strings.ToLower(payload.Status) {
	case "down":
		templateMessage = template.DownMessage
	case "up":
		templateMessage = template.UpMessage
	case "warning":
		templateMessage = template.WarningMessage
	case "paused":
		templateMessage = template.PausedMessage
	default:
		// Handle resource-specific messages
		switch resourceType {
		case "cpu":
			if strings.Contains(strings.ToLower(payload.Status), "restore") {
				templateMessage = template.RestoreCPUMessage
			} else {
				templateMessage = template.CPUMessage
			}
		case "ram", "memory":
			if strings.Contains(strings.ToLower(payload.Status), "restore") {
				templateMessage = template.RestoreRAMMessage
			} else {
				templateMessage = template.RAMMessage
			}
		case "disk":
			if strings.Contains(strings.ToLower(payload.Status), "restore") {
				templateMessage = template.RestoreDiskMessage
			} else {
				templateMessage = template.DiskMessage
			}
		case "network":
			if strings.Contains(strings.ToLower(payload.Status), "restore") {
				templateMessage = template.RestoreNetworkMessage
			} else {
				templateMessage = template.NetworkMessage
			}
		case "cpu_temp", "cpu_temperature":
			if strings.Contains(strings.ToLower(payload.Status), "restore") {
				templateMessage = template.RestoreCPUTempMessage
			} else {
				templateMessage = template.CPUTempMessage
			}
		case "disk_io":
			if strings.Contains(strings.ToLower(payload.Status), "restore") {
				templateMessage = template.RestoreDiskIOMessage
			} else {
				templateMessage = template.DiskIOMessage
			}
		default:
			templateMessage = template.WarningMessage
		}
	}
	
	// If no template message found, use a default
	if templateMessage == "" {
		templateMessage = ws.generateDefaultServerMessage(payload, resourceType)
	}
	
	return ws.replacePlaceholders(templateMessage, payload)
}

// generateServiceMessage creates a message for service notifications using service template
func (ws *WebhookService) generateServiceMessage(payload *NotificationPayload, template *ServiceNotificationTemplate) string {
	var templateMessage string
	
	// Select appropriate template message based on status
	switch strings.ToLower(payload.Status) {
	case "up":
		templateMessage = template.UpMessage
	case "down":
		templateMessage = template.DownMessage
	case "maintenance":
		templateMessage = template.MaintenanceMessage
	case "incident":
		templateMessage = template.IncidentMessage
	case "resolved":
		templateMessage = template.ResolvedMessage
	case "warning":
		templateMessage = template.WarningMessage
	default:
		templateMessage = template.WarningMessage
	}
	
	// If no template message found, use a default
	if templateMessage == "" {
		templateMessage = ws.generateDefaultUptimeMessage(payload)
	}
	
	return ws.replacePlaceholders(templateMessage, payload)
}

// replacePlaceholders replaces all placeholders in the message with actual values
func (ws *WebhookService) replacePlaceholders(message string, payload *NotificationPayload) string {
	// Replace basic placeholders
	message = strings.ReplaceAll(message, "${service_name}", payload.ServiceName)
	message = strings.ReplaceAll(message, "${server_name}", payload.ServiceName) // server_name maps to service_name
	message = strings.ReplaceAll(message, "${status}", strings.ToUpper(payload.Status))
	message = strings.ReplaceAll(message, "${host}", ws.safeString(payload.Host))
	message = strings.ReplaceAll(message, "${hostname}", ws.safeString(payload.Hostname))
	
	// Replace URL with fallback to host
	url := ws.safeString(payload.URL)
	if url == "N/A" && payload.Host != "" {
		url = payload.Host
	}
	message = strings.ReplaceAll(message, "${url}", url)
	
	// Replace domain
	message = strings.ReplaceAll(message, "${domain}", ws.safeString(payload.Domain))
	
	// Replace service type
	if payload.ServiceType != "" {
		message = strings.ReplaceAll(message, "${service_type}", strings.ToUpper(payload.ServiceType))
	} else {
		message = strings.ReplaceAll(message, "${service_type}", "N/A")
	}
	
	// Replace region and agent info
	message = strings.ReplaceAll(message, "${region_name}", ws.safeString(payload.RegionName))
	message = strings.ReplaceAll(message, "${agent_id}", ws.safeString(payload.AgentID))
	
	// Handle numeric fields safely
	if payload.Port > 0 {
		message = strings.ReplaceAll(message, "${port}", fmt.Sprintf("%d", payload.Port))
	} else {
		message = strings.ReplaceAll(message, "${port}", "N/A")
	}
	
	if payload.ResponseTime > 0 {
		message = strings.ReplaceAll(message, "${response_time}", fmt.Sprintf("%dms", payload.ResponseTime))
	} else {
		message = strings.ReplaceAll(message, "${response_time}", "N/A")
	}
	
	if payload.Uptime > 0 {
		message = strings.ReplaceAll(message, "${uptime}", fmt.Sprintf("%d%%", payload.Uptime))
	} else {
		message = strings.ReplaceAll(message, "${uptime}", "N/A")
	}
	
	// Replace server monitoring fields
	message = strings.ReplaceAll(message, "${cpu_usage}", ws.safeString(payload.CPUUsage))
	message = strings.ReplaceAll(message, "${ram_usage}", ws.safeString(payload.RAMUsage))
	message = strings.ReplaceAll(message, "${disk_usage}", ws.safeString(payload.DiskUsage))
	message = strings.ReplaceAll(message, "${network_usage}", ws.safeString(payload.NetworkUsage))
	message = strings.ReplaceAll(message, "${cpu_temp}", ws.safeString(payload.CPUTemp))
	message = strings.ReplaceAll(message, "${disk_io}", ws.safeString(payload.DiskIO))
	message = strings.ReplaceAll(message, "${threshold}", ws.safeString(payload.Threshold))
	
	// Replace SSL certificate fields
	message = strings.ReplaceAll(message, "${certificate_name}", ws.safeString(payload.CertificateName))
	message = strings.ReplaceAll(message, "${expiry_date}", ws.safeString(payload.ExpiryDate))
	message = strings.ReplaceAll(message, "${days_left}", ws.safeString(payload.DaysLeft))
	message = strings.ReplaceAll(message, "${issuer_cn}", ws.safeString(payload.IssuerCN))
	message = strings.ReplaceAll(message, "${issuer}", ws.safeString(payload.IssuerCN)) // alias
	
	// Replace error message - important for uptime services
	message = strings.ReplaceAll(message, "${error_message}", ws.safeString(payload.ErrorMessage))
	message = strings.ReplaceAll(message, "${error}", ws.safeString(payload.ErrorMessage))
	message = strings.ReplaceAll(message, "${message}", ws.safeString(payload.Message))
	
	// Replace time placeholders
	message = strings.ReplaceAll(message, "${time}", payload.Timestamp.Format("2006-01-02 15:04:05"))
	message = strings.ReplaceAll(message, "${timestamp}", payload.Timestamp.Format("2006-01-02 15:04:05"))
	
	return message
}

// safeString returns the string value or "N/A" if empty
func (ws *WebhookService) safeString(value string) string {
	if value == "" {
		return "N/A"
	}
	return value
}

// generateDefaultUptimeMessage creates a default uptime message with proper formatting
func (ws *WebhookService) generateDefaultUptimeMessage(payload *NotificationPayload) string {
	// Status emoji mapping
	statusEmoji := "🔵"
	switch strings.ToLower(payload.Status) {
	case "up":
		statusEmoji = "🟢"
	case "down":
		statusEmoji = "🔴"
	case "warning":
		statusEmoji = "🟡"
	case "maintenance", "paused":
		statusEmoji = "🟠"
	}
	
	message := fmt.Sprintf("%s Service %s is %s.", statusEmoji, payload.ServiceName, strings.ToUpper(payload.Status))
	
	// Build formatted details
	details := []string{}
	
	// Add URL or host
	if payload.URL != "" {
		details = append(details, fmt.Sprintf(" - Host URL: %s", payload.URL))
	} else if payload.Host != "" {
		details = append(details, fmt.Sprintf(" - Host: %s", payload.Host))
	}
	
	// Add service type
	if payload.ServiceType != "" {
		details = append(details, fmt.Sprintf(" - Type: %s", strings.ToUpper(payload.ServiceType)))
	}
	
	// Add port if available
	if payload.Port > 0 {
		details = append(details, fmt.Sprintf(" - Port: %d", payload.Port))
	}
	
	// Add domain if available
	if payload.Domain != "" {
		details = append(details, fmt.Sprintf(" - Domain: %s", payload.Domain))
	}
	
	// Add response time
	if payload.ResponseTime > 0 {
		details = append(details, fmt.Sprintf(" - Response time: %dms", payload.ResponseTime))
	} else {
		details = append(details, " - Response time: N/A")
	}
	
	// Add region info
	if payload.RegionName != "" {
		details = append(details, fmt.Sprintf(" - Region: %s", payload.RegionName))
	}
	
	// Add agent info
	if payload.AgentID != "" {
		details = append(details, fmt.Sprintf(" - Agent: %s", payload.AgentID))
	}
	
	// Add uptime if available
	if payload.Uptime > 0 {
		details = append(details, fmt.Sprintf(" - Uptime: %d%%", payload.Uptime))
	}
	
	// Add timestamp
	details = append(details, fmt.Sprintf(" - Time: %s", payload.Timestamp.Format("2006-01-02 15:04:05")))
	
	// Combine message with details
	if len(details) > 0 {
		message += "\n" + strings.Join(details, "\n")
	}
	
	return message
}

// generateDefaultServerMessage creates a default server message
func (ws *WebhookService) generateDefaultServerMessage(payload *NotificationPayload, resourceType string) string {
	statusEmoji := "🔵"
	switch strings.ToLower(payload.Status) {
	case "up":
		statusEmoji = "🟢"
	case "down":
		statusEmoji = "🔴"
	case "warning":
		statusEmoji = "🟡"
	}
	
	return fmt.Sprintf("%s 🖥️ Server %s (%s) status: %s", statusEmoji, payload.ServiceName, payload.Hostname, strings.ToUpper(payload.Status))
}