package apperrorsx

import (
	"errors"

	mysqldriver "github.com/go-sql-driver/mysql"
)

const (
	mysqlErrAccessDenied            uint16 = 1045
	mysqlErrDuplicateEntry          uint16 = 1062
	mysqlErrCommandDenied           uint16 = 1142
	mysqlErrLockWaitTimeout         uint16 = 1205
	mysqlErrDeadlockFound           uint16 = 1213
	mysqlErrSpecificAccessDenied    uint16 = 1227
	mysqlErrColumnCannotBeNull      uint16 = 1048
	mysqlErrTruncatedWrongValue     uint16 = 1292
	mysqlErrIncorrectValue          uint16 = 1366
	mysqlErrServerGone              uint16 = 2006
	mysqlErrFkParentStillReferenced uint16 = 1451
	mysqlErrFkChildNotFound         uint16 = 1452
	mysqlErrCheckConstraintViolated uint16 = 3819
	mysqlErrConstraintFailed        uint16 = 4025
)

func mapMySQLError(module string, err error) *AppError {
	var mysqlErr *mysqldriver.MySQLError
	if !errors.As(err, &mysqlErr) {
		return nil
	}

	switch module {
	case ModuleHelicopter:
		return mapMySQLErrorHelicopter(mysqlErr, err)
	case ModuleHospital:
		return mapMySQLErrorHospital(mysqlErr, err)
	case ModuleFleetStatus:
		return mapMySQLErrorFleetStatus(mysqlErr, err)
	case ModuleComplaint:
		return mapMySQLErrorComplaint(mysqlErr, err)
	default:
		return mapMySQLErrorGeneral(mysqlErr, err)
	}
}

func mapMySQLErrorFleetStatus(mysqlErr *mysqldriver.MySQLError, cause error) *AppError {
	switch mysqlErr.Number {
	case mysqlErrDuplicateEntry:
		return New(ErrFleetStatusDuplicate).WithCause(cause)
	case mysqlErrFkParentStillReferenced:
		return New(ErrFleetStatusRelationDelete).WithCause(cause)
	case mysqlErrFkChildNotFound:
		// fleet_status.helicopter_id references a non-existent helicopter.
		return New(ErrFleetStatusHelicopterInvalid).WithCause(cause)
	case mysqlErrColumnCannotBeNull,
		mysqlErrTruncatedWrongValue,
		mysqlErrIncorrectValue,
		mysqlErrCheckConstraintViolated,
		mysqlErrConstraintFailed:
		return New(ErrFleetStatusInvalidPayload).WithCause(cause)
	default:
		return nil
	}
}

func mapMySQLErrorGeneral(mysqlErr *mysqldriver.MySQLError, cause error) *AppError {
	switch mysqlErr.Number {
	case mysqlErrAccessDenied:
		return New(ErrUserUnauthorized).WithCause(cause)
	case mysqlErrCommandDenied, mysqlErrSpecificAccessDenied:
		return New(ErrForbiddenAccess).WithCause(cause)
	case mysqlErrLockWaitTimeout, mysqlErrDeadlockFound:
		return New(ErrBusinessRuleFailed).WithCause(cause)
	case mysqlErrServerGone:
		return New(ErrInternal).WithCause(cause)
	case mysqlErrDuplicateEntry, mysqlErrFkParentStillReferenced:
		return New(ErrBusinessRuleFailed).WithCause(cause)
	case mysqlErrColumnCannotBeNull,
		mysqlErrTruncatedWrongValue,
		mysqlErrIncorrectValue,
		mysqlErrFkChildNotFound,
		mysqlErrCheckConstraintViolated,
		mysqlErrConstraintFailed:
		return New(ErrValidation).WithCause(cause)
	default:
		return nil
	}
}

func mapMySQLErrorHelicopter(mysqlErr *mysqldriver.MySQLError, cause error) *AppError {
	switch mysqlErr.Number {
	case mysqlErrDuplicateEntry:
		return New(ErrHelicopterDuplicate).WithCause(cause)
	case mysqlErrFkParentStillReferenced:
		return New(ErrHelicopterRelationDelete).WithCause(cause)
	case mysqlErrColumnCannotBeNull,
		mysqlErrTruncatedWrongValue,
		mysqlErrIncorrectValue,
		mysqlErrFkChildNotFound,
		mysqlErrCheckConstraintViolated,
		mysqlErrConstraintFailed:
		return New(ErrHelicopterInvalidPayload).WithCause(cause)
	default:
		return nil
	}
}

func mapMySQLErrorHospital(mysqlErr *mysqldriver.MySQLError, cause error) *AppError {
	switch mysqlErr.Number {
	case mysqlErrFkParentStillReferenced:
		return New(ErrHospitalRelationDelete).WithCause(cause)
	case mysqlErrColumnCannotBeNull,
		mysqlErrTruncatedWrongValue,
		mysqlErrIncorrectValue,
		mysqlErrFkChildNotFound,
		mysqlErrCheckConstraintViolated,
		mysqlErrConstraintFailed:
		return New(ErrHospitalInvalidPayload).WithCause(cause)
	default:
		return nil
	}
}

func mapMySQLErrorComplaint(mysqlErr *mysqldriver.MySQLError, cause error) *AppError {
	switch mysqlErr.Number {
	case mysqlErrDuplicateEntry:
		return New(ErrComplaintInvalidPayload).WithCause(cause)
	case mysqlErrFkParentStillReferenced:
		return New(ErrComplaintRelationDelete).WithCause(cause)
	case mysqlErrFkChildNotFound:
		return New(ErrComplaintInvalidHelicopterID).WithCause(cause)
	case mysqlErrColumnCannotBeNull,
		mysqlErrTruncatedWrongValue,
		mysqlErrIncorrectValue,
		mysqlErrCheckConstraintViolated,
		mysqlErrConstraintFailed:
		return New(ErrComplaintInvalidPayload).WithCause(cause)
	default:
		return nil
	}
}
