<?php

namespace App\Livewire\Settings;

use App\Domain\Identity\Services\TerminologyService;
use App\Domain\Identity\Support\Terminology;
use App\Domain\Shared\Traits\UsesBranchScope;
use Livewire\Attributes\Layout;
use Livewire\Component;

/**
 * Choose what this branch calls the people it serves.
 *
 * A club says عضو, a compound says مقيم, a beach says مشترك. The stored value
 * never changes — participants.membership_type is 'member' / 'non_member' in
 * every branch — so switching the vocabulary here relabels every screen, list,
 * price field, report and import hint without touching a single row.
 */
#[Layout('layouts.app')]
class TerminologySettings extends Component
{
    use UsesBranchScope;

    public ?int $selectedBranchId = null;

    public string $preset = 'club';

    /** @var array<string, string> */
    public array $terms = [];

    public bool $saved = false;

    public ?string $copiedTo = null;

    public function mount(): void
    {
        $this->authorize('settings.manage');

        $this->selectedBranchId = $this->defaultBranchId();
        $this->load();
    }

    public function updatedSelectedBranchId(): void
    {
        // A plain public property is settable from the browser, and this one
        // decides whose labels get rewritten — so it is reduced to a branch
        // this user may actually edit before anything is read or written.
        $this->selectedBranchId = $this->allowedBranchId($this->selectedBranchId);
        $this->load();
    }

    private function defaultBranchId(): ?int
    {
        $active = $this->getActiveBranchId();

        if ($active !== null) {
            return $active;
        }

        $first = $this->selectableBranches()->first();

        return $first ? (int) $first->id : null;
    }

    private function allowedBranchId(?int $branchId): ?int
    {
        if ($branchId === null) {
            return null;
        }

        return $this->selectableBranches()->contains('id', $branchId)
            ? $branchId
            : $this->defaultBranchId();
    }

    private function service(): TerminologyService
    {
        return app(TerminologyService::class);
    }

    private function load(): void
    {
        $service = $this->service();
        $vocabulary = $service->for($this->selectedBranchId, 'ar');

        $this->preset = $vocabulary->preset;
        $this->terms = [];

        foreach ([...Terminology::BASE_KEYS, ...Terminology::ENGLISH_KEYS] as $key) {
            $this->terms[$key] = (string) ($vocabulary->toArray()[$key] ?? '');
        }

        $this->saved = false;
        $this->copiedTo = null;
        $this->resetErrorBag();
    }

    /**
     * Swap in a whole preset. The fields stay editable afterwards — picking
     * "مجمع سكني" and then changing one word is the normal path, not an edge
     * case, which is why nothing here locks the inputs.
     */
    public function applyPreset(string $preset): void
    {
        $this->authorize('settings.manage');

        $presets = $this->service()->presets();

        if (! isset($presets[$preset])) {
            return;
        }

        $this->preset = $preset;

        foreach ([...Terminology::BASE_KEYS, ...Terminology::ENGLISH_KEYS] as $key) {
            $this->terms[$key] = (string) ($presets[$preset]['terms'][$key] ?? '');
        }

        $this->saved = false;
        $this->copiedTo = null;
        $this->resetErrorBag();
    }

    public function rules(): array
    {
        $rules = [
            'preset' => 'required|string|in:' . implode(',', array_keys($this->service()->presets())),
        ];

        foreach (Terminology::BASE_KEYS as $key) {
            $rules["terms.{$key}"] = 'required|string|max:60';
        }

        foreach (Terminology::ENGLISH_KEYS as $key) {
            $rules["terms.{$key}"] = 'nullable|string|max:60';
        }

        return $rules;
    }

    /**
     * The field labels, shared with the view so an error names the box the
     * user is looking at rather than a second wording of it.
     *
     * @return array<string, array{0: string, 1: string}>  key => [label, club example]
     */
    public static function arabicFields(): array
    {
        return [
            'member'                => ['المنتمي — مفرد نكرة', 'عضو'],
            'member_definite'       => ['المنتمي — مفرد مُعرَّف', 'العضو'],
            'members'               => ['المنتمون — جمع مُعرَّف', 'الأعضاء'],
            'non_member'            => ['غير المنتمي — مفرد نكرة', 'غير عضو'],
            'non_member_definite'   => ['غير المنتمي — مفرد مُعرَّف', 'غير العضو'],
            'non_members'           => ['غير المنتمين — جمع مُعرَّف', 'غير الأعضاء'],
            'membership'            => ['الصفة — مُعرَّفة', 'العضوية'],
            'membership_indefinite' => ['الصفة — نكرة', 'عضوية'],
        ];
    }

    /**
     * @return array<string, array{0: string, 1: string}>
     */
    public static function englishFields(): array
    {
        return [
            'member_en'      => ['Member (singular)', 'Member'],
            'members_en'     => ['Members (plural)', 'Members'],
            'non_member_en'  => ['Non-member (singular)', 'Non-member'],
            'non_members_en' => ['Non-members (plural)', 'Non-members'],
            'membership_en'  => ['Membership', 'Membership'],
        ];
    }

    public function messages(): array
    {
        $messages = ['preset.required' => 'اختر نوع الفرع'];

        foreach (self::arabicFields() as $key => [$label]) {
            $messages["terms.{$key}.required"] = "{$label} مطلوب";
            $messages["terms.{$key}.max"] = "{$label} يجب ألا يتجاوز 60 حرفاً";
        }

        foreach (Terminology::ENGLISH_KEYS as $key) {
            $messages["terms.{$key}.max"] = 'المسمى الإنجليزي يجب ألا يتجاوز 60 حرفاً';
        }

        return $messages;
    }

    public function save(): void
    {
        $this->authorize('settings.manage');

        $this->selectedBranchId = $this->allowedBranchId($this->selectedBranchId);

        if ($this->selectedBranchId === null) {
            $this->addError('branch', __('اختر فرعاً أولاً'));

            return;
        }

        $this->validate();

        try {
            $this->service()->save($this->selectedBranchId, $this->preset, $this->terms);
        } catch (\Throwable $e) {
            report($e);
            $this->addError('branch', __('تعذر حفظ المسميات. حاول مرة أخرى.'));

            return;
        }

        $this->saved = true;
        $this->copiedTo = null;
    }

    /**
     * Nine branches of the same compound should not be configured nine times.
     * Saves first, so what gets copied is what is on screen.
     */
    public function applyToAllBranches(): void
    {
        $this->authorize('settings.manage');

        $this->save();

        if ($this->getErrorBag()->isNotEmpty() || $this->selectedBranchId === null) {
            return;
        }

        try {
            $count = $this->service()->copyToAcademy($this->selectedBranchId);
        } catch (\Throwable $e) {
            report($e);
            $this->addError('branch', __('تعذر نسخ المسميات لباقي الفروع.'));

            return;
        }

        $this->copiedTo = trans_choice(
            '{0}لا توجد فروع أخرى لنسخها|{1}تم تطبيق المسميات على فرع آخر|[2,*]تم تطبيق المسميات على :count فروع أخرى',
            $count,
            ['count' => $count],
        );
    }

    /**
     * What the form currently says, rendered the way the app will render it.
     * Built from the unsaved values so the preview answers "what will this
     * look like" rather than "what did I last save".
     */
    public function getPreviewProperty(): Terminology
    {
        return new Terminology($this->terms, 'ar', $this->preset);
    }

    public function render()
    {
        return view('livewire.settings.terminology-settings', [
            'presets'  => $this->service()->presets(),
            'branches' => $this->selectableBranches(),
            'preview'  => $this->preview,
        ])->title(__('مسميات') . ' ' . term('members'));
    }
}
