package at.mocode.horses.application.usecase

import at.mocode.horses.domain.model.DomPferd
import at.mocode.horses.domain.repository.HorseRepository
import at.mocode.enums.PferdeGeschlechtE
import at.mocode.enums.DatenQuelleE
import at.mocode.dto.base.ApiResponse
import at.mocode.dto.base.ErrorDto
import at.mocode.validation.ValidationResult
import at.mocode.validation.ValidationError
import com.benasher44.uuid.Uuid
import kotlinx.datetime.LocalDate
import kotlinx.datetime.todayIn

/**
 * Use case for creating a new horse in the registry.
 *
 * This use case handles the business logic for horse registration including
 * validation, uniqueness checks, and persistence.
 */
class CreateHorseUseCase(
    private val horseRepository: HorseRepository
) {

    /**
     * Request data for creating a new horse.
     */
    data class CreateHorseRequest(
        val pferdeName: String,
        val geschlecht: PferdeGeschlechtE,
        val geburtsdatum: LocalDate? = null,
        val rasse: String? = null,
        val farbe: String? = null,
        val besitzerId: Uuid? = null,
        val verantwortlichePersonId: Uuid? = null,
        val zuechterName: String? = null,
        val zuchtbuchNummer: String? = null,
        val lebensnummer: String? = null,
        val chipNummer: String? = null,
        val passNummer: String? = null,
        val oepsNummer: String? = null,
        val feiNummer: String? = null,
        val vaterName: String? = null,
        val mutterName: String? = null,
        val mutterVaterName: String? = null,
        val stockmass: Int? = null,
        val bemerkungen: String? = null,
        val datenQuelle: DatenQuelleE = DatenQuelleE.MANUELL
    )


    /**
     * Executes the horse creation use case.
     *
     * @param request The horse creation request data
     * @return ApiResponse with the created horse or validation errors
     */
    suspend fun execute(request: CreateHorseRequest): ApiResponse<DomPferd> {
        // Create domain object
        val horse = DomPferd(
            pferdeName = request.pferdeName,
            geschlecht = request.geschlecht,
            geburtsdatum = request.geburtsdatum,
            rasse = request.rasse,
            farbe = request.farbe,
            besitzerId = request.besitzerId,
            verantwortlichePersonId = request.verantwortlichePersonId,
            zuechterName = request.zuechterName,
            zuchtbuchNummer = request.zuchtbuchNummer,
            lebensnummer = request.lebensnummer,
            chipNummer = request.chipNummer,
            passNummer = request.passNummer,
            oepsNummer = request.oepsNummer,
            feiNummer = request.feiNummer,
            vaterName = request.vaterName,
            mutterName = request.mutterName,
            mutterVaterName = request.mutterVaterName,
            stockmass = request.stockmass,
            bemerkungen = request.bemerkungen,
            datenQuelle = request.datenQuelle
        )

        // Validate the horse
        val validationResult = validateHorse(horse)
        if (!validationResult.isValid()) {
            val errors = (validationResult as ValidationResult.Invalid).errors
            return ApiResponse(
                success = false,
                data = null,
                error = ErrorDto(
                    code = "VALIDATION_ERROR",
                    message = "Horse validation failed",
                    details = errors.associate { it.field to it.message }
                )
            )
        }

        // Check for uniqueness constraints
        val uniquenessResult = checkUniquenessConstraints(horse)
        if (!uniquenessResult.isValid()) {
            val errors = (uniquenessResult as ValidationResult.Invalid).errors
            return ApiResponse(
                success = false,
                data = null,
                error = ErrorDto(
                    code = "UNIQUENESS_ERROR",
                    message = "Horse uniqueness validation failed",
                    details = errors.associate { it.field to it.message }
                )
            )
        }

        // Save the horse
        val savedHorse = horseRepository.save(horse)

        return ApiResponse(
            success = true,
            data = savedHorse,
            message = "Horse created successfully"
        )
    }

    /**
     * Validates the horse data according to business rules.
     */
    private fun validateHorse(horse: DomPferd): ValidationResult {
        val errors = mutableListOf<ValidationError>()

        // Use domain validation
        val domainErrors = horse.validateForRegistration()
        domainErrors.forEach { errorMessage ->
            errors.add(ValidationError("horse", errorMessage, "DOMAIN_VALIDATION"))
        }

        // Additional business validations
        horse.stockmass?.let { height ->
            if (height < 50 || height > 220) {
                errors.add(ValidationError("stockmass", "Horse height must be between 50 and 220 cm", "INVALID_RANGE"))
            }
        }

        horse.geburtsdatum?.let { birthDate ->
            val currentYear = kotlinx.datetime.Clock.System.todayIn(kotlinx.datetime.TimeZone.currentSystemDefault()).year
            if (birthDate.year > currentYear) {
                errors.add(ValidationError("geburtsdatum", "Birth date cannot be in the future", "FUTURE_DATE"))
            }
            if (birthDate.year < (currentYear - 50)) {
                errors.add(ValidationError("geburtsdatum", "Birth date cannot be more than 50 years ago", "TOO_OLD"))
            }
        }

        return if (errors.isEmpty()) {
            ValidationResult.Valid
        } else {
            ValidationResult.Invalid(errors)
        }
    }

    /**
     * Checks uniqueness constraints for identification numbers.
     */
    private suspend fun checkUniquenessConstraints(horse: DomPferd): ValidationResult {
        val errors = mutableListOf<ValidationError>()

        // Check lebensnummer uniqueness
        horse.lebensnummer?.let { lebensnummer ->
            if (lebensnummer.isNotBlank() && horseRepository.existsByLebensnummer(lebensnummer)) {
                errors.add(ValidationError("lebensnummer", "A horse with this life number already exists", "DUPLICATE"))
            }
        }

        // Check chip number uniqueness
        horse.chipNummer?.let { chipNummer ->
            if (chipNummer.isNotBlank() && horseRepository.existsByChipNummer(chipNummer)) {
                errors.add(ValidationError("chipNummer", "A horse with this chip number already exists", "DUPLICATE"))
            }
        }

        // Check passport number uniqueness
        horse.passNummer?.let { passNummer ->
            if (passNummer.isNotBlank() && horseRepository.existsByPassNummer(passNummer)) {
                errors.add(ValidationError("passNummer", "A horse with this passport number already exists", "DUPLICATE"))
            }
        }

        // Check OEPS number uniqueness
        horse.oepsNummer?.let { oepsNummer ->
            if (oepsNummer.isNotBlank() && horseRepository.existsByOepsNummer(oepsNummer)) {
                errors.add(ValidationError("oepsNummer", "A horse with this OEPS number already exists", "DUPLICATE"))
            }
        }

        // Check FEI number uniqueness
        horse.feiNummer?.let { feiNummer ->
            if (feiNummer.isNotBlank() && horseRepository.existsByFeiNummer(feiNummer)) {
                errors.add(ValidationError("feiNummer", "A horse with this FEI number already exists", "DUPLICATE"))
            }
        }

        return if (errors.isEmpty()) {
            ValidationResult.Valid
        } else {
            ValidationResult.Invalid(errors)
        }
    }
}
