<?php

namespace App\Http\Controllers\Admin;

use App\Http\Controllers\Controller;
use App\Models\Language;
use App\Models\LanguageTranslation;
use App\Services\UiTextCatalogService;
use Illuminate\Http\RedirectResponse;
use Illuminate\Http\Request;
use Illuminate\Pagination\LengthAwarePaginator;
use Illuminate\Support\Facades\DB;
use Illuminate\Validation\Rule;
use Illuminate\Validation\ValidationException;
use Illuminate\View\View;

class LanguageController extends Controller
{
    public function index(): View
    {
        $languages = Language::query()->withCount('translations')->orderByDesc('is_default')->orderBy('name')->get();

        return view('admin.settings.languages.index', compact('languages'));
    }

    public function edit(Language $language): View
    {
        $language->loadCount('translations');

        return view('admin.settings.languages.edit', compact('language'));
    }

    public function store(Request $request): RedirectResponse
    {
        $request->merge(['code' => $this->normalizeCode((string) $request->input('code'))]);
        $data = $request->validate([
            'name' => ['required', 'string', 'max:100'],
            'code' => ['required', 'string', 'max:15', 'regex:/^[a-z]{2,3}(?:-[a-z0-9]{2,8})?$/', 'unique:languages,code'],
            'native_name' => ['nullable', 'string', 'max:100'],
            'direction' => ['required', Rule::in(['ltr', 'rtl'])],
            'is_active' => ['nullable', 'boolean'],
            'is_default' => ['nullable', 'boolean'],
        ]);

        $language = DB::transaction(function () use ($data, $request): Language {
            $makeDefault = $request->boolean('is_default') || ! Language::query()->where('is_default', true)->exists();
            if ($makeDefault) Language::query()->update(['is_default' => false]);

            return Language::query()->create([
                'name' => trim($data['name']), 'code' => $data['code'],
                'native_name' => filled($data['native_name'] ?? null) ? trim($data['native_name']) : null,
                'direction' => $data['direction'],
                'is_active' => $makeDefault || $request->boolean('is_active'), 'is_default' => $makeDefault,
            ]);
        });

        return redirect()->route('admin.languages.translations', $language)->with('success', 'Language added. You can now enter its translations.');
    }

    public function update(Request $request, Language $language): RedirectResponse
    {
        $request->merge(['code' => $this->normalizeCode((string) $request->input('code'))]);
        $data = $request->validate([
            'name' => ['required', 'string', 'max:100'],
            'code' => ['required', 'string', 'max:15', 'regex:/^[a-z]{2,3}(?:-[a-z0-9]{2,8})?$/', Rule::unique('languages', 'code')->ignore($language->id)],
            'native_name' => ['nullable', 'string', 'max:100'],
            'direction' => ['required', Rule::in(['ltr', 'rtl'])],
            'is_active' => ['nullable', 'boolean'],
        ]);

        $oldCode = $language->code;
        $language->update([
            'name' => trim($data['name']), 'code' => $data['code'],
            'native_name' => filled($data['native_name'] ?? null) ? trim($data['native_name']) : null,
            'direction' => $data['direction'], 'is_active' => $language->is_default ? true : $request->boolean('is_active'),
        ]);
        if ($oldCode !== $language->code && $request->session()->get('locale') === $oldCode) $request->session()->put('locale', $language->code);

        return back()->with('success', 'Language settings updated.');
    }

    public function makeDefault(Language $language): RedirectResponse
    {
        DB::transaction(function () use ($language): void {
            Language::query()->where('id', '!=', $language->id)->update(['is_default' => false]);
            $language->update(['is_default' => true, 'is_active' => true]);
        });

        return back()->with('success', $language->name.' is now the default language.');
    }

    public function destroy(Request $request, Language $language): RedirectResponse
    {
        if ($language->is_default) throw ValidationException::withMessages(['language' => 'The default language cannot be deleted. Choose another default language first.']);
        if ($request->session()->get('locale') === $language->code) $request->session()->forget('locale');
        $language->delete();

        return redirect()->route('admin.languages.index')->with('success', 'Language deleted.');
    }

    public function translations(Request $request, Language $language, UiTextCatalogService $uiCatalog): View
    {
        $catalog = $uiCatalog->catalog();
        $defaultLanguage = Language::query()->where('is_default', true)->first();
        $defaultOverrides = $defaultLanguage?->translations()->pluck('value', 'key') ?? collect();
        $translations = $language->translations()->pluck('value', 'key');

        $customKeys = $translations->keys()->merge($defaultOverrides->keys())
            ->filter(fn (string $key): bool => str_starts_with($key, 'ui.'))->unique();
        foreach ($customKeys as $key) {
            if (! $catalog->has($key)) $catalog->put($key, $defaultOverrides->get($key, $key));
        }

        $catalogTotal = $catalog->count();
        $query = trim((string) $request->query('q', ''));
        if ($query !== '') {
            $needle = mb_strtolower($query);
            $catalog = $catalog->filter(fn (string $value, string $key): bool =>
                str_contains(mb_strtolower($key), $needle) || str_contains(mb_strtolower($value), $needle));
        }

        $page = max(1, (int) $request->query('page', 1));
        $perPage = 100;
        $catalog = new LengthAwarePaginator(
            $catalog->slice(($page - 1) * $perPage, $perPage, true),
            $catalog->count(), $perPage, $page,
            ['path' => $request->url(), 'query' => $request->except('page')],
        );

        return view('admin.settings.languages.translations', compact('language', 'catalog', 'catalogTotal', 'query', 'translations'));
    }

    public function updateTranslations(Request $request, Language $language): RedirectResponse
    {
        $data = $request->validate(['translations' => ['nullable', 'array'], 'translations.*' => ['nullable', 'string', 'max:5000']]);
        DB::transaction(function () use ($data, $language): void {
            foreach (($data['translations'] ?? []) as $key => $value) {
                if (! is_string($key) || ! str_starts_with($key, 'ui.')) continue;
                $value = trim((string) $value);
                if ($value === '') {
                    LanguageTranslation::query()->where('language_id', $language->id)->where('key', $key)->delete();
                    continue;
                }
                LanguageTranslation::query()->updateOrCreate(['language_id' => $language->id, 'key' => $key], ['value' => $value]);
            }
        });

        return back()->with('success', 'Translations saved.');
    }

    public function storeTranslation(Request $request, Language $language): RedirectResponse
    {
        $data = $request->validate([
            'key' => ['required', 'string', 'max:191', 'regex:/^ui\.[a-z0-9_.-]+$/'],
            'value' => ['required', 'string', 'max:5000'],
        ]);
        LanguageTranslation::query()->updateOrCreate(
            ['language_id' => $language->id, 'key' => strtolower(trim($data['key']))], ['value' => trim($data['value'])]);

        return back()->with('success', 'Translation added.');
    }

    private function normalizeCode(string $code): string
    {
        return strtolower(str_replace('_', '-', trim($code)));
    }
}
