package alerts

import (
	"fmt"
	"strings"
	"time"

	"github.com/pocketbase/dbx"
	"github.com/pocketbase/pocketbase/core"
)

type alertTask struct {
	action      string // "schedule" or "cancel"
	systemName  string
	alertRecord *core.Record
	delay       time.Duration
}

type alertInfo struct {
	systemName  string
	alertRecord *core.Record
	expireTime  time.Time
}

// startWorker is a long-running goroutine that processes alert tasks
// every x seconds. It must be running to process status alerts.
func (am *AlertManager) startWorker() {
	tick := time.Tick(15 * time.Second)
	for {
		select {
		case <-am.stopChan:
			return
		case task := <-am.alertQueue:
			switch task.action {
			case "schedule":
				am.pendingAlerts.Store(task.alertRecord.Id, &alertInfo{
					systemName:  task.systemName,
					alertRecord: task.alertRecord,
					expireTime:  time.Now().Add(task.delay),
				})
			case "cancel":
				am.pendingAlerts.Delete(task.alertRecord.Id)
			}
		case <-tick:
			// Check for expired alerts every tick
			now := time.Now()
			for key, value := range am.pendingAlerts.Range {
				info := value.(*alertInfo)
				if now.After(info.expireTime) {
					// Downtime delay has passed, process alert
					am.sendStatusAlert("down", info.systemName, info.alertRecord)
					am.pendingAlerts.Delete(key)
				}
			}
		}
	}
}

// StopWorker shuts down the AlertManager.worker goroutine
func (am *AlertManager) StopWorker() {
	close(am.stopChan)
}

// HandleStatusAlerts manages the logic when system status changes.
func (am *AlertManager) HandleStatusAlerts(newStatus string, systemRecord *core.Record) error {
	if newStatus != "up" && newStatus != "down" {
		return nil
	}

	alertRecords, err := am.getSystemStatusAlerts(systemRecord.Id)
	if err != nil {
		return err
	}
	if len(alertRecords) == 0 {
		return nil
	}

	systemName := systemRecord.GetString("name")
	if newStatus == "down" {
		am.handleSystemDown(systemName, alertRecords)
	} else {
		am.handleSystemUp(systemName, alertRecords)
	}
	return nil
}

// getSystemStatusAlerts retrieves all "Status" alert records for a given system ID.
func (am *AlertManager) getSystemStatusAlerts(systemID string) ([]*core.Record, error) {
	alertRecords, err := am.hub.FindAllRecords("alerts", dbx.HashExp{
		"system": systemID,
		"name":   "Status",
	})
	if err != nil {
		return nil, err
	}
	return alertRecords, nil
}

// Schedules delayed "down" alerts for each alert record.
func (am *AlertManager) handleSystemDown(systemName string, alertRecords []*core.Record) {
	for _, alertRecord := range alertRecords {
		// Continue if alert is already scheduled
		if _, exists := am.pendingAlerts.Load(alertRecord.Id); exists {
			continue
		}
		// Schedule by adding to queue
		min := max(1, alertRecord.GetInt("min"))
		am.alertQueue <- alertTask{
			action:      "schedule",
			systemName:  systemName,
			alertRecord: alertRecord,
			delay:       time.Duration(min) * time.Minute,
		}
	}
}

// handleSystemUp manages the logic when a system status changes to "up".
// It cancels any pending alerts and sends "up" alerts.
func (am *AlertManager) handleSystemUp(systemName string, alertRecords []*core.Record) {
	for _, alertRecord := range alertRecords {
		alertRecordID := alertRecord.Id
		// If alert exists for record, delete and continue (down alert not sent)
		if _, exists := am.pendingAlerts.Load(alertRecordID); exists {
			am.alertQueue <- alertTask{
				action:      "cancel",
				alertRecord: alertRecord,
			}
			continue
		}
		// No alert scheduled for this record, send "up" alert
		if err := am.sendStatusAlert("up", systemName, alertRecord); err != nil {
			am.hub.Logger().Error("Failed to send alert", "err", err)
		}
	}
}

// sendStatusAlert sends a status alert ("up" or "down") to the users associated with the alert records.
func (am *AlertManager) sendStatusAlert(alertStatus string, systemName string, alertRecord *core.Record) error {
	switch alertStatus {
	case "up":
		alertRecord.Set("triggered", false)
	case "down":
		alertRecord.Set("triggered", true)
	}
	am.hub.Save(alertRecord)

	var emoji string
	if alertStatus == "up" {
		emoji = "\u2705" // Green checkmark emoji
	} else {
		emoji = "\U0001F534" // Red alert emoji
	}

	title := fmt.Sprintf("Connection to %s is %s %v", systemName, alertStatus, emoji)
	message := strings.TrimSuffix(title, emoji)

	// if errs := am.hub.ExpandRecord(alertRecord, []string{"user"}, nil); len(errs) > 0 {
	// 	return errs["user"]
	// }
	// user := alertRecord.ExpandedOne("user")
	// if user == nil {
	// 	return nil
	// }

	return am.SendAlert(AlertMessageData{
		UserID:   alertRecord.GetString("user"),
		Title:    title,
		Message:  message,
		Link:     am.hub.MakeLink("system", systemName),
		LinkText: "View " + systemName,
	})
}
