package handlers

import (
	"testing"

	fmreport "wucher/internal/domain/fm_report"
	helicopterusage "wucher/internal/domain/helicopter_usage"
)

func strptr(s string) *string { return &s }

func approx(a, b float64) bool { d := a - b; return d < 1e-9 && d > -1e-9 }

func TestBuildFMReportUsageBreakdown(t *testing.T) {
	usage := &helicopterusage.HelicopterUsage{
		TotalAirframeHours:  1523.5,
		TotalAirframeCycles: 320,
		TotalEngine1Hours:   1523.5,
		TotalEngine1GpcNgN1: 200.0,
		TotalEngine1PtcNfN2: 150.0,
		TotalEngine1Ccc:     44,
		TotalEngine2Hours:   1523.5,
		TotalEngine2GpcNgN1: 100.0,
	}
	today := helicopterusage.FlightDayStats{AirframeHours: 11.2, AirframeCycles: 2}
	report := &fmreport.Report{
		Engine1GpcN1: strptr("GPC/N1 63.2"), // must parse 63.2, not the "1" of N1
		Engine1PtcN2: strptr("PTC/N2 61.8"),
		Engine2GpcN1: strptr("GPC/N1 40.5"),
	}

	bd := buildFMReportUsageBreakdown(usage, today, report, true, true)

	// Grouped by row: Total / Today / Prev(=Total-Today).
	if bd.Total.Airframe.Hours != 1523.5 || bd.Total.Airframe.Cycles != 320 {
		t.Fatalf("total airframe = %+v", bd.Total.Airframe)
	}
	if bd.Today.Airframe.Hours != 11.2 || bd.Today.Airframe.Cycles != 2 {
		t.Fatalf("today airframe = %+v", bd.Today.Airframe)
	}
	if !approx(bd.Prev.Airframe.Hours, 1523.5-11.2) || bd.Prev.Airframe.Cycles != 318 {
		t.Fatalf("prev airframe = %+v", bd.Prev.Airframe)
	}
	// Engine1 hours mirror airframe today.
	if bd.Today.Engine1.Hours != 11.2 {
		t.Fatalf("today engine1 hours = %v, want 11.2", bd.Today.Engine1.Hours)
	}
	// Engine1 GPC parsed 63.2 (NOT the "1" of N1); prev = 200 - 63.2.
	if bd.Today.Engine1.GpcNgN1 != 63.2 {
		t.Fatalf("today engine1 gpc = %v, want 63.2 (parser grabbed label digit?)", bd.Today.Engine1.GpcNgN1)
	}
	if !approx(bd.Prev.Engine1.GpcNgN1, 200.0-63.2) {
		t.Fatalf("prev engine1 gpc = %v, want %v", bd.Prev.Engine1.GpcNgN1, 200.0-63.2)
	}
	if bd.Today.Engine1.PtcNfN2 != 61.8 {
		t.Fatalf("today engine1 ptc = %v, want 61.8", bd.Today.Engine1.PtcNfN2)
	}
	// CCC: no flight contribution -> today 0, total 44, prev 44.
	if bd.Today.Engine1.Ccc != 0 || bd.Total.Engine1.Ccc != 44 || bd.Prev.Engine1.Ccc != 44 {
		t.Fatalf("engine1 ccc rows: today=%v total=%v prev=%v", bd.Today.Engine1.Ccc, bd.Total.Engine1.Ccc, bd.Prev.Engine1.Ccc)
	}
	if bd.Today.Engine2.GpcNgN1 != 40.5 {
		t.Fatalf("today engine2 gpc = %v, want 40.5", bd.Today.Engine2.GpcNgN1)
	}
	// Counters: total from usage, today = this flight (+1 flight report), prev = total - today.
	if bd.Total.Counters.Landings != 0 { // usage above has no landings set
		t.Fatalf("total landings = %v, want 0", bd.Total.Counters.Landings)
	}
	if bd.Today.Counters.FlightReports != 1 {
		t.Fatalf("today flight_reports = %v, want 1", bd.Today.Counters.FlightReports)
	}
}

func TestBuildFMReportUsageBreakdownCounters(t *testing.T) {
	usage := &helicopterusage.HelicopterUsage{
		TotalLanding:         10,
		TotalFlightReport:    5,
		TotalHookRelease:     3,
		TotalRotorBrakeCycle: 2,
	}
	today := helicopterusage.FlightDayStats{Landing: 1, HookRelease: 1, RotorBrakeCycle: 1}
	bd := buildFMReportUsageBreakdown(usage, today, &fmreport.Report{}, true, true)

	if bd.Total.Counters.Landings != 10 || bd.Today.Counters.Landings != 1 || bd.Prev.Counters.Landings != 9 {
		t.Fatalf("landings rows: total=%v today=%v prev=%v", bd.Total.Counters.Landings, bd.Today.Counters.Landings, bd.Prev.Counters.Landings)
	}
	// flight_reports: total 5, today 1 (this report), prev 4.
	if bd.Total.Counters.FlightReports != 5 || bd.Today.Counters.FlightReports != 1 || bd.Prev.Counters.FlightReports != 4 {
		t.Fatalf("flight_reports rows: total=%v today=%v prev=%v", bd.Total.Counters.FlightReports, bd.Today.Counters.FlightReports, bd.Prev.Counters.FlightReports)
	}
	if bd.Prev.Counters.HookReleases != 2 || bd.Prev.Counters.RotorBrakeCycles != 1 {
		t.Fatalf("hook/rotor prev: %v / %v", bd.Prev.Counters.HookReleases, bd.Prev.Counters.RotorBrakeCycles)
	}
}

func TestBuildFMReportUsageBreakdownSingleEngine(t *testing.T) {
	usage := &helicopterusage.HelicopterUsage{
		TotalAirframeHours: 1523.5,
		TotalEngine1Hours:  1523.5,
		TotalEngine2Hours:  1523.5, // stored, but must be hidden for single-engine
	}
	today := helicopterusage.FlightDayStats{AirframeHours: 11.2}
	report := &fmreport.Report{Engine2GpcN1: strptr("GPC/N1 40.5")}

	bd := buildFMReportUsageBreakdown(usage, today, report, true, false)
	if bd.Today.Engine1.Hours != 11.2 {
		t.Fatalf("engine1 should still be populated, got %+v", bd.Today.Engine1)
	}
	// Engine 2 empty across all three rows.
	if bd.Total.Engine2.Hours != 0 || bd.Today.Engine2.Hours != 0 || bd.Prev.Engine2.Hours != 0 ||
		bd.Today.Engine2.GpcNgN1 != 0 {
		t.Fatalf("engine2 must be empty for single-engine: total=%+v today=%+v prev=%+v",
			bd.Total.Engine2, bd.Today.Engine2, bd.Prev.Engine2)
	}
}

func TestBuildFMReportUsageBreakdownNilUsage(t *testing.T) {
	today := helicopterusage.FlightDayStats{AirframeHours: 11.2, AirframeCycles: 2}
	bd := buildFMReportUsageBreakdown(nil, today, &fmreport.Report{}, true, true)
	// No cumulative yet: total 0, today = flight, prev = -today.
	if bd.Total.Airframe.Hours != 0 || bd.Today.Airframe.Hours != 11.2 || bd.Prev.Airframe.Hours != -11.2 {
		t.Fatalf("nil-usage airframe hours rows: total=%v today=%v prev=%v",
			bd.Total.Airframe.Hours, bd.Today.Airframe.Hours, bd.Prev.Airframe.Hours)
	}
	if bd.Prev.Airframe.Cycles != -2 {
		t.Fatalf("nil-usage prev cycles = %v, want -2", bd.Prev.Airframe.Cycles)
	}
}
