package apperrorsx

import (
	"context"
	"errors"

	"gorm.io/gorm"

	afterflightinspection "wucher/internal/domain/after_flight_inspection"
	"wucher/internal/service"
	"wucher/internal/shared/pkg/apperrors"
)

// Translate is the single entrypoint for mapping domain/service errors to AppError.
func Translate(module string, err error) *AppError {
	if err == nil {
		return nil
	}

	switch module {
	case ModuleAuth, ModulePIN:
		if mapped := mapAuthPIN(err); mapped != nil {
			return mapped
		}
	case ModuleHelicopter:
		if mapped := mapHelicopter(err); mapped != nil {
			return mapped
		}
	case ModuleHospital:
		if mapped := mapHospital(err); mapped != nil {
			return mapped
		}
	case ModuleTakeover:
		if mapped := mapTakeover(err); mapped != nil {
			return mapped
		}
	case ModuleFleetStatus:
		if mapped := mapFleetStatus(err); mapped != nil {
			return mapped
		}
	case ModuleComplaint:
		if mapped := mapComplaint(err); mapped != nil {
			return mapped
		}
	case ModuleAfterFlightInspection:
		if mapped := mapAfterFlightInspection(err); mapped != nil {
			return mapped
		}
	}

	return mapGeneral(err)
}

func mapAfterFlightInspection(err error) *AppError {
	switch {
	case errors.Is(err, afterflightinspection.ErrInvalidFlightInspectionID):
		return New(ErrAfterFlightInvalidID).WithCause(err)
	case errors.Is(err, afterflightinspection.ErrFlightInspectionNotFound):
		return New(ErrAfterFlightFlightInspectionNotFound).WithCause(err)
	case errors.Is(err, afterflightinspection.ErrCapabilitiesUnavailable):
		return New(ErrAfterFlightCapabilitiesUnavailable).WithCause(err)
	case errors.Is(err, afterflightinspection.ErrNR1Required):
		return New(ErrAfterFlightNR1Required).WithCause(err)
	case errors.Is(err, afterflightinspection.ErrNR1NotAvailable):
		return New(ErrAfterFlightNR1NotAvailable).WithCause(err)
	case errors.Is(err, afterflightinspection.ErrNR2Required):
		return New(ErrAfterFlightNR2Required).WithCause(err)
	case errors.Is(err, afterflightinspection.ErrNR2NotAvailable):
		return New(ErrAfterFlightNR2NotAvailable).WithCause(err)
	case errors.Is(err, afterflightinspection.ErrLHRequired):
		return New(ErrAfterFlightLHRequired).WithCause(err)
	case errors.Is(err, afterflightinspection.ErrLHNotAvailable):
		return New(ErrAfterFlightLHNotAvailable).WithCause(err)
	case errors.Is(err, afterflightinspection.ErrRHRequired):
		return New(ErrAfterFlightRHRequired).WithCause(err)
	case errors.Is(err, afterflightinspection.ErrRHNotAvailable):
		return New(ErrAfterFlightRHNotAvailable).WithCause(err)
	case errors.Is(err, afterflightinspection.ErrMGBRequired):
		return New(ErrAfterFlightMGBRequired).WithCause(err)
	case errors.Is(err, afterflightinspection.ErrMGBNotAvailable):
		return New(ErrAfterFlightMGBNotAvailable).WithCause(err)
	case errors.Is(err, afterflightinspection.ErrIGBRequired):
		return New(ErrAfterFlightIGBRequired).WithCause(err)
	case errors.Is(err, afterflightinspection.ErrIGBNotAvailable):
		return New(ErrAfterFlightIGBNotAvailable).WithCause(err)
	case errors.Is(err, afterflightinspection.ErrTGBRequired):
		return New(ErrAfterFlightTGBRequired).WithCause(err)
	case errors.Is(err, afterflightinspection.ErrTGBNotAvailable):
		return New(ErrAfterFlightTGBNotAvailable).WithCause(err)
	}

	if errors.Is(err, gorm.ErrRecordNotFound) {
		return New(ErrAfterFlightInspectionNotFound).WithCause(err)
	}

	if mapped := mapMySQLError(ModuleAfterFlightInspection, err); mapped != nil {
		return mapped
	}

	return nil
}

func mapFleetStatus(err error) *AppError {
	if errors.Is(err, gorm.ErrRecordNotFound) {
		return New(ErrFleetStatusNotFound).WithCause(err)
	}

	if _, ok := apperrors.AsDeleteConflict(err); ok {
		return New(ErrFleetStatusRelationDelete).WithCause(err)
	}

	if mapped := mapMySQLError(ModuleFleetStatus, err); mapped != nil {
		return mapped
	}

	return nil
}

func mapComplaint(err error) *AppError {
	if errors.Is(err, gorm.ErrRecordNotFound) {
		return New(ErrComplaintNotFound).WithCause(err)
	}

	if mapped := mapMySQLError(ModuleComplaint, err); mapped != nil {
		return mapped
	}

	return nil
}

func mapGeneral(err error) *AppError {
	switch {
	case errors.Is(err, gorm.ErrRecordNotFound):
		return New(ErrDataNotFound).WithCause(err)
	case errors.Is(err, context.Canceled), errors.Is(err, context.DeadlineExceeded):
		return New(ErrInvalidRequest).WithCause(err)
	}

	if mapped := mapMySQLError(ModuleGeneral, err); mapped != nil {
		return mapped
	}

	switch {
	case errors.Is(err, service.ErrInvalidReauthMethod):
		return New(ErrValidation).WithCause(err)
	default:
		return New(ErrInternal).WithCause(err)
	}
}

func mapAuthPIN(err error) *AppError {
	switch {
	case errors.Is(err, service.ErrInvalidToken):
		return New(ErrTokenInvalid).WithCause(err)
	case errors.Is(err, service.ErrInvalidCredentials):
		return New(ErrUserUnauthorized).WithCause(err)
	case errors.Is(err, service.ErrSecurityPINNotSet):
		return New(ErrPINNotSet).WithCause(err)
	case errors.Is(err, service.ErrSecurityPINLocked), errors.Is(err, service.ErrSecurityPINResetRequired):
		return New(ErrPINBlocked).WithCause(err)
	case errors.Is(err, service.ErrSecurityPINAttemptLimit):
		return New(ErrPINBlocked).WithCause(err)
	case errors.Is(err, service.ErrInvalidSecurityPIN):
		return New(ErrPINIncorrect).WithCause(err)
	case errors.Is(err, service.ErrInvalidPINAction):
		return New(ErrPINVerificationRequired202).WithCause(err)
	case errors.Is(err, service.ErrInvalidReauthMethod):
		return New(ErrValidation).WithCause(err)
	default:
		return nil
	}
}

func mapHelicopter(err error) *AppError {
	if errors.Is(err, gorm.ErrRecordNotFound) {
		return New(ErrHelicopterNotFound).WithCause(err)
	}

	if _, ok := apperrors.AsDeleteConflict(err); ok {
		return New(ErrHelicopterRelationDelete).WithCause(err)
	}

	if mapped := mapMySQLError(ModuleHelicopter, err); mapped != nil {
		return mapped
	}

	switch {
	case errors.Is(err, service.ErrInvalidReauthMethod):
		return New(ErrHelicopterInvalidPayload).WithCause(err)
	default:
		return nil
	}
}

func mapHospital(err error) *AppError {
	if errors.Is(err, gorm.ErrRecordNotFound) {
		return New(ErrHospitalNotFound).WithCause(err)
	}

	if _, ok := apperrors.AsDeleteConflict(err); ok {
		return New(ErrHospitalRelationDelete).WithCause(err)
	}

	if mapped := mapMySQLError(ModuleHospital, err); mapped != nil {
		return mapped
	}

	return nil
}

func mapTakeover(err error) *AppError {
	if errors.Is(err, gorm.ErrRecordNotFound) {
		return New(ErrTakeoverNotFound).WithCause(err)
	}

	if mapped := mapMySQLError(ModuleTakeover, err); mapped != nil {
		return mapped
	}

	return nil
}
