package service

import (
	"context"
	"fmt"
	"strings"

	afterflightinspection "wucher/internal/domain/after_flight_inspection"
	"wucher/internal/shared/pkg/appctx"
)

type AfterFlightInspectionService struct {
	repo  afterflightinspection.Repository
}

func NewAfterFlightInspectionService(repo afterflightinspection.Repository) *AfterFlightInspectionService {
	return &AfterFlightInspectionService{repo: repo}
}

func (s *AfterFlightInspectionService) Upsert(ctx context.Context, flightInspectionID []byte, req *afterflightinspection.UpsertRequest) (*afterflightinspection.AfterFlightInspection, error) {
	if err := s.ensureFlightInspection(ctx, flightInspectionID); err != nil {
		return nil, err
	}

	caps, err := s.repo.GetHelicopterCapabilities(ctx, flightInspectionID)
	if err != nil {
		return nil, fmt.Errorf("failed to get helicopter capabilities: %w", err)
	}

	if err := s.validateRequest(req, caps); err != nil {
		return nil, err
	}

	row := &afterflightinspection.AfterFlightInspection{
		FlightInspectionID:        flightInspectionID,
		OilEngineNR1Checked:       req.OilEngineNR1Checked,
		OilEngineNR2Checked:       req.OilEngineNR2Checked,
		HydraulicLHChecked:        req.HydraulicLHChecked,
		HydraulicRHChecked:        req.HydraulicRHChecked,
		OilTransmissionMGBChecked: req.OilTransmissionMGBChecked,
		OilTransmissionIGBChecked: req.OilTransmissionIGBChecked,
		OilTransmissionTGBChecked: req.OilTransmissionTGBChecked,
		Note:                      req.Note,
	}

	existing, err := s.repo.GetByFlightInspectionID(ctx, flightInspectionID)
	if err != nil {
		return nil, fmt.Errorf("failed to load after flight inspection: %w", err)
	}

	userID := appctx.GetUserID(ctx)
	if existing != nil {
		row.ID = existing.ID
		row.CreatedAt = existing.CreatedAt
		row.CreatedBy = existing.CreatedBy
		if userID != nil {
			row.UpdatedBy = userID
		} else {
			row.UpdatedBy = existing.UpdatedBy
		}
	} else if userID != nil {
		row.CreatedBy = userID
		row.UpdatedBy = userID
	}

	if err := s.repo.Upsert(ctx, row); err != nil {
		return nil, fmt.Errorf("failed to upsert after flight inspection: %w", err)
	}

	return row, nil
}

func (s *AfterFlightInspectionService) GetByFlightInspectionID(ctx context.Context, flightInspectionID []byte) (*afterflightinspection.AfterFlightInspection, error) {
	if err := s.ensureFlightInspection(ctx, flightInspectionID); err != nil {
		return nil, err
	}

	row, err := s.repo.GetByFlightInspectionID(ctx, flightInspectionID)
	if err != nil {
		return nil, fmt.Errorf("failed to get after flight inspection: %w", err)
	}

	return row, nil
}

func (s *AfterFlightInspectionService) DeleteByFlightInspectionID(ctx context.Context, flightInspectionID []byte) error {
	if err := s.ensureFlightInspection(ctx, flightInspectionID); err != nil {
		return err
	}

	if err := s.repo.DeleteByFlightInspectionID(ctx, flightInspectionID); err != nil {
		return fmt.Errorf("failed to delete after flight inspection: %w", err)
	}
	return nil
}

// ensureFlightInspection validates the identifier and confirms the referenced
// flight inspection exists. It returns domain sentinel errors so the transport
// layer can map them to apperrorsx definitions.
func (s *AfterFlightInspectionService) ensureFlightInspection(ctx context.Context, flightInspectionID []byte) error {
	if len(flightInspectionID) != 16 {
		return afterflightinspection.ErrInvalidFlightInspectionID
	}

	exists, err := s.repo.FlightInspectionExists(ctx, flightInspectionID)
	if err != nil {
		return fmt.Errorf("failed to check flight inspection: %w", err)
	}
	if !exists {
		return afterflightinspection.ErrFlightInspectionNotFound
	}
	return nil
}

func (s *AfterFlightInspectionService) validateRequest(req *afterflightinspection.UpsertRequest, caps *afterflightinspection.HelicopterCapabilities) error {
	if caps == nil {
		return afterflightinspection.ErrCapabilitiesUnavailable
	}

	if caps.NR1 && !hasNonEmptyString(req.OilEngineNR1Checked) {
		return afterflightinspection.ErrNR1Required
	}
	if hasNonEmptyString(req.OilEngineNR1Checked) && !caps.NR1 {
		return afterflightinspection.ErrNR1NotAvailable
	}
	if caps.NR2 && !hasNonEmptyString(req.OilEngineNR2Checked) {
		return afterflightinspection.ErrNR2Required
	}
	if hasNonEmptyString(req.OilEngineNR2Checked) && !caps.NR2 {
		return afterflightinspection.ErrNR2NotAvailable
	}
	if caps.LH && !hasNonEmptyString(req.HydraulicLHChecked) {
		return afterflightinspection.ErrLHRequired
	}
	if hasNonEmptyString(req.HydraulicLHChecked) && !caps.LH {
		return afterflightinspection.ErrLHNotAvailable
	}
	if caps.RH && !hasNonEmptyString(req.HydraulicRHChecked) {
		return afterflightinspection.ErrRHRequired
	}
	if hasNonEmptyString(req.HydraulicRHChecked) && !caps.RH {
		return afterflightinspection.ErrRHNotAvailable
	}
	if caps.MGB && !hasNonEmptyString(req.OilTransmissionMGBChecked) {
		return afterflightinspection.ErrMGBRequired
	}
	if hasNonEmptyString(req.OilTransmissionMGBChecked) && !caps.MGB {
		return afterflightinspection.ErrMGBNotAvailable
	}
	if caps.IGB && !hasNonEmptyString(req.OilTransmissionIGBChecked) {
		return afterflightinspection.ErrIGBRequired
	}
	if hasNonEmptyString(req.OilTransmissionIGBChecked) && !caps.IGB {
		return afterflightinspection.ErrIGBNotAvailable
	}
	if caps.TGB && !hasNonEmptyString(req.OilTransmissionTGBChecked) {
		return afterflightinspection.ErrTGBRequired
	}
	if hasNonEmptyString(req.OilTransmissionTGBChecked) && !caps.TGB {
		return afterflightinspection.ErrTGBNotAvailable
	}

	return nil
}

func hasNonEmptyString(v *string) bool {
	return v != nil && strings.TrimSpace(*v) != ""
}
