<?php

declare(strict_types=1);

namespace ImovelZapAi\Publishing\Application\Services;

use ImovelZapAi\Properties\Infrastructure\Persistence\Eloquent\PropertyModel;
use ImovelZapAi\Publishing\Application\Data\FeedHealthCheckData;
use ImovelZapAi\Publishing\Application\Data\FeedHealthData;
use ImovelZapAi\Publishing\Application\Data\XmlValidationResult;
use ImovelZapAi\Publishing\Application\Mappers\ListingMapper;
use ImovelZapAi\Publishing\Application\Queries\PublishedFeedPropertiesQuery;
use ImovelZapAi\Publishing\Domain\Enums\FeedHealthCheckKey;
use ImovelZapAi\Tenancy\Domain\Enums\TenantStatus;
use ImovelZapAi\Tenancy\Infrastructure\Persistence\Eloquent\TenantModel;

final class TenantFeedHealthChecker
{
    public function __construct(
        private readonly TenantFeedStorage $storage,
        private readonly PublishedFeedPropertiesQuery $publishedProperties,
        private readonly ListingMapper $listingMapper,
        private readonly ListingDataFeedXmlValidator $validator,
        private readonly TenantFeedUrlResolver $feedUrls,
        private readonly TenantPublishingConfigResolver $publishingConfig,
    ) {}

    public function check(TenantModel $tenant): FeedHealthData
    {
        $tenantId = (string) $tenant->id;
        $xml = $this->storage->get($tenantId);
        $properties = $this->publishedProperties->execute($tenantId);
        $listingCount = count($properties);

        $xmlValidation = $xml !== null ? $this->validator->validateXml($xml) : null;

        return new FeedHealthData(
            checks: [
                $this->xmlCheck($xml),
                $this->listingCountCheck($listingCount),
                $this->requiredFieldsCheck($properties),
                $this->photosCheck($properties),
                $this->validationCheck($xmlValidation),
                $this->publicUrlCheck($tenant, $tenantId),
            ],
        );
    }

    private function xmlCheck(?string $xml): FeedHealthCheckData
    {
        $passed = $xml !== null
            && str_contains($xml, '<?xml')
            && str_contains($xml, 'ListingDataFeed');

        return new FeedHealthCheckData(
            key: FeedHealthCheckKey::Xml,
            passed: $passed,
            detail: $passed ? null : 'Arquivo XML em cache ausente ou inválido.',
        );
    }

    private function listingCountCheck(int $listingCount): FeedHealthCheckData
    {
        return new FeedHealthCheckData(
            key: FeedHealthCheckKey::ListingCount,
            passed: $listingCount > 0,
            detail: $listingCount > 0 ? null : 'Nenhum imóvel publicado no feed.',
        );
    }

    /**
     * @param  list<PropertyModel>  $properties
     */
    private function requiredFieldsCheck(array $properties): FeedHealthCheckData
    {
        if ($properties === []) {
            return new FeedHealthCheckData(
                key: FeedHealthCheckKey::RequiredFields,
                passed: false,
                detail: 'Sem imóveis para validar.',
            );
        }

        foreach ($properties as $property) {
            $listing = $this->listingMapper->toNormalized($property);

            if (! $listing->isValidForExport()) {
                return new FeedHealthCheckData(
                    key: FeedHealthCheckKey::RequiredFields,
                    passed: false,
                    detail: $listing->exportValidationErrors()[0] ?? 'Campos obrigatórios incompletos.',
                );
            }

            if (trim($listing->title) === '' || trim($listing->description) === '') {
                return new FeedHealthCheckData(
                    key: FeedHealthCheckKey::RequiredFields,
                    passed: false,
                    detail: 'Título ou descrição ausentes em um imóvel do feed.',
                );
            }
        }

        return new FeedHealthCheckData(
            key: FeedHealthCheckKey::RequiredFields,
            passed: true,
        );
    }

    /**
     * @param  list<PropertyModel>  $properties
     */
    private function photosCheck(array $properties): FeedHealthCheckData
    {
        if ($properties === []) {
            return new FeedHealthCheckData(
                key: FeedHealthCheckKey::Photos,
                passed: false,
                detail: 'Sem imóveis no feed.',
            );
        }

        foreach ($properties as $property) {
            $listing = $this->listingMapper->toNormalized($property);

            if ($listing->photos === []) {
                return new FeedHealthCheckData(
                    key: FeedHealthCheckKey::Photos,
                    passed: false,
                    detail: 'Imóvel '.$listing->code.' sem fotos válidas.',
                );
            }
        }

        return new FeedHealthCheckData(
            key: FeedHealthCheckKey::Photos,
            passed: true,
        );
    }

    private function validationCheck(?XmlValidationResult $result): FeedHealthCheckData
    {
        if ($result === null) {
            return new FeedHealthCheckData(
                key: FeedHealthCheckKey::Validation,
                passed: false,
                detail: 'XML em cache indisponível para validação.',
            );
        }

        return new FeedHealthCheckData(
            key: FeedHealthCheckKey::Validation,
            passed: $result->isValid(),
            detail: $result->firstError(),
        );
    }

    private function publicUrlCheck(TenantModel $tenant, string $tenantId): FeedHealthCheckData
    {
        $config = $this->publishingConfig->forTenant($tenantId);

        $passed = $tenant->status === TenantStatus::Active
            && $config->isPublishingActive()
            && $this->storage->exists($tenantId)
            && $this->feedUrls->resolve($tenant) !== '';

        return new FeedHealthCheckData(
            key: FeedHealthCheckKey::PublicUrl,
            passed: $passed,
            detail: $passed ? null : ($config->isPublishingActive() ? 'Feed público indisponível.' : 'Publicação pausada pela imobiliária.'),
        );
    }
}
