// Package state manages daemon state persistence.
package state

import (
	"context"
	"encoding/json"
	"fmt"
	"log/slog"
	"os"
	"path/filepath"
	"time"

	"git.eeqj.de/sneak/ipapi/internal/config"
)

const (
	stateFileName   = "daemon.json"
	dirPermissions  = 0750
	filePermissions = 0600
)

// State holds the daemon's persistent state information.
type State struct {
	LastASNDownload     time.Time `json:"lastAsnDownload"`
	LastCityDownload    time.Time `json:"lastCityDownload"`
	LastCountryDownload time.Time `json:"lastCountryDownload"`
}

// Manager handles state file operations.
type Manager struct {
	config    *config.Config
	logger    *slog.Logger
	statePath string
}

// New creates a new state manager.
func New(cfg *config.Config, logger *slog.Logger) (*Manager, error) {
	statePath := filepath.Join(cfg.StateDir, stateFileName)

	return &Manager{
		config:    cfg,
		logger:    logger,
		statePath: statePath,
	}, nil
}

// Initialize ensures the state directory exists.
func (m *Manager) Initialize(_ context.Context) error {
	// Ensure state directory exists
	dir := filepath.Dir(m.statePath)
	if err := os.MkdirAll(dir, dirPermissions); err != nil {
		return fmt.Errorf("failed to create state directory: %w", err)
	}

	m.logger.Info("State manager initialized", "path", m.statePath)

	return nil
}

// Load reads the state from disk.
func (m *Manager) Load() (*State, error) {
	data, err := os.ReadFile(m.statePath)
	if err != nil {
		if os.IsNotExist(err) {
			// Return empty state if file doesn't exist
			return &State{}, nil
		}

		return nil, fmt.Errorf("failed to read state file: %w", err)
	}

	var state State
	if err := json.Unmarshal(data, &state); err != nil {
		return nil, fmt.Errorf("failed to parse state file: %w", err)
	}

	return &state, nil
}

// Save writes the state to disk.
func (m *Manager) Save(state *State) error {
	data, err := json.MarshalIndent(state, "", "  ")
	if err != nil {
		return fmt.Errorf("failed to marshal state: %w", err)
	}

	// Write to temporary file first
	tmpPath := m.statePath + ".tmp"
	if err := os.WriteFile(tmpPath, data, filePermissions); err != nil {
		return fmt.Errorf("failed to write state file: %w", err)
	}

	// Rename to final path (atomic operation)
	if err := os.Rename(tmpPath, m.statePath); err != nil {
		return fmt.Errorf("failed to save state file: %w", err)
	}

	m.logger.Debug("State saved", "path", m.statePath)

	return nil
}

// UpdateASNDownloadTime updates the ASN database download timestamp.
func (m *Manager) UpdateASNDownloadTime() error {
	state, err := m.Load()
	if err != nil {
		return err
	}
	state.LastASNDownload = time.Now().UTC()

	return m.Save(state)
}

// UpdateCityDownloadTime updates the City database download timestamp.
func (m *Manager) UpdateCityDownloadTime() error {
	state, err := m.Load()
	if err != nil {
		return err
	}
	state.LastCityDownload = time.Now().UTC()

	return m.Save(state)
}

// UpdateCountryDownloadTime updates the Country database download timestamp.
func (m *Manager) UpdateCountryDownloadTime() error {
	state, err := m.Load()
	if err != nil {
		return err
	}
	state.LastCountryDownload = time.Now().UTC()

	return m.Save(state)
}
