<?php

declare(strict_types=1);

namespace ImovelZapAi\Publishing\Application\Services;

use DOMDocument;
use ImovelZapAi\Publishing\Domain\Feeds\VrSync\ListingDataFeed;
use ImovelZapAi\Publishing\Domain\Feeds\VrSync\ListingDataFeedDetails;
use ImovelZapAi\Publishing\Domain\Feeds\VrSync\ListingDataFeedListing;
use ImovelZapAi\Publishing\Domain\Feeds\VrSync\ListingDataFeedLocation;
use ImovelZapAi\Publishing\Domain\Feeds\VrSync\ListingDataFeedMediaItem;

/**
 * Serializa {@see ListingDataFeed} VRSync para XML.
 *
 * Não monta o feed — apenas renderiza a estrutura já gerada.
 */
final class ListingDataFeedXmlSerializer
{
    public function serialize(ListingDataFeed $feed): string
    {
        $document = new DOMDocument('1.0', 'UTF-8');
        $document->formatOutput = true;

        $root = $document->createElementNS(ListingDataFeed::NAMESPACE, 'ListingDataFeed');
        $root->setAttributeNS('http://www.w3.org/2000/xmlns/', 'xmlns:xsi', 'http://www.w3.org/2001/XMLSchema-instance');
        $root->setAttributeNS('http://www.w3.org/2001/XMLSchema-instance', 'xsi:schemaLocation', ListingDataFeed::SCHEMA_LOCATION);
        $document->appendChild($root);

        $header = $document->createElement('Header');
        $this->appendTextElement($document, $header, 'Provider', $feed->header->provider);
        $this->appendTextElement($document, $header, 'Email', $feed->header->email);

        if ($feed->header->contact !== null) {
            $this->appendTextElement($document, $header, 'ContactName', $feed->header->contact->name);
        }

        $this->appendTextElement($document, $header, 'PublishDate', $feed->header->publishDate);
        $this->appendOptionalTextElement($document, $header, 'Logo', $feed->header->logo);

        if ($feed->header->contact !== null) {
            $this->appendOptionalTextElement($document, $header, 'Telephone', $feed->header->contact->telephone);
        }

        $root->appendChild($header);

        $listings = $document->createElement('Listings');
        $root->appendChild($listings);

        foreach ($feed->listings as $listing) {
            $listings->appendChild($this->listingElement($document, $listing));
        }

        $xml = $document->saveXML();

        return is_string($xml) ? $xml : '';
    }

    private function listingElement(DOMDocument $document, ListingDataFeedListing $listing): \DOMElement
    {
        $node = $document->createElement('Listing');

        $this->appendTextElement($document, $node, 'ListingID', $listing->listingId);
        $this->appendCdataElement($document, $node, 'Title', $listing->title);
        $this->appendTextElement($document, $node, 'TransactionType', $listing->transactionType);
        $this->appendTextElement($document, $node, 'Purpose', $listing->purpose);
        $this->appendTextElement($document, $node, 'Status', $listing->status);

        if ($listing->listDate !== null) {
            $this->appendTextElement($document, $node, 'ListDate', $listing->listDate);
        }

        if ($listing->lastUpdateDate !== null) {
            $this->appendTextElement($document, $node, 'LastUpdateDate', $listing->lastUpdateDate);
        }

        if ($listing->detailUrl !== null && $listing->detailUrl !== '') {
            $this->appendTextElement($document, $node, 'DetailUrl', $listing->detailUrl);
        }

        $node->appendChild($this->detailsElement($document, $listing->details));
        $node->appendChild($this->locationElement($document, $listing->location));

        if ($listing->media !== []) {
            $node->appendChild($this->mediaElement($document, $listing->media));
        }

        return $node;
    }

    private function detailsElement(DOMDocument $document, ListingDataFeedDetails $details): \DOMElement
    {
        $node = $document->createElement('Details');

        $this->appendTextElement($document, $node, 'PropertyType', $details->propertyType);
        $this->appendCdataElement($document, $node, 'Description', $details->description);

        if ($details->listPrice !== null) {
            $price = $document->createElement('ListPrice', $details->listPrice);
            $price->setAttribute('currency', $details->currency);
            $node->appendChild($price);
        }

        $this->appendOptionalTextElement($document, $node, 'Bedrooms', $details->bedrooms);
        $this->appendOptionalTextElement($document, $node, 'Suites', $details->suites);
        $this->appendOptionalTextElement($document, $node, 'Bathrooms', $details->bathrooms);
        $this->appendOptionalTextElement($document, $node, 'Garage', $details->garage);
        $this->appendOptionalTextElement($document, $node, 'LivingArea', $details->livingArea);
        $this->appendOptionalTextElement($document, $node, 'LotArea', $details->lotArea);
        $this->appendOptionalTextElement($document, $node, 'CondominiumFee', $details->condominiumFee);
        $this->appendOptionalTextElement($document, $node, 'Iptu', $details->iptu);

        return $node;
    }

    private function locationElement(DOMDocument $document, ListingDataFeedLocation $location): \DOMElement
    {
        $node = $document->createElement('Location');
        $node->setAttribute('displayAddress', $location->displayAddress);

        $this->appendTextElement($document, $node, 'Country', $location->country);
        $this->appendTextElement($document, $node, 'State', $location->state);
        $this->appendTextElement($document, $node, 'City', $location->city);
        $this->appendTextElement($document, $node, 'Neighborhood', $location->neighborhood);
        $this->appendTextElement($document, $node, 'PostalCode', $location->postalCode);
        $this->appendTextElement($document, $node, 'Street', $location->street);
        $this->appendTextElement($document, $node, 'Number', $location->number);
        $this->appendTextElement($document, $node, 'Complement', $location->complement);
        $this->appendTextElement($document, $node, 'Address', $location->address);
        $this->appendOptionalTextElement($document, $node, 'Latitude', $location->latitude);
        $this->appendOptionalTextElement($document, $node, 'Longitude', $location->longitude);

        return $node;
    }

    /**
     * @param  list<ListingDataFeedMediaItem>  $items
     */
    private function mediaElement(DOMDocument $document, array $items): \DOMElement
    {
        $media = $document->createElement('Media');

        foreach ($items as $item) {
            $node = $document->createElement('Item');
            $node->setAttribute('medium', $item->medium);
            $node->setAttribute('primary', $item->isPrimary ? 'true' : 'false');

            if ($item->caption !== null && $item->caption !== '') {
                $node->setAttribute('caption', $item->caption);
            }

            $node->appendChild($document->createTextNode($item->url));
            $media->appendChild($node);
        }

        return $media;
    }

    private function appendTextElement(DOMDocument $document, \DOMElement $parent, string $name, string $value): void
    {
        $element = $document->createElement($name);
        $element->appendChild($document->createTextNode($value));
        $parent->appendChild($element);
    }

    private function appendOptionalTextElement(DOMDocument $document, \DOMElement $parent, string $name, ?string $value): void
    {
        if ($value === null || $value === '') {
            return;
        }

        $this->appendTextElement($document, $parent, $name, $value);
    }

    private function appendCdataElement(DOMDocument $document, \DOMElement $parent, string $name, string $value): void
    {
        $element = $document->createElement($name);
        $element->appendChild($document->createCDATASection($value));
        $parent->appendChild($element);
    }
}
