package alerts

import (
	"encoding/json"
	"os"
	"path/filepath"
	"sort"
	"strconv"
	"sync"
	"time"
)

type Status string

const (
	StatusOpen   Status = "open"
	StatusAcked  Status = "acked"
	StatusClosed Status = "closed"
)

type Alert struct {
	ID        string            `json:"id"`
	Title     string            `json:"title"`
	Severity  string            `json:"severity"`
	Status    Status            `json:"status"`
	Group     string            `json:"group"`
	Code      string            `json:"code"`
	Trace     string            `json:"trace"`
	Message   string            `json:"message"`
	CreatedAt time.Time         `json:"created_at"`
	UpdatedAt time.Time         `json:"updated_at"`
	Meta      map[string]string `json:"meta,omitempty"`
}

type Store struct {
	path string
	mu   sync.Mutex
}

func NewStore(path string) *Store {
	return &Store{path: path}
}

func (s *Store) Add(alert Alert) error {
	s.mu.Lock()
	defer s.mu.Unlock()

	alertsList, err := s.readLocked()
	if err != nil {
		return err
	}
	now := time.Now().UTC()
	if alert.ID == "" {
		alert.ID = strconv.FormatInt(now.UnixNano(), 10)
	}
	if alert.Status == "" {
		alert.Status = StatusOpen
	}
	if alert.CreatedAt.IsZero() {
		alert.CreatedAt = now
	}
	alert.UpdatedAt = now
	alertsList = append(alertsList, alert)
	return s.writeLocked(alertsList)
}

func (s *Store) List(limit int) ([]Alert, error) {
	s.mu.Lock()
	defer s.mu.Unlock()
	alertsList, err := s.readLocked()
	if err != nil {
		return nil, err
	}
	sort.Slice(alertsList, func(i, j int) bool {
		return alertsList[i].CreatedAt.After(alertsList[j].CreatedAt)
	})
	if limit > 0 && len(alertsList) > limit {
		return alertsList[:limit], nil
	}
	return alertsList, nil
}

func (s *Store) SetStatus(ids []string, status Status) error {
	s.mu.Lock()
	defer s.mu.Unlock()
	alertsList, err := s.readLocked()
	if err != nil {
		return err
	}
	target := map[string]bool{}
	for _, id := range ids {
		target[id] = true
	}
	now := time.Now().UTC()
	for i := range alertsList {
		if target[alertsList[i].ID] {
			alertsList[i].Status = status
			alertsList[i].UpdatedAt = now
		}
	}
	return s.writeLocked(alertsList)
}

func (s *Store) Delete(ids []string) error {
	s.mu.Lock()
	defer s.mu.Unlock()
	alertsList, err := s.readLocked()
	if err != nil {
		return err
	}
	target := map[string]bool{}
	for _, id := range ids {
		target[id] = true
	}
	kept := make([]Alert, 0, len(alertsList))
	for _, alert := range alertsList {
		if !target[alert.ID] {
			kept = append(kept, alert)
		}
	}
	return s.writeLocked(kept)
}

func (s *Store) readLocked() ([]Alert, error) {
	data, err := os.ReadFile(s.path)
	if err != nil {
		if os.IsNotExist(err) {
			return []Alert{}, nil
		}
		return nil, err
	}
	if len(data) == 0 {
		return []Alert{}, nil
	}
	var alertsList []Alert
	if err := json.Unmarshal(data, &alertsList); err != nil {
		return []Alert{}, nil
	}
	return alertsList, nil
}

func (s *Store) writeLocked(alertsList []Alert) error {
	if err := os.MkdirAll(filepath.Dir(s.path), 0755); err != nil {
		return err
	}
	data, err := json.MarshalIndent(alertsList, "", "  ")
	if err != nil {
		return err
	}
	return os.WriteFile(s.path, data, 0644)
}
