package service

import (
	"context"
	"testing"
	"time"

	afterflightinspection "wucher/internal/domain/after_flight_inspection"
)

type afterFlightInspectionRepoStub struct {
	exists        bool
	caps          *afterflightinspection.HelicopterCapabilities
	existing      *afterflightinspection.AfterFlightInspection
	upserted      *afterflightinspection.AfterFlightInspection
	upsertErr     error
	existsErr     error
	capErr        error
	flightChecked int
}

func (r *afterFlightInspectionRepoStub) Upsert(_ context.Context, row *afterflightinspection.AfterFlightInspection) error {
	r.upserted = row
	return r.upsertErr
}

func (r *afterFlightInspectionRepoStub) GetByFlightInspectionID(context.Context, []byte) (*afterflightinspection.AfterFlightInspection, error) {
	return r.existing, nil
}

func (r *afterFlightInspectionRepoStub) DeleteByFlightInspectionID(context.Context, []byte) error {
	return nil
}

func (r *afterFlightInspectionRepoStub) FlightInspectionExists(context.Context, []byte) (bool, error) {
	if r.existsErr != nil {
		return false, r.existsErr
	}
	return r.exists, nil
}

func (r *afterFlightInspectionRepoStub) GetHelicopterCapabilities(context.Context, []byte) (*afterflightinspection.HelicopterCapabilities, error) {
	if r.capErr != nil {
		return nil, r.capErr
	}
	r.flightChecked++
	return r.caps, nil
}

func TestAfterFlightInspectionServiceValidateRequestRequiresCapabilityChecks(t *testing.T) {
	svc := NewAfterFlightInspectionService(&afterFlightInspectionRepoStub{})

	nr1 := "checked"
	nr2 := "checked"
	lh := "checked"
	rh := "checked"
	mgb := "checked"
	igb := "checked"
	tgb := "checked"
	req := &afterflightinspection.UpsertRequest{
		OilEngineNR1Checked:       &nr1,
		OilEngineNR2Checked:       &nr2,
		HydraulicLHChecked:        &lh,
		HydraulicRHChecked:        &rh,
		OilTransmissionMGBChecked: &mgb,
		OilTransmissionIGBChecked: &igb,
		OilTransmissionTGBChecked: &tgb,
	}
	caps := &afterflightinspection.HelicopterCapabilities{
		NR1: true, NR2: true, LH: true, RH: true, MGB: true, IGB: true, TGB: true,
	}
	if err := svc.validateRequest(req, caps); err != nil {
		t.Fatalf("expected request to be valid, got %v", err)
	}

	req.OilEngineNR2Checked = nil
	if err := svc.validateRequest(req, caps); err == nil {
		t.Fatal("expected missing required nr2 check to be rejected")
	}
}

func TestAfterFlightInspectionServiceUpsertUpdatesExistingRow(t *testing.T) {
	existingID := make([]byte, 16)
	for i := range existingID {
		existingID[i] = byte(i + 1)
	}
	createdBy := []byte("creator123456789")
	createdAt := time.Date(2026, 1, 1, 8, 0, 0, 0, time.UTC)

	repo := &afterFlightInspectionRepoStub{
		exists: true,
		caps:   &afterflightinspection.HelicopterCapabilities{NR1: true},
		existing: &afterflightinspection.AfterFlightInspection{
			ID:        existingID,
			CreatedAt: createdAt,
			CreatedBy: createdBy,
		},
	}
	svc := NewAfterFlightInspectionService(repo)

	flightInspectionID := make([]byte, 16)
	for i := range flightInspectionID {
		flightInspectionID[i] = byte(100 + i)
	}
	nr1 := "400"
	row, err := svc.Upsert(context.Background(), flightInspectionID, &afterflightinspection.UpsertRequest{
		OilEngineNR1Checked: &nr1,
	})
	if err != nil {
		t.Fatalf("Upsert returned error: %v", err)
	}

	// The saved row must reuse the existing identity so GORM performs an UPDATE
	// instead of inserting a duplicate flight_inspection_id (the original bug).
	if string(repo.upserted.ID) != string(existingID) {
		t.Fatalf("expected upsert to reuse existing ID %x, got %x", existingID, repo.upserted.ID)
	}
	if !repo.upserted.CreatedAt.Equal(createdAt) {
		t.Fatalf("expected created_at to be preserved, got %v", repo.upserted.CreatedAt)
	}
	if string(repo.upserted.CreatedBy) != string(createdBy) {
		t.Fatalf("expected created_by to be preserved, got %x", repo.upserted.CreatedBy)
	}
	if row == nil || string(row.ID) != string(existingID) {
		t.Fatal("expected returned row to carry existing ID")
	}
}

func TestAfterFlightInspectionServiceUpsertInsertsWhenAbsent(t *testing.T) {
	repo := &afterFlightInspectionRepoStub{
		exists: true,
		caps:   &afterflightinspection.HelicopterCapabilities{NR1: true},
	}
	svc := NewAfterFlightInspectionService(repo)

	flightInspectionID := make([]byte, 16)
	for i := range flightInspectionID {
		flightInspectionID[i] = byte(50 + i)
	}
	nr1 := "400"
	if _, err := svc.Upsert(context.Background(), flightInspectionID, &afterflightinspection.UpsertRequest{
		OilEngineNR1Checked: &nr1,
	}); err != nil {
		t.Fatalf("Upsert returned error: %v", err)
	}

	// No existing row: a new INSERT (empty ID, BeforeCreate mints a UUID).
	if len(repo.upserted.ID) != 0 {
		t.Fatalf("expected empty ID for new row, got %x", repo.upserted.ID)
	}
	if string(repo.upserted.FlightInspectionID) != string(flightInspectionID) {
		t.Fatal("expected flight_inspection_id to be set")
	}
}

func TestAfterFlightInspectionServiceValidateRequestRejectsUnsupportedChecks(t *testing.T) {
	svc := NewAfterFlightInspectionService(&afterFlightInspectionRepoStub{})

	yes := "checked"
	req := &afterflightinspection.UpsertRequest{
		OilEngineNR1Checked: &yes,
	}
	caps := &afterflightinspection.HelicopterCapabilities{}

	if err := svc.validateRequest(req, caps); err == nil {
		t.Fatal("expected unsupported nr1 check to be rejected")
	}
}
