package config

import (
	"errors"
	"fmt"
	"time"

	"github.com/kami/maven/internal/morning"
	"github.com/robfig/cron/v3"
)

// The two scheduled things Maven says on her own, plus the window she does not
// say them in. Both routine kinds default to severity 1 — the care class, which
// quiet hours suppress and which never reaches an away channel. A routine that
// speaks at 3am has to be written that way on purpose.

// RoutineConfig — one scheduled routine. Cron is a standard 5-field expression
// ("0 8 * * *" = 08:00 daily). Body is the RU text delivered verbatim (routines
// are not LLM-phrased). Severity (1-4, default 1) drives routing: care-class
// (≤2) is suppressed by quiet hours and drops when away; ops-class reaches away
// channels.
type RoutineConfig struct {
	Name     string `json:"name"`
	Cron     string `json:"cron"`
	Body     string `json:"body"`
	Severity int    `json:"severity,omitempty"`
}

// MorningRoutineConfig — one daily checklist. WindowStart/WindowEnd/NudgeAt
// are "HH:MM" local time; NudgeAt empty defaults to WindowEnd. Weekdays are
// 0=Sunday..6=Saturday; empty means every day (set two routines under
// different names for weekday/weekend variants).
type MorningRoutineConfig struct {
	Name        string                     `json:"name"`
	Weekdays    []int                      `json:"weekdays,omitempty"`
	WindowStart string                     `json:"window_start"`
	WindowEnd   string                     `json:"window_end"`
	NudgeAt     string                     `json:"nudge_at,omitempty"`
	Severity    int                        `json:"severity,omitempty"`
	Items       []MorningRoutineItemConfig `json:"items"`
}

// MorningRoutineItemConfig — one checklist entry. FactKey is the fact whose
// presence within the window counts as completion evidence.
type MorningRoutineItemConfig struct {
	Key     string `json:"key"`
	FactKey string `json:"fact_key"`
	Label   string `json:"label"`
	// Optional — this one being skipped does not earn a nudge. Default false,
	// so a routine written before 04-08-2026 keeps behaving as it did.
	Optional bool `json:"optional,omitempty"`
}

// QuietHoursConfig — a recurring daily quiet-window. Times are local to the
// server's wall clock. A window crossing midnight (Start > End) is handled:
// "23:00"-"08:00" means quiet from 23:00 to 08:00 the next day.
type QuietHoursConfig struct {
	Start string `json:"start,omitempty"` // "HH:MM" local time, e.g. "23:00"
	End   string `json:"end,omitempty"`   // "HH:MM" local time, e.g. "08:00"
}

// normaliseRoutines defaults both routine kinds to severity 1, the care class:
// the safe floor, so a misconfigured routine cannot blast an away channel at
// 3am.
func (c *Config) normaliseRoutines() {
	for i := range c.Routines {
		if c.Routines[i].Severity == 0 {
			c.Routines[i].Severity = 1
		}
	}
	for i := range c.MorningRoutines {
		if c.MorningRoutines[i].Severity == 0 {
			c.MorningRoutines[i].Severity = 1
		}
	}
}

// validateRoutines fails a routine that could never fire. A typo in a cron
// expression or a missing body should stop the daemon at startup, not go
// unnoticed as silence at 08:00 every day.
func (c *Config) validateRoutines() error {
	for _, r := range c.Routines {
		if r.Name == "" {
			return errors.New("routine: name is required")
		}
		if r.Body == "" {
			return fmt.Errorf("routine %q: body is required", r.Name)
		}
		if _, err := cron.ParseStandard(r.Cron); err != nil {
			return fmt.Errorf("routine %q: bad cron %q: %w", r.Name, r.Cron, err)
		}
	}
	if len(c.MorningRoutines) > 0 {
		if err := morning.Validate(morningRoutinesFromConfig(c.MorningRoutines)); err != nil {
			return err
		}
	}
	return nil
}

// morningRoutinesFromConfig maps the config's morning-routine blocks to the
// engine type. Shared with the daemon so config validation and daemon wiring
// can never drift on the mapping.
func morningRoutinesFromConfig(mc []MorningRoutineConfig) []morning.Routine {
	out := make([]morning.Routine, len(mc))
	for i, r := range mc {
		items := make([]morning.Item, len(r.Items))
		for j, it := range r.Items {
			items[j] = morning.Item{Key: it.Key, FactKey: it.FactKey, Label: it.Label, Optional: it.Optional}
		}
		weekdays := make([]time.Weekday, len(r.Weekdays))
		for j, w := range r.Weekdays {
			weekdays[j] = time.Weekday(w)
		}
		out[i] = morning.Routine{
			Name:        r.Name,
			Weekdays:    weekdays,
			WindowStart: r.WindowStart,
			WindowEnd:   r.WindowEnd,
			NudgeAt:     r.NudgeAt,
			Severity:    r.Severity,
			Items:       items,
		}
	}
	return out
}

// MorningRoutinesFromConfig is the exported form daemon wiring uses.
func MorningRoutinesFromConfig(mc []MorningRoutineConfig) []morning.Routine {
	return morningRoutinesFromConfig(mc)
}
