package tools

import (
	"fmt"
	"strings"
	"time"

	"github.com/sixdouglas/suncalc"

	"skraak/utils"
)

// IsNightInput defines the input parameters for the isnight tool
type IsNightInput struct {
	FilePath string  `json:"file_path"`
	Lat      float64 `json:"lat"`
	Lng      float64 `json:"lng"`
	Timezone string  `json:"timezone,omitempty"`
}

// IsNightOutput defines the output structure for the isnight tool
type IsNightOutput struct {
	FilePath      string  `json:"file_path"`
	TimestampUTC  string  `json:"timestamp_utc"`
	SolarNight    bool    `json:"solar_night"`
	CivilNight    bool    `json:"civil_night"`
	DiurnalActive bool    `json:"diurnal_active"`
	MoonPhase     float64 `json:"moon_phase"`
	DurationSec   float64 `json:"duration_seconds"`
	TimestampSrc  string  `json:"timestamp_source"`
	MidpointUTC   string  `json:"midpoint_utc"`
	SunriseUTC    string  `json:"sunrise_utc,omitempty"`
	SunsetUTC     string  `json:"sunset_utc,omitempty"`
	DawnUTC       string  `json:"dawn_utc,omitempty"`
	DuskUTC       string  `json:"dusk_utc,omitempty"`
}

// IsNight determines if a WAV file was recorded at night based on its
// metadata timestamp and the given GPS coordinates.
//
// Timestamp resolution order:
//  1. AudioMoth comment (timezone embedded)
//  2. Filename timestamp + timezone offset (requires --timezone)
//  3. File modification time (system local time)
func IsNight(input IsNightInput) (IsNightOutput, error) {
	var output IsNightOutput

	// Step 1: Parse WAV header
	metadata, err := utils.ParseWAVHeader(input.FilePath)
	if err != nil {
		return output, fmt.Errorf("WAV header parsing failed: %w", err)
	}

	output.DurationSec = metadata.Duration

	// Step 2: Resolve timestamp (use file mod time as fallback)
	tsResult, err := utils.ResolveTimestamp(metadata, input.FilePath, input.Timezone, true, nil)
	if err != nil {
		return output, fmt.Errorf("cannot determine recording timestamp: %w", err)
	}

	// Determine timestamp source label
	tsSource := "file_mod_time"
	if tsResult.IsAudioMoth {
		tsSource = "audiomoth_comment"
	} else if utils.HasTimestampFilename(input.FilePath) {
		tsSource = "filename"
	}

	// Step 3: Calculate astronomical data using recording midpoint
	astroData := utils.CalculateAstronomicalData(
		tsResult.Timestamp.UTC(),
		metadata.Duration,
		input.Lat,
		input.Lng,
	)

	// Step 4: Get sun event times for informational output
	midpoint := utils.CalculateMidpointTime(tsResult.Timestamp.UTC(), metadata.Duration)
	sunTimes := suncalc.GetTimes(midpoint, input.Lat, input.Lng)

	output.FilePath = input.FilePath
	output.TimestampUTC = tsResult.Timestamp.UTC().Format(time.RFC3339)
	output.SolarNight = astroData.SolarNight
	output.CivilNight = astroData.CivilNight
	output.MoonPhase = astroData.MoonPhase
	output.TimestampSrc = tsSource
	output.MidpointUTC = midpoint.Format(time.RFC3339)

	populateSunTimes(&output, sunTimes, midpoint)

	return output, nil
}

// sunTimeUTC returns the UTC RFC3339 string for a suncalc event, or "" if absent/zero.
func sunTimeUTC(sunTimes map[suncalc.DayTimeName]suncalc.DayTime, name suncalc.DayTimeName) string {
	if entry, ok := sunTimes[name]; ok && !entry.Value.IsZero() {
		return entry.Value.UTC().Format(time.RFC3339)
	}
	return ""
}

// populateSunTimes fills in sun event times and diurnal status from suncalc results.
func populateSunTimes(output *IsNightOutput, sunTimes map[suncalc.DayTimeName]suncalc.DayTime, midpoint time.Time) {
	// Diurnal: midpoint is between dawn and sunset
	if dawn, ok := sunTimes[suncalc.Dawn]; ok && !dawn.Value.IsZero() {
		if sunset, ok := sunTimes[suncalc.Sunset]; ok && !sunset.Value.IsZero() {
			output.DiurnalActive = !midpoint.Before(dawn.Value) && !midpoint.After(sunset.Value)
		}
	}

	output.SunriseUTC = sunTimeUTC(sunTimes, suncalc.Sunrise)
	output.SunsetUTC = sunTimeUTC(sunTimes, suncalc.Sunset)
	output.DawnUTC = sunTimeUTC(sunTimes, suncalc.Dawn)
	output.DuskUTC = sunTimeUTC(sunTimes, suncalc.Dusk)
}

// String returns a human-readable summary of the isnight result
func (o IsNightOutput) String() string {
	var sb strings.Builder
	fmt.Fprintf(&sb, "File: %s\n", o.FilePath)
	fmt.Fprintf(&sb, "Timestamp (UTC): %s\n", o.TimestampUTC)
	fmt.Fprintf(&sb, "Midpoint (UTC):  %s\n", o.MidpointUTC)
	fmt.Fprintf(&sb, "Duration: %.1f seconds\n", o.DurationSec)
	fmt.Fprintf(&sb, "Source: %s\n", o.TimestampSrc)
	fmt.Fprintf(&sb, "Solar night: %v\n", o.SolarNight)
	fmt.Fprintf(&sb, "Civil night: %v\n", o.CivilNight)
	fmt.Fprintf(&sb, "Moon phase: %.2f\n", o.MoonPhase)
	if o.SunriseUTC != "" {
		fmt.Fprintf(&sb, "Sunrise (UTC): %s\n", o.SunriseUTC)
	}
	if o.SunsetUTC != "" {
		fmt.Fprintf(&sb, "Sunset (UTC):  %s\n", o.SunsetUTC)
	}
	if o.DawnUTC != "" {
		fmt.Fprintf(&sb, "Dawn (UTC):    %s\n", o.DawnUTC)
	}
	if o.DuskUTC != "" {
		fmt.Fprintf(&sb, "Dusk (UTC):    %s\n", o.DuskUTC)
	}
	return sb.String()
}