<?php

namespace App\Service;

use App\Entity\Advert;
use App\Entity\Customer;
use App\Entity\CustomerContact;
use App\Entity\Devis;
use App\Entity\Domain;
use App\Entity\Facture;
use App\Entity\PriceAutomatic;
use App\Entity\Revendeur;
use App\Entity\Website;
use Meilisearch\Client;
use Psr\Log\LoggerInterface;
use Symfony\Component\DependencyInjection\Attribute\Autowire;

class MeilisearchService
{
    private const FILTER_CUSTOMER_ID = 'customerId = ';

    private Client $client;

    /**
     * @param array<string, mixed> $document
     */
    private function addToIndex(string $index, array $document, string $label): void
    {
        try {
            $this->client->index($index)->addDocuments([$document]);
        } catch (\Throwable $e) {
            $this->logger->error('Meilisearch: Failed to index '.$label.': '.$e->getMessage());
        }
    }

    private function removeFromIndex(string $index, int $id, string $label): void
    {
        try {
            $this->client->index($index)->deleteDocument($id);
        } catch (\Throwable $e) {
            $this->logger->error('Meilisearch: Failed to remove '.$label.' '.$id.': '.$e->getMessage());
        }
    }

    /**
     * @return list<array<string, mixed>>
     */
    private function searchIndex(string $index, string $query, int $limit, ?int $customerId = null): array
    {
        try {
            $options = ['limit' => $limit];
            if (null !== $customerId) {
                $options['filter'] = self::FILTER_CUSTOMER_ID.$customerId;
            }

            return $this->client->index($index)->search($query, $options)->getHits();
        } catch (\Throwable $e) {
            $this->logger->error('Meilisearch: search '.$index.' error: '.$e->getMessage());

            return [];
        }
    }

    public function __construct(
        private LoggerInterface $logger,
        #[Autowire(env: 'MEILISEARCH_URL')] string $url,
        #[Autowire(env: 'MEILISEARCH_API_KEY')] string $apiKey,
    ) {
        $this->client = new Client($url, $apiKey);
    }

    public function indexCustomer(Customer $customer): void { $this->addToIndex('customer', $this->serializeCustomer($customer), 'customer '.$customer->getId()); }
    public function removeCustomer(int $id): void { $this->removeFromIndex('customer', $id, 'customer'); }
    /** @return list<array<string, mixed>> */
    public function searchCustomers(string $query, int $limit = 20): array { return $this->searchIndex('customer', $query, $limit); }

    public function indexRevendeur(Revendeur $revendeur): void { $this->addToIndex('reseller', $this->serializeRevendeur($revendeur), 'reseller '.$revendeur->getId()); }
    public function removeRevendeur(int $id): void { $this->removeFromIndex('reseller', $id, 'reseller'); }
    /** @return list<array<string, mixed>> */
    public function searchRevendeurs(string $query, int $limit = 20): array { return $this->searchIndex('reseller', $query, $limit); }

    public function indexPrice(PriceAutomatic $price): void { $this->addToIndex('price_auto', $this->serializePrice($price), 'price '.$price->getId()); }
    public function removePrice(int $id): void { $this->removeFromIndex('price_auto', $id, 'price'); }
    /** @return list<array<string, mixed>> */
    public function searchPrices(string $query, int $limit = 20): array { return $this->searchIndex('price_auto', $query, $limit); }

    public function indexContact(CustomerContact $contact): void { $this->addToIndex('customer_contact', $this->serializeContact($contact), 'contact '.$contact->getId()); }
    public function removeContact(int $id): void { $this->removeFromIndex('customer_contact', $id, 'contact'); }
    /** @return list<array<string, mixed>> */
    public function searchContacts(string $query, int $limit = 20): array { return $this->searchIndex('customer_contact', $query, $limit); }

    public function indexDomain(Domain $domain): void { $this->addToIndex('customer_ndd', $this->serializeDomain($domain), 'domain '.$domain->getId()); }
    public function removeDomain(int $id): void { $this->removeFromIndex('customer_ndd', $id, 'domain'); }
    /** @return list<array<string, mixed>> */
    public function searchDomains(string $query, int $limit = 20): array { return $this->searchIndex('customer_ndd', $query, $limit); }

    public function indexWebsite(Website $website): void { $this->addToIndex('customer_website', $this->serializeWebsite($website), 'website '.$website->getId()); }
    public function removeWebsite(int $id): void { $this->removeFromIndex('customer_website', $id, 'website'); }
    /** @return list<array<string, mixed>> */
    public function searchWebsites(string $query, int $limit = 20): array { return $this->searchIndex('customer_website', $query, $limit); }

    public function indexDevis(Devis $devis): void { $this->addToIndex('customer_devis', $this->serializeDevis($devis), 'devis '.$devis->getId()); }
    public function removeDevis(int $id): void { $this->removeFromIndex('customer_devis', $id, 'devis'); }
    /** @return list<array<string, mixed>> */
    public function searchDevis(string $query, int $limit = 20, ?int $customerId = null): array { return $this->searchIndex('customer_devis', $query, $limit, $customerId); }

    public function indexAdvert(Advert $advert): void { $this->addToIndex('customer_advert', $this->serializeAdvert($advert), 'advert '.$advert->getId()); }
    public function removeAdvert(int $id): void { $this->removeFromIndex('customer_advert', $id, 'advert'); }
    /** @return list<array<string, mixed>> */
    public function searchAdverts(string $query, int $limit = 20, ?int $customerId = null): array { return $this->searchIndex('customer_advert', $query, $limit, $customerId); }

    public function indexFacture(Facture $facture): void { $this->addToIndex('customer_facture', $this->serializeFacture($facture), 'facture '.$facture->getId()); }
    public function removeFacture(int $id): void { $this->removeFromIndex('customer_facture', $id, 'facture'); }
    /** @return list<array<string, mixed>> */
    public function searchFactures(string $query, int $limit = 20, ?int $customerId = null): array { return $this->searchIndex('customer_facture', $query, $limit, $customerId); }

    public function purgeAllIndexes(): void
    {
        $indexes = ['customer', 'reseller', 'price_auto', 'customer_contact', 'customer_ndd', 'customer_website', 'customer_devis', 'customer_advert', 'customer_facture'];

        foreach ($indexes as $index) {
            try {
                $this->client->index($index)->deleteAllDocuments();
            } catch (\Throwable $e) {
                $this->logger->warning('Meilisearch: purge index '.$index.' failed: '.$e->getMessage());
            }
        }
    }

    public function getIndexCount(string $indexName): int
    {
        try {
            $stats = $this->client->index($indexName)->stats();

            return $stats['numberOfDocuments'] ?? 0;
        } catch (\Throwable) {
            return 0;
        }
    }

    public function setupIndexes(): void
    {
        try {
            $this->client->createIndex('customer', ['primaryKey' => 'id']);
        } catch (\Throwable $e) {
            $this->logger->warning('Meilisearch: setupIndexes (customer) - '.$e->getMessage());
        }
        $this->client->index('customer')->updateSearchableAttributes([
            'firstName', 'lastName', 'raisonSociale', 'fullName', 'email', 'phone', 'siret', 'rcs', 'numTva', 'codeComptable', 'city', 'zipCode', 'address',
        ]);
        $this->client->index('customer')->updateFilterableAttributes([
            'state', 'typeCompany',
        ]);

        try {
            $this->client->createIndex('reseller', ['primaryKey' => 'id']);
        } catch (\Throwable $e) {
            $this->logger->warning('Meilisearch: setupIndexes (reseller) - '.$e->getMessage());
        }
        $this->client->index('reseller')->updateSearchableAttributes([
            'firstName', 'lastName', 'fullName', 'raisonSociale', 'email', 'phone', 'siret', 'codeRevendeur', 'city', 'zipCode', 'address',
        ]);
        $this->client->index('reseller')->updateFilterableAttributes([
            'isActive',
        ]);

        try {
            $this->client->createIndex('price_auto', ['primaryKey' => 'id']);
        } catch (\Throwable $e) {
            $this->logger->warning('Meilisearch: setupIndexes (price_auto) - '.$e->getMessage());
        }
        $this->client->index('price_auto')->updateSearchableAttributes([
            'type', 'title', 'description',
        ]);
        $this->client->index('price_auto')->updateFilterableAttributes([
            'type', 'period',
        ]);

        try {
            $this->client->createIndex('customer_contact', ['primaryKey' => 'id']);
        } catch (\Throwable $e) {
            $this->logger->warning('Meilisearch: setupIndexes (customer_contact) - '.$e->getMessage());
        }
        $this->client->index('customer_contact')->updateSearchableAttributes([
            'firstName', 'lastName', 'fullName', 'email', 'phone', 'role',
        ]);
        $this->client->index('customer_contact')->updateFilterableAttributes([
            'customerId', 'isBillingEmail',
        ]);

        try {
            $this->client->createIndex('customer_ndd', ['primaryKey' => 'id']);
        } catch (\Throwable $e) {
            $this->logger->warning('Meilisearch: setupIndexes (customer_ndd) - '.$e->getMessage());
        }
        $this->client->index('customer_ndd')->updateSearchableAttributes([
            'fqdn', 'registrar', 'customerName', 'customerEmail',
        ]);
        $this->client->index('customer_ndd')->updateFilterableAttributes([
            'customerId', 'isGestion', 'isBilling',
        ]);

        try {
            $this->client->createIndex('customer_website', ['primaryKey' => 'id']);
        } catch (\Throwable $e) {
            $this->logger->warning('Meilisearch: setupIndexes (customer_website) - '.$e->getMessage());
        }
        $this->client->index('customer_website')->updateSearchableAttributes([
            'name', 'uuid', 'customerName', 'customerEmail',
        ]);
        $this->client->index('customer_website')->updateFilterableAttributes([
            'customerId', 'type', 'state',
        ]);

        try {
            $this->client->createIndex('customer_devis', ['primaryKey' => 'id']);
        } catch (\Throwable $e) {
            $this->logger->warning('Meilisearch: setupIndexes (customer_devis) - '.$e->getMessage());
        }
        $this->client->index('customer_devis')->updateSearchableAttributes([
            'numOrder', 'customerName', 'customerEmail', 'state',
        ]);
        $this->client->index('customer_devis')->updateFilterableAttributes([
            'customerId', 'state',
        ]);

        try {
            $this->client->createIndex('customer_advert', ['primaryKey' => 'id']);
        } catch (\Throwable $e) {
            $this->logger->warning('Meilisearch: setupIndexes (customer_advert) - '.$e->getMessage());
        }
        $this->client->index('customer_advert')->updateSearchableAttributes([
            'numOrder', 'customerName', 'customerEmail', 'state',
        ]);
        $this->client->index('customer_advert')->updateFilterableAttributes([
            'customerId', 'state',
        ]);

        try {
            $this->client->createIndex('customer_facture', ['primaryKey' => 'id']);
        } catch (\Throwable $e) {
            $this->logger->warning('Meilisearch: setupIndexes (customer_facture) - '.$e->getMessage());
        }
        $this->client->index('customer_facture')->updateSearchableAttributes([
            'invoiceNumber', 'customerName', 'customerEmail', 'state',
        ]);
        $this->client->index('customer_facture')->updateFilterableAttributes([
            'customerId', 'state', 'isPaid',
        ]);
    }

    /**
     * @return array<string, mixed>
     */
    private function serializePrice(PriceAutomatic $price): array
    {
        return [
            'id' => $price->getId(),
            'type' => $price->getType(),
            'title' => $price->getTitle(),
            'description' => $price->getDescription(),
            'priceHt' => $price->getPriceHt(),
            'monthPrice' => $price->getMonthPrice(),
            'period' => $price->getPeriod(),
            'stripeId' => $price->getStripeId(),
            'stripeAbonnementId' => $price->getStripeAbonnementId(),
        ];
    }

    /**
     * @return array<string, mixed>
     */
    private function serializeCustomer(Customer $customer): array
    {
        return [
            'id' => $customer->getId(),
            'firstName' => $customer->getFirstName(),
            'lastName' => $customer->getLastName(),
            'raisonSociale' => $customer->getRaisonSociale(),
            'fullName' => $customer->getFullName(),
            'email' => $customer->getEmail(),
            'phone' => $customer->getPhone(),
            'address' => $customer->getAddress(),
            'address2' => $customer->getAddress2(),
            'zipCode' => $customer->getZipCode(),
            'city' => $customer->getCity(),
            'siret' => $customer->getSiret(),
            'rcs' => $customer->getRcs(),
            'numTva' => $customer->getNumTva(),
            'codeComptable' => $customer->getCodeComptable(),
            'stripeCustomerId' => $customer->getStripeCustomerId(),
            'typeCompany' => $customer->getTypeCompany(),
            'state' => $customer->getState(),
        ];
    }

    /**
     * @return array<string, mixed>
     */
    private function serializeRevendeur(Revendeur $revendeur): array
    {
        return [
            'id' => $revendeur->getId(),
            'codeRevendeur' => $revendeur->getCodeRevendeur(),
            'firstName' => $revendeur->getUser()->getFirstName(),
            'lastName' => $revendeur->getUser()->getLastName(),
            'fullName' => $revendeur->getUser()->getFullName(),
            'raisonSociale' => $revendeur->getRaisonSociale(),
            'email' => $revendeur->getEmail(),
            'phone' => $revendeur->getPhone(),
            'address' => $revendeur->getAddress(),
            'zipCode' => $revendeur->getZipCode(),
            'city' => $revendeur->getCity(),
            'siret' => $revendeur->getSiret(),
            'stripeConnectId' => $revendeur->getStripeConnectId(),
            'isUseStripe' => $revendeur->isUseStripe(),
            'isActive' => $revendeur->isActive(),
        ];
    }

    /**
     * @return array<string, mixed>
     */
    private function serializeContact(CustomerContact $contact): array
    {
        return [
            'id' => $contact->getId(),
            'customerId' => $contact->getCustomer()->getId(),
            'firstName' => $contact->getFirstName(),
            'lastName' => $contact->getLastName(),
            'fullName' => $contact->getFullName(),
            'email' => $contact->getEmail(),
            'phone' => $contact->getPhone(),
            'role' => $contact->getRole(),
            'isBillingEmail' => $contact->isBillingEmail(),
        ];
    }

    /** @return array<string, mixed> */
    private function serializeDomain(Domain $domain): array
    {
        return [
            'id' => $domain->getId(),
            'fqdn' => $domain->getFqdn(),
            'registrar' => $domain->getRegistrar(),
            'isGestion' => $domain->isGestion(),
            'isBilling' => $domain->isBilling(),
            'customerId' => $domain->getCustomer()->getId(),
            'customerName' => $domain->getCustomer()->getFullName(),
            'customerEmail' => $domain->getCustomer()->getEmail(),
        ];
    }

    /** @return array<string, mixed> */
    private function serializeWebsite(Website $website): array
    {
        return [
            'id' => $website->getId(),
            'name' => $website->getName(),
            'uuid' => $website->getUuid(),
            'type' => $website->getType(),
            'state' => $website->getState(),
            'customerId' => $website->getCustomer()->getId(),
            'customerName' => $website->getCustomer()->getFullName(),
            'customerEmail' => $website->getCustomer()->getEmail(),
        ];
    }

    /**
     * @return array<string, mixed>
     */
    private function serializeOrderDocument(object $doc, ?Customer $customer): array
    {
        return [
            'id' => $doc->getId(),
            'numOrder' => $doc->getOrderNumber()->getNumOrder(),
            'state' => $doc->getState(),
            'totalHt' => $doc->getTotalHt(),
            'totalTtc' => $doc->getTotalTtc(),
            'customerId' => $customer?->getId(),
            'customerName' => $customer?->getFullName(),
            'customerEmail' => $customer?->getEmail(),
            'createdAt' => $doc->getCreatedAt()->format('Y-m-d'),
        ];
    }

    /** @return array<string, mixed> */
    private function serializeDevis(Devis $devis): array
    {
        return $this->serializeOrderDocument($devis, $devis->getCustomer());
    }

    /** @return array<string, mixed> */
    private function serializeFacture(Facture $facture): array
    {
        return array_merge($this->serializeOrderDocument($facture, $facture->getCustomer()), [
            'invoiceNumber' => $facture->getInvoiceNumber(),
            'isPaid' => $facture->isPaid(),
            'paidMethod' => $facture->getPaidMethod(),
        ]);
    }

    /** @return array<string, mixed> */
    private function serializeAdvert(Advert $advert): array
    {
        return $this->serializeOrderDocument($advert, $advert->getCustomer());
    }
}
