<?php

declare(strict_types=1);

namespace ImovelZapAi\Properties\Application\Data;

use ImovelZapAi\Properties\Domain\Enums\PropertyPurpose;
use ImovelZapAi\Properties\Domain\Enums\PropertyStatus;
use ImovelZapAi\Properties\Domain\Enums\PropertyType;

final readonly class PropertySearchFilters
{
    public function __construct(
        public ?string $code = null,
        public ?string $title = null,
        public ?string $neighborhood = null,
        public ?string $city = null,
        public ?PropertyPurpose $purpose = null,
        public ?PropertyStatus $status = null,
        public ?PropertyType $type = null,
        public ?float $maxPrice = null,
        public ?float $minPrice = null,
        public ?int $minBedrooms = null,
        public ?int $minParkingSpaces = null,
        public ?float $minUsefulArea = null,
    ) {}

    public function mergeWith(self $other): self
    {
        return new self(
            code: $other->code ?? $this->code,
            title: $other->title ?? $this->title,
            neighborhood: $other->neighborhood ?? $this->neighborhood,
            city: $other->city ?? $this->city,
            purpose: $other->purpose ?? $this->purpose,
            status: $other->status ?? $this->status,
            type: $other->type ?? $this->type,
            maxPrice: $other->maxPrice ?? $this->maxPrice,
            minPrice: $other->minPrice ?? $this->minPrice,
            minBedrooms: $other->minBedrooms ?? $this->minBedrooms,
            minParkingSpaces: $other->minParkingSpaces ?? $this->minParkingSpaces,
            minUsefulArea: $other->minUsefulArea ?? $this->minUsefulArea,
        );
    }

    /**
     * @param  array<string, mixed>  $input
     */
    public static function fromQueryArray(array $input): self
    {
        return self::fromRequest([
            'code' => $input['code'] ?? null,
            'title' => $input['title'] ?? null,
            'neighborhood' => $input['neighborhood'] ?? null,
            'city' => $input['city'] ?? null,
            'purpose' => $input['purpose'] ?? null,
            'status' => $input['status'] ?? null,
            'type' => $input['type'] ?? null,
            'max_price' => $input['max_price'] ?? null,
            'min_price' => $input['min_price'] ?? null,
            'min_bedrooms' => $input['min_bedrooms'] ?? null,
            'min_parking_spaces' => $input['min_parking_spaces'] ?? null,
            'min_useful_area' => $input['min_useful_area'] ?? null,
        ]);
    }

    /**
     * @param  array<string, mixed>  $input
     */
    public static function fromRequest(array $input): self
    {
        $purpose = isset($input['purpose']) && is_string($input['purpose']) && $input['purpose'] !== ''
            ? PropertyPurpose::tryFrom($input['purpose'])
            : null;

        $status = isset($input['status']) && is_string($input['status']) && $input['status'] !== ''
            ? PropertyStatus::tryFrom($input['status'])
            : null;

        $type = isset($input['type']) && is_string($input['type']) && $input['type'] !== ''
            ? PropertyType::tryFrom($input['type'])
            : null;

        return new self(
            code: self::nullableTrimmedString($input['code'] ?? null),
            title: self::nullableTrimmedString($input['title'] ?? null),
            neighborhood: self::nullableTrimmedString($input['neighborhood'] ?? null),
            city: self::nullableTrimmedString($input['city'] ?? null),
            purpose: $purpose,
            status: $status,
            type: $type,
            maxPrice: self::nullableFloat($input['max_price'] ?? $input['maxPrice'] ?? null),
            minPrice: self::nullableFloat($input['min_price'] ?? $input['minPrice'] ?? null),
            minBedrooms: self::nullableInt($input['min_bedrooms'] ?? $input['minBedrooms'] ?? null),
            minParkingSpaces: self::nullableInt($input['min_parking_spaces'] ?? $input['minParkingSpaces'] ?? null),
            minUsefulArea: self::nullableFloat($input['min_useful_area'] ?? $input['minUsefulArea'] ?? null),
        );
    }

    /**
     * @return array<string, string>
     */
    public function toQueryArray(): array
    {
        return array_filter([
            'code' => $this->code,
            'title' => $this->title,
            'neighborhood' => $this->neighborhood,
            'city' => $this->city,
            'purpose' => $this->purpose?->value,
            'status' => $this->status?->value,
            'type' => $this->type?->value,
            'max_price' => $this->maxPrice !== null ? (string) $this->maxPrice : null,
            'min_price' => $this->minPrice !== null ? (string) $this->minPrice : null,
            'min_bedrooms' => $this->minBedrooms !== null ? (string) $this->minBedrooms : null,
            'min_parking_spaces' => $this->minParkingSpaces !== null ? (string) $this->minParkingSpaces : null,
            'min_useful_area' => $this->minUsefulArea !== null ? (string) $this->minUsefulArea : null,
        ], static fn (?string $value): bool => $value !== null && $value !== '');
    }

    private static function nullableFloat(mixed $value): ?float
    {
        if ($value === null || $value === '') {
            return null;
        }

        if (is_numeric($value)) {
            return (float) $value;
        }

        return null;
    }

    private static function nullableInt(mixed $value): ?int
    {
        if ($value === null || $value === '') {
            return null;
        }

        if (is_numeric($value)) {
            return (int) $value;
        }

        return null;
    }

    private static function nullableTrimmedString(mixed $value): ?string
    {
        if (! is_string($value)) {
            return null;
        }

        $trimmed = trim($value);

        return $trimmed === '' ? null : $trimmed;
    }
}
