package handlers

import (
	"encoding/json"
	"fmt"
	"io"
	"net/http"
	"strings"
	"testing"
	"time"

	"github.com/gofiber/fiber/v2"
	"gorm.io/driver/sqlite"
	"gorm.io/gorm"

	filemanager "wucher/internal/domain/file_manager"
	fleetstatus "wucher/internal/domain/fleet_status"
	"wucher/internal/domain/helicopter"
	helicopterusage "wucher/internal/domain/helicopter_usage"
	"wucher/internal/repository/mysql"
	"wucher/internal/service"
	"wucher/internal/shared/pkg/uuidv7"
)

// TestFleetStatusUpdateHTTPReflectsSummary drives the REAL PATCH handler over sqlite and
// asserts the response reflects the patched summary — the exact flow the user reports.
func TestFleetStatusUpdateHTTPReflectsSummary(t *testing.T) {
	dsn := fmt.Sprintf("file:fs_http_%d?mode=memory&cache=shared", time.Now().UnixNano())
	db, err := gorm.Open(sqlite.Open(dsn), &gorm.Config{NowFunc: func() time.Time { return time.Now().UTC() }})
	if err != nil {
		t.Fatalf("open: %v", err)
	}
	if err := db.AutoMigrate(
		&filemanager.Folder{}, &filemanager.File{}, &filemanager.Attachment{},
		&helicopter.Helicopter{},
		&fleetstatus.FleetStatus{}, &fleetstatus.MaintenanceSchedule{}, &fleetstatus.FleetStatusFile{},
		&helicopterusage.HelicopterUsage{},
	); err != nil {
		t.Fatalf("migrate: %v", err)
	}

	heliRepo := mysql.NewHelicopterRepository(db)
	heli := &helicopter.Helicopter{Designation: "H1", Identifier: "PK-H1", Type: "Twin", NR1: true, NR2: true, IsActive: true}
	if err := heliRepo.Create(nil, heli); err != nil {
		t.Fatalf("create heli: %v", err)
	}
	fsRepo := mysql.NewFleetStatusRepository(db)
	fs := &fleetstatus.FleetStatus{HelicopterID: heli.ID, Status: fleetstatus.StatusActive}
	if err := fsRepo.Create(nil, fs); err != nil {
		t.Fatalf("create fs: %v", err)
	}

	usageSvc := service.NewHelicopterUsageService(mysql.NewHelicopterUsageRepository(db))
	fleetSvc := service.NewFleetStatusService(fsRepo, nil, service.NewHelicopterService(heliRepo), nil).WithUsage(usageSvc)

	h := NewFleetStatusHandler(fleetSvc, fleetStatusTestDeps())
	app := fiber.New()
	app.Patch("/fleet-status/update/:id", h.Update)

	idStr, _ := uuidv7.BytesToString(fs.ID)
	body := fmt.Sprintf(`{"data":{"type":"fleet_status_update","id":"%s","attributes":{"summary":{"airframe":{"hours":1234,"cycles":-1},"engine_1":{"hours":1234,"gpc_ng_n1":23.23,"ptc_nf_n2":23.59,"ccc":-8},"engine_2":{"hours":234,"gpc_ng_n1":123,"ptc_nf_n2":1230,"ccc":123},"flight_reports":1,"hook_releases":1,"landings":1,"rotor_brake_cycles":1},"maintenance_schedules":[{"inspection_type":"A/F","type":"","due":"","ext":""}]}}}`, idStr)

	req, _ := http.NewRequest(http.MethodPatch, "/fleet-status/update/"+idStr, strings.NewReader(body))
	req.Header.Set("Content-Type", "application/json")
	resp, err := app.Test(req, -1)
	if err != nil {
		t.Fatalf("request: %v", err)
	}
	raw, _ := io.ReadAll(resp.Body)
	if resp.StatusCode != http.StatusOK {
		t.Fatalf("PATCH status = %d, body = %s", resp.StatusCode, string(raw))
	}

	var out struct {
		Data struct {
			Attributes struct {
				Summary struct {
					Airframe struct {
						Hours  *float64 `json:"hours"`
						Cycles *float64 `json:"cycles"`
					} `json:"airframe"`
					Engine1 struct {
						Hours *float64 `json:"hours"`
					} `json:"engine_1"`
				} `json:"summary"`
			} `json:"attributes"`
		} `json:"data"`
	}
	if err := json.Unmarshal(raw, &out); err != nil {
		t.Fatalf("unmarshal: %v (%s)", err, string(raw))
	}
	af := out.Data.Attributes.Summary.Airframe
	if af.Hours == nil || *af.Hours != 1234 {
		t.Fatalf("PATCH response airframe hours = %v, want 1234\nbody=%s", af.Hours, string(raw))
	}
	if af.Cycles == nil || *af.Cycles != -1 {
		t.Fatalf("PATCH response airframe cycles = %v, want -1", af.Cycles)
	}
	t.Logf("PATCH response reflects summary: hours=%v cycles=%v", *af.Hours, *af.Cycles)

	// Second PATCH with a new value.
	body2 := strings.Replace(body, `"hours":1234,"cycles":-1`, `"hours":5555,"cycles":7`, 1)
	req2, _ := http.NewRequest(http.MethodPatch, "/fleet-status/update/"+idStr, strings.NewReader(body2))
	req2.Header.Set("Content-Type", "application/json")
	resp2, _ := app.Test(req2, -1)
	raw2, _ := io.ReadAll(resp2.Body)
	var out2 = out
	_ = json.Unmarshal(raw2, &out2)
	if out2.Data.Attributes.Summary.Airframe.Hours == nil || *out2.Data.Attributes.Summary.Airframe.Hours != 5555 {
		t.Fatalf("second PATCH hours = %v, want 5555 (VALUE DID NOT CHANGE)\nbody=%s", out2.Data.Attributes.Summary.Airframe.Hours, string(raw2))
	}
	t.Logf("second PATCH reflects new value: hours=%v", *out2.Data.Attributes.Summary.Airframe.Hours)
}
