<?php

namespace App\Components\Settings;

use App\Models\Communication\PhoneNumberType;
use App\Models\Setting\PhoneNumber;
use App\Services\HistoryService;
use Illuminate\Support\Facades\DB;
use Illuminate\Validation\Rule;
use Livewire\Attributes\Computed;
use Livewire\Component;

class PhoneNumbers extends Component {
    public ?int $editingId = null;

    public string $label = '';
    public ?int $phone_number_type_id = null;
    public string $phone_number = '';
    public string $provider = 'twilio';

    public ?string $twilio_account_sid = null;
    public ?string $twilio_subaccount_sid = null;
    public ?string $twilio_phone_number_sid = null;

    public bool $is_active = true;
    public bool $is_primary = false;

    public array $capabilities = ['voice'];

    public bool $showForm = false;

    public function create(): void {
        $this->resetForm();
        $this->showForm = true;
    }

    public function edit(int $id): void {
        $phoneNumber = PhoneNumber::findOrFail($id);

        $this->editingId               = $phoneNumber->id;
        $this->label                   = (string) $phoneNumber->label;
        $this->phone_number_type_id    = $phoneNumber->phone_number_type_id;
        $this->phone_number            = (string) $phoneNumber->phone_number;
        $this->provider                = (string) $phoneNumber->provider;
        $this->twilio_account_sid      = $phoneNumber->twilio_account_sid;
        $this->twilio_subaccount_sid   = $phoneNumber->twilio_subaccount_sid;
        $this->twilio_phone_number_sid = $phoneNumber->twilio_phone_number_sid;
        $this->is_active               = (bool) $phoneNumber->is_active;
        $this->is_primary              = (bool) $phoneNumber->is_primary;
        $this->capabilities            = is_array($phoneNumber->capabilities) ? $phoneNumber->capabilities : [];

        $this->showForm = true;
    }

    public function save(HistoryService $history): void {
        $this->authorize('settings.edit');
        $validated = $this->validate($this->rules(), [], $this->validationAttributes());
        $wasEditing = (bool) $this->editingId;
        $normalized = $this->normalizePhoneNumber($validated['phone_number']);

        DB::transaction(function () use ($validated, $normalized) {
            if ($validated['is_primary']) {
                PhoneNumber::query()->update(['is_primary' => false]);
            }

            $payload = [
                'phone_number_type_id'      => $validated['phone_number_type_id'],
                'label'                     => $validated['label'] ?: null,
                'phone_number'              => $validated['phone_number'],
                'normalized_phone_number'   => $normalized,
                'provider'                  => $validated['provider'],
                'twilio_account_sid'        => $validated['twilio_account_sid'] ?: null,
                'twilio_subaccount_sid'     => $validated['twilio_subaccount_sid'] ?: null,
                'twilio_phone_number_sid'   => $validated['twilio_phone_number_sid'] ?: null,
                'is_active'                 => (bool) $validated['is_active'],
                'is_primary'               => (bool) $validated['is_primary'],
                'capabilities'              => array_values($validated['capabilities'] ?? []),
            ];

            if ($this->editingId) {
                PhoneNumber::findOrFail($this->editingId)->update($payload);
            } else {
                PhoneNumber::create($payload);
            }
        });

        $history->addForActor('Global phone number "' . $validated['phone_number'] . '" ' . ($wasEditing ? 'updated' : 'added'), [
            'phone_number' => $validated['phone_number'],
            'provider'     => $validated['provider'],
            'is_primary'   => (bool) $validated['is_primary'],
            'is_active'    => (bool) $validated['is_active'],
        ]);

        $this->dispatch('notify', message: $this->editingId ? 'Phone number updated.' : 'Phone number added.');
        $this->resetForm();
        $this->showForm = false;
    }

    public function setPrimary(int $id, HistoryService $history): void {
        $this->authorize('settings.edit');

        DB::transaction(function () use ($id) {
            PhoneNumber::query()->update(['is_primary' => false]);
            PhoneNumber::findOrFail($id)->update(['is_primary' => true, 'is_active' => true]);
        });

        $phoneNumber = PhoneNumber::find($id);
        $history->addForActor('Global phone number "' . ($phoneNumber?->phone_number ?? $id) . '" set as primary', [
            'phone_number_id' => $id,
            'phone_number'    => $phoneNumber?->phone_number,
        ]);

        $this->dispatch('notify', message: 'Primary phone number updated.');
    }

    public function toggleActive(int $id, HistoryService $history): void {
        $this->authorize('settings.edit');
        $phoneNumber = PhoneNumber::findOrFail($id);

        if ($phoneNumber->is_primary && $phoneNumber->is_active) {
            $activeCount = PhoneNumber::where('is_active', true)->count();

            if ($activeCount <= 1) {
                $this->addError('general', 'You cannot deactivate the only active primary phone number.');
                return;
            }
        }

        $phoneNumber->update(['is_active' => !$phoneNumber->is_active]);

        $history->addForActor('Global phone number "' . $phoneNumber->phone_number . '" ' . ($phoneNumber->is_active ? 'activated' : 'deactivated'), [
            'phone_number_id' => $phoneNumber->id,
            'phone_number'    => $phoneNumber->phone_number,
            'is_active'       => (bool) $phoneNumber->is_active,
        ]);

        $this->dispatch('notify', message: 'Phone number status updated.');
    }

    public function delete(int $id, HistoryService $history): void {
        $this->authorize('settings.edit');
        $phoneNumber = PhoneNumber::findOrFail($id);

        if ($phoneNumber->is_primary) {
            $this->addError('general', 'You cannot delete the primary phone number until another is made primary.');
            return;
        }

        $context = ['phone_number_id' => $phoneNumber->id, 'phone_number' => $phoneNumber->phone_number];
        $phoneNumber->delete();

        $history->addForActor('Global phone number "' . $context['phone_number'] . '" deleted', $context);

        $this->dispatch('notify', message: 'Phone number deleted.');

        if ($this->editingId === $id) {
            $this->resetForm();
            $this->showForm = false;
        }
    }

    public function cancel(): void {
        $this->resetForm();
        $this->showForm = false;
    }

    #[Computed]
    public function phoneNumbers() {
        return PhoneNumber::query()
            ->with('type')
            ->orderByDesc('is_primary')
            ->orderByDesc('is_active')
            ->orderBy('label')
            ->orderBy('id')
            ->get();
    }

    #[Computed]
    public function phoneNumberTypes() {
        return PhoneNumberType::query()
            ->where('is_active', true)
            ->orderBy('name')
            ->get();
    }

    protected function rules(): array {
        return [
            'label'                  => ['nullable', 'string', 'max:255'],
            'phone_number_type_id'   => ['required', 'integer', Rule::exists('phone_number_types', 'id')],
            'phone_number'           => [
                'required',
                'string',
                'max:50',
                Rule::unique('phone_numbers', 'normalized_phone_number')->ignore($this->editingId),
            ],
            'provider'               => ['required', 'string', 'max:50'],
            'twilio_account_sid'     => ['nullable', 'string', 'max:255'],
            'twilio_subaccount_sid'  => ['nullable', 'string', 'max:255'],
            'twilio_phone_number_sid' => ['nullable', 'string', 'max:255'],
            'is_active'              => ['required', 'boolean'],
            'is_primary'             => ['required', 'boolean'],
            'capabilities'           => ['nullable', 'array'],
            'capabilities.*'         => ['string', Rule::in(['voice', 'sms', 'whatsapp'])],
        ];
    }

    protected function validationAttributes(): array {
        return [
            'phone_number_type_id'    => 'phone number type',
            'phone_number'            => 'phone number',
            'twilio_account_sid'      => 'Twilio account SID',
            'twilio_subaccount_sid'   => 'Twilio subaccount SID',
            'twilio_phone_number_sid' => 'Twilio phone number SID',
        ];
    }

    protected function resetForm(): void {
        $this->reset([
            'editingId',
            'label',
            'phone_number_type_id',
            'phone_number',
            'provider',
            'twilio_account_sid',
            'twilio_subaccount_sid',
            'twilio_phone_number_sid',
            'is_active',
            'is_primary',
            'capabilities',
        ]);

        $this->provider     = 'twilio';
        $this->is_active    = true;
        $this->is_primary   = false;
        $this->capabilities = ['voice'];

        $this->resetErrorBag();
        $this->resetValidation();
    }

    protected function normalizePhoneNumber(?string $value): string {
        $value = trim((string) $value);

        if ($value === '') {
            return '';
        }

        $value = preg_replace('/[^\d+]/', '', $value) ?? '';

        if (str_starts_with($value, '00')) {
            $value = '+' . substr($value, 2);
        }

        if (!str_starts_with($value, '+')) {
            if (str_starts_with($value, '0')) {
                $value = '+44' . substr($value, 1);
            } else {
                $value = '+' . $value;
            }
        }

        return $value;
    }

    public function render() {
        return view('components.settings.phone-numbers');
    }
}
