<?php

declare(strict_types=1);

namespace Dealerdirect\Agreements\Utils;

use Dealerdirect\Agreements\Enums\Locale;
use Dealerdirect\Agreements\Exceptions\FileNotFoundException;
use Dealerdirect\Agreements\Exceptions\InvalidTranslationTypeException;
use Dealerdirect\Agreements\Exceptions\InvalidTranslationValueException;
use Dealerdirect\Agreements\Exceptions\TranslationNotFoundException;
use DealerDirect\Generic\Category\Driveable;
use DealerDirect\Generic\Category\OldNewModel;
use DealerDirect\Generic\Category\ServiceHistoryBooklet;
use DealerDirect\Generic\Category\Site;
use DealerDirect\Generic\Category\TranslationTypes;
use DealerDirect\Generic\Category\VehicleType;
use DealerDirect\Generic\Repository\CommonTranslator;
use DealerDirect\Generic\Repository\GenericProperties;
use DealerDirect\Generic\Repository\Locale as LocaleRepository;
use DealerDirect\Generic\Repository\Translator as GenericTranslator;

final class Translator
{
    private const TRANSLATION_TYPE_SALES_AGREEMENT = 'sales_agreement';
    private const TRANSLATION_TYPE_PURCHASE_AGREEMENT = 'purchase_agreement';
    private const TRANSLATION_TYPE_LABELS = 'labels';
    private const TRANSLATION_TYPE_BICYCLE = 'bicycle';
    private const DEFAULT_LOCALES_PATH = __DIR__ . '/../../resources/lang';
    private const TRANSLATION_TYPES = [
        'general' => TranslationTypes::GENERAL_VALUES,
        'options' => TranslationTypes::OPTIONS,
        'vehicle' => TranslationTypes::VEHICLE_VALUES,
        'sales_agreement' => self::TRANSLATION_TYPE_SALES_AGREEMENT,
        'purchase_agreement' => self::TRANSLATION_TYPE_PURCHASE_AGREEMENT,
        'labels' => self::TRANSLATION_TYPE_LABELS,
        'bicycle' => self::TRANSLATION_TYPE_BICYCLE,
    ];

    private static ?self $instance = null;

    /**
     * @param array<string,array<string,string|array<int,string>>> $translations
     */
    public function __construct(
        public readonly Locale $locale,
        private array $translations,
    ) {
        $translationKeys = array_keys($translations);
        foreach ($translationKeys as $translationKey) {
            if (!array_key_exists($translationKey, self::TRANSLATION_TYPES)) {
                throw new InvalidTranslationTypeException($translationKey, $translationKeys);
            }
        }

        $defaultValue = $translations['general']['color'][0] ?? '-';

        // Overwrite the ugly 'Kies...' value for the 'driveable' option
        $this->translations['general']['driveable'][Driveable::UNKNOWN] = $defaultValue;

        // Overwrite the ugly 'Kies...' value for the 'oldNewModel' option
        $this->translations['general']['oldNewModel'][OldNewModel::UNKNOWN] = $defaultValue;
        $this->translations['general']['serviceHistoryBooklet'][ServiceHistoryBooklet::UNKNOWN] = $defaultValue;

        self::$instance = $this;
    }

    /**
     * @param array<string,string|int> $replacements
     */
    public function get(string $key, array $replacements = []): mixed
    {
        $translation = $this->getTranslation($key);
        if (empty($replacements)) {
            return $translation;
        }

        if (!is_string($translation)) {
            throw new InvalidTranslationValueException($key, $translation);
        }

        foreach ($replacements as $placeholder => $value) {
            $placeholders = [
                '{{' . $placeholder . '}}',
                '{{ ' . $placeholder . ' }}',
                '{{' . $placeholder . ' }}',
                '{{ ' . $placeholder . '}}',
            ];

            $translation = str_replace($placeholders, (string) $value, $translation);
        }

        return $translation;
    }

    public static function getInstance(): self
    {
        if (null === self::$instance) {
            throw new \LogicException('Translator not yet initialized');
        }

        return self::$instance;
    }

    /**
     * @param array<string,string> $replacements
     */
    public static function translate(string $key, array $replacements = []): mixed
    {
        return self::getInstance()->get($key, $replacements);
    }

    public static function create(int $siteReference, string $localesPath = self::DEFAULT_LOCALES_PATH): self
    {
        $locale = Locale::fromSiteReference($siteReference);
        $localeRepository = new LocaleRepository(...explode('_', $locale->value));

        $vehicleType = GeneralUtils::getVehicleTypeBySiteReference($siteReference);
        $genericProperties = new GenericProperties(
            $localeRepository,
            match ($vehicleType) {
                VehicleType::SCOOTER, VehicleType::BICYCLE => VehicleType::MOTOR,
                default => $vehicleType,
            },
        );

        $translations = [];
        foreach (self::TRANSLATION_TYPES as $key => $translationType) {
            $translations[$key] = match ($translationType) {
                self::TRANSLATION_TYPE_LABELS => self::loadLabelsTranslations(
                    $locale,
                    $localesPath
                ),
                self::TRANSLATION_TYPE_SALES_AGREEMENT,
                self::TRANSLATION_TYPE_PURCHASE_AGREEMENT => self::loadAgreementTranslations(
                    $locale,
                    Site::REF_CAR_MARKTPLAATS_NL === $siteReference ? 'marktplaats' : $vehicleType,
                    "$localesPath",
                    $translationType
                ),
                TranslationTypes::GENERAL_VALUES,
                TranslationTypes::OPTIONS,
                TranslationTypes::VEHICLE_VALUES => self::loadGenericTranslations(
                    $genericProperties,
                    $translationType
                ),
                self::TRANSLATION_TYPE_BICYCLE => Locale::NL_NL === $locale
                    ? require "$localesPath/vehicle-values-bicycle-nl_NL.php"
                    : [],
            };
        }

        return new self($locale, $translations);
    }

    private function getTranslation(string $key): mixed
    {
        $keys = explode('.', $key);
        $result = $this->translations;
        foreach ($keys as $keyPart) {
            if (!array_key_exists($keyPart, $result)) {
                throw new TranslationNotFoundException($key);
            }

            $result = $result[$keyPart];
        }

        return $result;
    }

    /**
     * @return array<string,string>
     */
    private static function loadLabelsTranslations(Locale $locale, string $localesPath): array
    {
        $translationsPath = "{$localesPath}/labels-{$locale->value}.php";
        if (!file_exists($translationsPath)) {
            throw new FileNotFoundException($translationsPath);
        }

        return require $translationsPath;
    }

    /**
     * @return array<string,string>
     */
    private static function loadAgreementTranslations(
        Locale $locale,
        string $vehicleType,
        string $localesPath,
        string $translationType
    ): array {
        [$fileName, $subPath] = match ($translationType) {
            self::TRANSLATION_TYPE_SALES_AGREEMENT => ["{$vehicleType}-{$locale->value}.php", 'sales-agreement'],
            self::TRANSLATION_TYPE_PURCHASE_AGREEMENT => ["{$locale->value}.php", 'purchase-agreement'],
        };

        $translationsPath = "{$localesPath}/{$subPath}/{$fileName}";
        if (!file_exists($translationsPath)) {
            throw new FileNotFoundException($translationsPath);
        }

        return require $translationsPath;
    }

    /**
     * @return array<string,string>
     */
    private static function loadGenericTranslations(
        GenericProperties $genericProperties,
        string $translationType
    ): array {
        TranslationTypes::assertExists($translationType);
        $locale = $genericProperties->getLocaleInstance();
        $translator = TranslationTypes::GENERAL_VALUES === $translationType
            ? new CommonTranslator($locale)
            : new GenericTranslator($genericProperties);

        $translations = $translator->getAll($translationType);
        $vehicleType = $genericProperties->getVehicleType();
        if (VehicleType::CAR === $vehicleType && TranslationTypes::OPTIONS === $translationType) {
            // Also load the recreation translations for cars
            $recreationTranslations = self::loadGenericTranslations(
                new GenericProperties($locale, VehicleType::RECREATION),
                $translationType
            );

            foreach ($recreationTranslations as $key => $recreationTranslation) {
                $translations[$key] ??= $recreationTranslation;
            }
        }

        return $translations;
    }
}
