package db

import (
	"database/sql"
	"fmt"
	"os"
	"path/filepath"
	"time"

	_ "modernc.org/sqlite"
)

type Alarm struct {
	ID            int
	Name          string
	Description   string
	Time          string // HH:MM format for simple alarms
	Trigger       string // Cron expression or "once"
	SoundPath     string
	Enabled       bool
	CreatedAt     time.Time
	UpdatedAt     time.Time
	LastTriggered *time.Time
	SnoozeCount   int
}

type AlarmHistory struct {
	ID          int
	AlarmID     int
	TriggeredAt time.Time
	DismissedAt *time.Time
	SnoozedCount int
}

type Store struct {
	db *sql.DB
}

func Open() (*Store, error) {
	home, err := os.UserHomeDir()
	if err != nil {
		return nil, fmt.Errorf("get home dir: %w", err)
	}

	dir := filepath.Join(home, ".config", "woke")
	if err := os.MkdirAll(dir, 0755); err != nil {
		return nil, fmt.Errorf("create config dir: %w", err)
	}

	dbPath := filepath.Join(dir, "woke.db")
	db, err := sql.Open("sqlite", dbPath)
	if err != nil {
		return nil, fmt.Errorf("open database: %w", err)
	}

	store := &Store{db: db}
	if err := store.migrate(); err != nil {
		db.Close()
		return nil, fmt.Errorf("migrate: %w", err)
	}

	return store, nil
}

func (s *Store) Close() error {
	return s.db.Close()
}

func (s *Store) migrate() error {
	_, err := s.db.Exec(`
		CREATE TABLE IF NOT EXISTS alarms (
			id INTEGER PRIMARY KEY AUTOINCREMENT,
			name TEXT NOT NULL,
			description TEXT DEFAULT '',
			time TEXT DEFAULT '',
			trigger TEXT NOT NULL,
			sound_path TEXT NOT NULL DEFAULT 'default',
			enabled BOOLEAN DEFAULT 1,
			created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
			updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
			last_triggered TIMESTAMP,
			snooze_count INTEGER DEFAULT 0
		);

		CREATE TABLE IF NOT EXISTS alarm_history (
			id INTEGER PRIMARY KEY AUTOINCREMENT,
			alarm_id INTEGER,
			triggered_at TIMESTAMP,
			dismissed_at TIMESTAMP,
			snoozed_count INTEGER DEFAULT 0,
			FOREIGN KEY (alarm_id) REFERENCES alarms(id)
		);

		CREATE TABLE IF NOT EXISTS settings (
			key TEXT PRIMARY KEY,
			value TEXT
		);
	`)
	return err
}

func (s *Store) ListAlarms() ([]Alarm, error) {
	rows, err := s.db.Query(`
		SELECT id, name, description, time, trigger, sound_path, enabled,
		       created_at, updated_at, last_triggered, snooze_count
		FROM alarms ORDER BY time ASC, name ASC
	`)
	if err != nil {
		return nil, err
	}
	defer rows.Close()

	var alarms []Alarm
	for rows.Next() {
		var a Alarm
		var lastTriggered sql.NullTime
		err := rows.Scan(&a.ID, &a.Name, &a.Description, &a.Time, &a.Trigger,
			&a.SoundPath, &a.Enabled, &a.CreatedAt, &a.UpdatedAt,
			&lastTriggered, &a.SnoozeCount)
		if err != nil {
			return nil, err
		}
		if lastTriggered.Valid {
			a.LastTriggered = &lastTriggered.Time
		}
		alarms = append(alarms, a)
	}
	return alarms, rows.Err()
}

func (s *Store) GetAlarm(id int) (Alarm, error) {
	var a Alarm
	var lastTriggered sql.NullTime
	err := s.db.QueryRow(`
		SELECT id, name, description, time, trigger, sound_path, enabled,
		       created_at, updated_at, last_triggered, snooze_count
		FROM alarms WHERE id = ?
	`, id).Scan(&a.ID, &a.Name, &a.Description, &a.Time, &a.Trigger,
		&a.SoundPath, &a.Enabled, &a.CreatedAt, &a.UpdatedAt,
		&lastTriggered, &a.SnoozeCount)
	if lastTriggered.Valid {
		a.LastTriggered = &lastTriggered.Time
	}
	return a, err
}

func (s *Store) CreateAlarm(a Alarm) (int, error) {
	result, err := s.db.Exec(`
		INSERT INTO alarms (name, description, time, trigger, sound_path, enabled)
		VALUES (?, ?, ?, ?, ?, ?)
	`, a.Name, a.Description, a.Time, a.Trigger, a.SoundPath, a.Enabled)
	if err != nil {
		return 0, err
	}
	id, err := result.LastInsertId()
	return int(id), err
}

func (s *Store) UpdateAlarm(a Alarm) error {
	_, err := s.db.Exec(`
		UPDATE alarms SET name=?, description=?, time=?, trigger=?, sound_path=?,
		       enabled=?, updated_at=CURRENT_TIMESTAMP
		WHERE id=?
	`, a.Name, a.Description, a.Time, a.Trigger, a.SoundPath, a.Enabled, a.ID)
	return err
}

func (s *Store) DeleteAlarm(id int) error {
	_, err := s.db.Exec(`DELETE FROM alarms WHERE id=?`, id)
	return err
}

func (s *Store) ToggleAlarm(id int) error {
	_, err := s.db.Exec(`
		UPDATE alarms SET enabled = NOT enabled, updated_at = CURRENT_TIMESTAMP WHERE id = ?
	`, id)
	return err
}

func (s *Store) MarkTriggered(id int) error {
	now := time.Now()
	_, err := s.db.Exec(`
		UPDATE alarms SET last_triggered = ?, updated_at = CURRENT_TIMESTAMP WHERE id = ?
	`, now, id)
	if err != nil {
		return err
	}
	_, err = s.db.Exec(`
		INSERT INTO alarm_history (alarm_id, triggered_at) VALUES (?, ?)
	`, id, now)
	return err
}

// Settings holds all configurable app settings.
type Settings struct {
	SnoozeMinutes  int
	TimeoutMinutes int
	DefaultSound   string
	BlinkOnMs      int
	BlinkOffMs     int
	ColorClock     string
	ColorAlarm     string
	ShowSeconds    bool

	// Client/server mode
	ServerURL      string // If set, run as client connecting to this URL
	ServerPassword string // Password for authenticating to remote server
	APIPassword    string // Password required for incoming API requests (host mode)
	PollSeconds    int    // How often to poll server in client mode

	// Crescendo volume control (PulseAudio)
	CrescendoEnabled   bool // Whether to gradually raise volume
	CrescendoStartPct  int  // Starting volume percentage (0-100)
	CrescendoEndPct    int  // Ending volume percentage (0-100)
	CrescendoDurationS int  // Seconds to ramp from start to end

	// Callback script
	CallbackScript string // Path to script called on alarm events (start, dismiss, snooze)
}

func DefaultSettings() Settings {
	return Settings{
		SnoozeMinutes:      5,
		TimeoutMinutes:     5,
		DefaultSound:       "default",
		BlinkOnMs:          1000,
		BlinkOffMs:         500,
		ColorClock:         "#00FF88",
		ColorAlarm:         "#FF4444",
		ShowSeconds:        true,
		ServerURL:          "",
		ServerPassword:     "",
		APIPassword:        "",
		PollSeconds:        5,
		CrescendoEnabled:   false,
		CrescendoStartPct:  20,
		CrescendoEndPct:    100,
		CrescendoDurationS: 60,
		CallbackScript:     "",
	}
}

func (s *Store) LoadSettings() Settings {
	cfg := DefaultSettings()
	cfg.SnoozeMinutes = s.getSettingInt("snooze_minutes", cfg.SnoozeMinutes)
	cfg.TimeoutMinutes = s.getSettingInt("timeout_minutes", cfg.TimeoutMinutes)
	cfg.DefaultSound = s.GetSetting("default_sound", cfg.DefaultSound)
	cfg.BlinkOnMs = s.getSettingInt("blink_on_ms", cfg.BlinkOnMs)
	cfg.BlinkOffMs = s.getSettingInt("blink_off_ms", cfg.BlinkOffMs)
	cfg.ColorClock = s.GetSetting("color_clock", cfg.ColorClock)
	cfg.ColorAlarm = s.GetSetting("color_alarm", cfg.ColorAlarm)
	cfg.ShowSeconds = s.GetSetting("show_seconds", "true") == "true"
	cfg.ServerURL = s.GetSetting("server_url", cfg.ServerURL)
	cfg.ServerPassword = s.GetSetting("server_password", cfg.ServerPassword)
	cfg.APIPassword = s.GetSetting("api_password", cfg.APIPassword)
	cfg.PollSeconds = s.getSettingInt("poll_seconds", cfg.PollSeconds)
	cfg.CrescendoEnabled = s.GetSetting("crescendo_enabled", "false") == "true"
	cfg.CrescendoStartPct = s.getSettingInt("crescendo_start_pct", cfg.CrescendoStartPct)
	cfg.CrescendoEndPct = s.getSettingInt("crescendo_end_pct", cfg.CrescendoEndPct)
	cfg.CrescendoDurationS = s.getSettingInt("crescendo_duration_s", cfg.CrescendoDurationS)
	cfg.CallbackScript = s.GetSetting("callback_script", cfg.CallbackScript)
	return cfg
}

func (s *Store) SaveSettings(cfg Settings) error {
	pairs := map[string]string{
		"snooze_minutes":       fmt.Sprintf("%d", cfg.SnoozeMinutes),
		"timeout_minutes":      fmt.Sprintf("%d", cfg.TimeoutMinutes),
		"default_sound":        cfg.DefaultSound,
		"blink_on_ms":          fmt.Sprintf("%d", cfg.BlinkOnMs),
		"blink_off_ms":         fmt.Sprintf("%d", cfg.BlinkOffMs),
		"color_clock":          cfg.ColorClock,
		"color_alarm":          cfg.ColorAlarm,
		"show_seconds":         fmt.Sprintf("%t", cfg.ShowSeconds),
		"server_url":           cfg.ServerURL,
		"server_password":      cfg.ServerPassword,
		"api_password":         cfg.APIPassword,
		"poll_seconds":         fmt.Sprintf("%d", cfg.PollSeconds),
		"crescendo_enabled":    fmt.Sprintf("%t", cfg.CrescendoEnabled),
		"crescendo_start_pct":  fmt.Sprintf("%d", cfg.CrescendoStartPct),
		"crescendo_end_pct":    fmt.Sprintf("%d", cfg.CrescendoEndPct),
		"crescendo_duration_s": fmt.Sprintf("%d", cfg.CrescendoDurationS),
		"callback_script":      cfg.CallbackScript,
	}
	for k, v := range pairs {
		if err := s.SetSetting(k, v); err != nil {
			return err
		}
	}
	return nil
}

func (s *Store) getSettingInt(key string, defaultVal int) int {
	val := s.GetSetting(key, "")
	if val == "" {
		return defaultVal
	}
	var n int
	if _, err := fmt.Sscanf(val, "%d", &n); err != nil {
		return defaultVal
	}
	return n
}

func (s *Store) GetSetting(key, defaultVal string) string {
	var val string
	err := s.db.QueryRow(`SELECT value FROM settings WHERE key = ?`, key).Scan(&val)
	if err != nil {
		return defaultVal
	}
	return val
}

func (s *Store) SetSetting(key, value string) error {
	_, err := s.db.Exec(`
		INSERT INTO settings (key, value) VALUES (?, ?)
		ON CONFLICT(key) DO UPDATE SET value = excluded.value
	`, key, value)
	return err
}
