<?php

namespace App\Services\Localization;

use App\Models\Language;
use App\Models\Translation;
use Illuminate\Database\Eloquent\Collection as EloquentCollection;
use Illuminate\Support\Arr;
use Illuminate\Support\Facades\DB;

class LanguageService
{
    public function modules(): array
    {
        return config('booklio_languages.modules', []);
    }

    public function catalog(?string $locale = null): array
    {
        $translations = config('booklio_languages.translations', []);

        return $locale ? ($translations[$locale] ?? []) : $translations;
    }

    public function ensureDefaults(bool $fillMissingTranslations = false): void
    {
        DB::transaction(function () use ($fillMissingTranslations): void {
            $defaultCode = config('booklio_languages.default', 'en');

            foreach (config('booklio_languages.languages', []) as $code => $language) {
                $model = Language::query()->firstOrCreate(
                    ['code' => $code],
                    [
                        'name' => $language['name'],
                        'native_name' => $language['native_name'],
                        'direction' => $language['direction'],
                        'is_default' => $code === $defaultCode,
                        'is_active' => true,
                        'sort_order' => $language['sort_order'] ?? 0,
                    ]
                );

                if ($fillMissingTranslations) {
                    $model->forceFill([
                        'name' => $language['name'],
                        'native_name' => $language['native_name'],
                        'direction' => $language['direction'],
                        'is_active' => true,
                        'sort_order' => $language['sort_order'] ?? $model->sort_order,
                    ])->save();
                }

                $this->seedCatalogFor($model, $fillMissingTranslations);
            }

            if ($fillMissingTranslations || ! Language::query()->where('is_default', true)->exists()) {
                $this->enforceSingleDefault($defaultCode);
            }
        });
    }

    public function seedCatalogFor(Language $language, bool $fillMissingTranslations = false): void
    {
        $englishCatalog = $this->catalog('en');
        $localeCatalog = $this->catalog($language->code);

        foreach ($englishCatalog as $module => $rows) {
            foreach ($rows as $key => $defaultText) {
                $translatedText = data_get($localeCatalog, "{$module}.{$key}");

                $row = Translation::query()->firstOrNew(
                    [
                        'language_id' => $language->id,
                        'module' => $module,
                        'translation_key' => $key,
                    ]
                );

                $row->default_text = $defaultText;

                if (! $row->exists || ($fillMissingTranslations && ! filled($row->translated_text))) {
                    $row->translated_text = $this->repairMojibake($translatedText);
                    $row->is_missing = ! filled($translatedText);
                } else {
                    $row->is_missing = ! filled($row->translated_text);
                }

                $row->save();
            }
        }
    }

    public function activeLanguages(): EloquentCollection
    {
        return Language::query()
            ->where('is_active', true)
            ->orderByDesc('is_default')
            ->orderBy('sort_order')
            ->orderBy('name')
            ->get();
    }

    public function defaultLanguage(): ?Language
    {
        return Language::query()->where('is_default', true)->where('is_active', true)->first()
            ?? Language::query()->where('code', config('booklio_languages.default', 'en'))->first()
            ?? Language::query()->where('is_active', true)->orderBy('sort_order')->first();
    }

    public function setDefault(Language $language): void
    {
        DB::transaction(function () use ($language): void {
            Language::query()->whereKeyNot($language->id)->update(['is_default' => false]);
            $language->forceFill(['is_default' => true, 'is_active' => true])->save();
        });
    }

    public function enforceSingleDefault(string $code): void
    {
        Language::query()->where('code', '!=', $code)->update(['is_default' => false]);
        Language::query()->where('code', $code)->update(['is_default' => true, 'is_active' => true]);
    }

    public function exportPayload(Language $language, ?string $module = null): array
    {
        $query = $language->translations()->orderBy('module')->orderBy('translation_key');

        if ($module && $module !== 'all') {
            $query->where('module', $module);
        }

        $translations = [];

        foreach ($query->get() as $translation) {
            data_set($translations, "{$translation->module}.{$translation->translation_key}", $translation->translated_text ?? '');
        }

        return [
            'locale' => $language->code,
            'name' => $language->name,
            'native_name' => $language->native_name,
            'direction' => $language->direction,
            'module' => $module ?: 'all',
            'translations' => $translations,
        ];
    }

    public function importPayload(Language $language, array $payload): int
    {
        $translations = Arr::dot($payload['translations'] ?? []);
        $count = 0;

        foreach ($translations as $key => $text) {
            $parts = explode('.', (string) $key, 2);

            if (count($parts) !== 2) {
                continue;
            }

            [$module, $translationKey] = $parts;

            $row = Translation::query()
                ->where('language_id', $language->id)
                ->where('module', $module)
                ->where('translation_key', $translationKey)
                ->first();

            if (! $row) {
                continue;
            }

            $row->update([
                'translated_text' => $text,
                'is_missing' => ! filled($text),
            ]);
            $count++;
        }

        return $count;
    }

    public function translationPayload(?Language $current = null): array
    {
        $current ??= $this->defaultLanguage();
        $languages = $this->activeLanguages()->load('translations');

        return [
            'defaultLocale' => $this->defaultLanguage()?->code ?? 'en',
            'currentLocale' => $current?->code ?? 'en',
            'direction' => $current?->direction ?? 'ltr',
            'languages' => $languages->mapWithKeys(function (Language $language): array {
                return [
                    $language->code => [
                        'code' => $language->code,
                        'name' => $language->name,
                        'native' => $this->nativeName($language),
                        'direction' => $language->direction,
                        'enabled' => $language->is_active,
                        'strings' => $language->translations
                            ->mapWithKeys(fn (Translation $translation) => [
                                "{$translation->module}.{$translation->translation_key}" => $this->repairMojibake($translation->translated_text ?: $translation->default_text),
                            ])
                            ->all(),
                        'lookup' => $this->sourceLookup($language),
                    ],
                ];
            })->all(),
        ];
    }

    private function sourceLookup(Language $language): array
    {
        $lookup = [];

        foreach ($language->translations as $translation) {
            $source = trim((string) $translation->default_text);
            $target = trim($this->repairMojibake((string) ($translation->translated_text ?: $translation->default_text)));

            if ($source !== '' && $target !== '') {
                $lookup[$source] = $target;
            }
        }

        $configured = collect(config("booklio_ui_text.lookup.{$language->code}", []))
            ->map(fn (string $value): string => $this->repairMojibake($value))
            ->all();

        return array_merge($lookup, $configured);
    }

    private function nativeName(Language $language): string
    {
        if ($language->code === 'ar') {
            return json_decode('"\u0627\u0644\u0639\u0631\u0628\u064a\u0629"', false, 512, JSON_THROW_ON_ERROR);
        }

        if ($language->code === 'ur') {
            return json_decode('"\u0627\u0631\u062f\u0648"', false, 512, JSON_THROW_ON_ERROR);
        }

        return match ($language->code) {
            'ar' => 'العربية',
            'ur' => 'اردو',
            default => $this->repairMojibake($language->native_name),
        };
    }

    private function repairMojibake(?string $value): string
    {
        $value = (string) $value;

        if ($value === '' || ! preg_match('/[ØÙÚÛ]/u', $value)) {
            return $value;
        }

        $converted = @mb_convert_encoding($value, 'ISO-8859-1', 'UTF-8');

        return is_string($converted) && mb_check_encoding($converted, 'UTF-8')
            ? $converted
            : $value;
    }
}
