package emailmonitor

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

const maxSeenMessages = 2000

// State tracks last processed message IDs and query boundaries.
type State struct {
	LastAfter     int64            `json:"last_after"`
	LastHistoryID uint64           `json:"last_history_id"`
	LastWatchTime int64            `json:"last_watch_time"`
	Seen          map[string]int64 `json:"seen"`
}

func LoadState(path string) (*State, error) {
	data, err := os.ReadFile(path)
	if err != nil {
		if errors.Is(err, os.ErrNotExist) {
			return &State{Seen: make(map[string]int64)}, nil
		}
		return nil, err
	}

	var state State
	if err := json.Unmarshal(data, &state); err != nil {
		return nil, err
	}
	if state.Seen == nil {
		state.Seen = make(map[string]int64)
	}
	return &state, nil
}

func (s *State) Save(path string) error {
	if s == nil {
		return nil
	}
	data, err := json.Marshal(s)
	if err != nil {
		return err
	}
	return os.WriteFile(path, data, 0600)
}

func (s *State) SeenMessage(id string) bool {
	_, ok := s.Seen[id]
	return ok
}

func (s *State) MarkSeen(id string, when time.Time) {
	if s.Seen == nil {
		s.Seen = make(map[string]int64)
	}
	s.Seen[id] = when.Unix()
}

func (s *State) Prune(window time.Duration) {
	cutoff := time.Now().Add(-window).Unix()
	for id, ts := range s.Seen {
		if ts < cutoff {
			delete(s.Seen, id)
		}
	}

	if len(s.Seen) <= maxSeenMessages {
		return
	}

	type entry struct {
		id string
		ts int64
	}
	entries := make([]entry, 0, len(s.Seen))
	for id, ts := range s.Seen {
		entries = append(entries, entry{id: id, ts: ts})
	}

	// Keep newest maxSeenMessages by removing the oldest.
	for len(entries) > maxSeenMessages {
		oldestIdx := 0
		for i := 1; i < len(entries); i++ {
			if entries[i].ts < entries[oldestIdx].ts {
				oldestIdx = i
			}
		}
		delete(s.Seen, entries[oldestIdx].id)
		entries[oldestIdx] = entries[len(entries)-1]
		entries = entries[:len(entries)-1]
	}
}
