package activity

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

type Event struct {
	ID        string            `json:"id"`
	Kind      string            `json:"kind"`
	Severity  string            `json:"severity"`
	Message   string            `json:"message"`
	Actor     string            `json:"actor"`
	IP        string            `json:"ip"`
	Path      string            `json:"path"`
	Method    string            `json:"method"`
	CreatedAt time.Time         `json:"created_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) Append(event Event, retentionSeconds int64) error {
	s.mu.Lock()
	defer s.mu.Unlock()

	events, err := s.readLocked()
	if err != nil {
		return err
	}
	if event.CreatedAt.IsZero() {
		event.CreatedAt = time.Now().UTC()
	}
	if event.ID == "" {
		event.ID = event.CreatedAt.Format("20060102T150405.000000000")
	}

	events = append(events, event)
	events = pruneByRetention(events, retentionSeconds)
	return s.writeLocked(events)
}

func (s *Store) List(limit int, retentionSeconds int64) ([]Event, error) {
	s.mu.Lock()
	defer s.mu.Unlock()

	events, err := s.readLocked()
	if err != nil {
		return nil, err
	}
	events = pruneByRetention(events, retentionSeconds)
	if err := s.writeLocked(events); err != nil {
		return nil, err
	}
	sort.Slice(events, func(i, j int) bool {
		return events[i].CreatedAt.After(events[j].CreatedAt)
	})
	if limit > 0 && len(events) > limit {
		return events[:limit], nil
	}
	return events, nil
}

func pruneByRetention(events []Event, retentionSeconds int64) []Event {
	if retentionSeconds <= 0 {
		return events
	}
	cutoff := time.Now().UTC().Add(-time.Duration(retentionSeconds) * time.Second)
	out := make([]Event, 0, len(events))
	for _, event := range events {
		if event.CreatedAt.IsZero() || event.CreatedAt.After(cutoff) {
			out = append(out, event)
		}
	}
	return out
}

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

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