package notifications

import (
	"bytes"
	"context"
	"encoding/json"
	"fmt"
	"log"
	"net/http"
	"time"
)

const (
	// discordHTTPTimeoutSeconds is the timeout for Discord webhook requests.
	discordHTTPTimeoutSeconds = 10

	// Alert color codes for Discord embeds.
	colorGreen  = 0x00ff00
	colorOrange = 0xff9900
	colorRed    = 0xff0000
	colorBlue   = 0x0099ff
)

var discordHTTPClient = &http.Client{ //nolint:gochecknoglobals
	Timeout: discordHTTPTimeoutSeconds * time.Second,
}

//  sends alert notifications via a Discord webhook.
type DiscordNotifier struct {
	WebhookURL string
}
func (d *DiscordNotifier) Send(_ context.Context, message string, meta AlertMetadata) error {
	_, err := SendDiscordNotification(d.WebhookURL, message, meta)
	return err
}

type discordEmbed struct {
	Title       string         `json:"title"`
	Description string         `json:"description"`
	Color       int            `json:"color"`
	Fields      []discordField `json:"fields"`
	Timestamp   string         `json:"timestamp"`
	Footer      discordFooter  `json:"footer"`
}

type discordField struct {
	Name   string `json:"name"`
	Value  string `json:"value"`
	Inline bool   `json:"inline"`
}

type discordFooter struct {
	Text string `json:"text"`
}

type discordPayload struct {
	Content *string        `json:"content"`
	Embeds  []discordEmbed `json:"embeds,omitempty"`
}

func SendDiscordNotification(webhookURL, message string, meta AlertMetadata) (bool, error) {
	fields := []discordField{
		{Name: "Address", Value: meta.AddressLabel, Inline: true},
		{Name: "Blockchain Address", Value: fmt.Sprintf("`%s`", meta.Address), Inline: false},
	}

	if meta.TxHash != "" {
		fields = append(fields, discordField{
			Name:   "Transaction",
			Value:  fmt.Sprintf("[View on Etherscan](https://etherscan.io/tx/%s)", meta.TxHash),
			Inline: false,
		})
	}

	payload := discordPayload{
		Content: nil,
		Embeds: []discordEmbed{
			{
				Title:       "Koin Ping Alert",
				Description: message,
				Color:       colorForAlertType(meta.AlertType),
				Fields:      fields,
				Timestamp:   time.Now().UTC().Format(time.RFC3339),
				Footer:      discordFooter{Text: "Koin Ping"},
			},
		},
	}

	body, err := json.Marshal(payload)
	if err != nil {
		return false, fmt.Errorf("marshal discord payload: %w", err)
	}

	resp, err := discordHTTPClient.Post(
		webhookURL, "application/json", bytes.NewReader(body),
	)
	if err != nil {
		log.Printf("Failed to send Discord notification: %v", err)
		return false, err
	}
	defer resp.Body.Close()

	if resp.StatusCode < 200 || resp.StatusCode >= 300 {
		err := fmt.Errorf("discord webhook failed: HTTP %d", resp.StatusCode)
		log.Printf("Discord webhook failed: HTTP %d", resp.StatusCode)
		if isPermanentStatusCode(resp.StatusCode) {
			return false, &PermanentError{Err: err}
		}
		return false, err
	}

	return true, nil
}

func TestDiscordWebhook(webhookURL string) (bool, error) {
	payload := map[string]string{
		"content": "Koin Ping test notification - Your Discord webhook is configured correctly!",
	}

	body, err := json.Marshal(payload)
	if err != nil {
		return false, err
	}

	resp, err := discordHTTPClient.Post(
		webhookURL, "application/json", bytes.NewReader(body),
	)
	if err != nil {
		log.Printf("Discord webhook test failed: %v", err)
		return false, err
	}
	defer resp.Body.Close()

	return resp.StatusCode >= 200 && resp.StatusCode < 300, nil
}

func colorForAlertType(alertType string) int {
	switch alertType {
	case "incoming_tx":
		return colorGreen
	case "outgoing_tx":
		return colorOrange
	case "large_transfer":
		return colorRed
	case "balance_below":
		return colorRed
	default:
		return colorBlue
	}
}
