package main

import (
	"encoding/json"
	"os"
	"time"
)

// State tracks which tickets have already been pushed to Lark. We key by
// ticket key ALONE — once a ticket has been notified, it will never be
// re-notified (until its entry expires, see Save). Evaluation is decoupled
// from notification: every run re-evaluates all in-window tickets (so a
// ticket that goes from "boring" to "on fire" still gets caught), but the
// Lark push is suppressed for any key that is already in Sent.
type State struct {
	Sent map[string]int64 `json:"sent"` // ticketKey -> unix seconds when first notified
}

func LoadState(path string) (*State, error) {
	b, err := os.ReadFile(path)
	if os.IsNotExist(err) {
		return &State{Sent: map[string]int64{}}, nil
	}
	if err != nil {
		return nil, err
	}
	var s State
	if err := json.Unmarshal(b, &s); err != nil {
		return &State{Sent: map[string]int64{}}, nil
	}
	if s.Sent == nil {
		s.Sent = map[string]int64{}
	}
	return &s, nil
}

func (s *State) Save(path string) error {
	// Expire entries older than 7 days to keep the file small.
	cutoff := time.Now().Add(-7 * 24 * time.Hour).Unix()
	for k, ts := range s.Sent {
		if ts < cutoff {
			delete(s.Sent, k)
		}
	}
	b, err := json.MarshalIndent(s, "", "  ")
	if err != nil {
		return err
	}
	tmp := path + ".tmp"
	if err := os.WriteFile(tmp, b, 0o644); err != nil {
		return err
	}
	return os.Rename(tmp, path)
}

func (s *State) AlreadyNotified(key string) bool {
	_, ok := s.Sent[key]
	return ok
}

func (s *State) MarkNotified(key string) {
	s.Sent[key] = time.Now().Unix()
}
