<?php

namespace App\Components\Solicitor;

use App\Models\Communication\PhoneNumberType;
use App\Models\Communication\SolicitorPhoneNumber;
use App\Models\Solicitor\Solicitor;
use Illuminate\Foundation\Auth\Access\AuthorizesRequests;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Storage;
use Illuminate\Validation\Rule;
use Livewire\Attributes\Computed;
use Livewire\Component;
use Livewire\WithFileUploads;

class PhoneNumbers extends Component {
    use AuthorizesRequests;
    use WithFileUploads;
    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 ?string $twilio_twiml_app_sid = null;

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

    public array $capabilities = ['voice'];

    // Newly-uploaded MP3 for this line's voicemail greeting (temporary upload).
    public $voicemail_greeting = null;
    // Stored path of the greeting already on the record being edited, if any.
    public ?string $existing_voicemail_greeting_path = null;

    public bool $showForm = false;
    public Solicitor $solicitor;

    public function create(): void {
        $this->resetForm();
        $this->is_active = true;
        $this->is_primary = false;
        $this->provider = 'twilio';
        $this->capabilities = ['voice'];
        $this->showForm = true;
    }

    public function edit(int $id): void {
        $phoneNumber = $this->getOwnedPhoneNumber($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->twilio_twiml_app_sid = $phoneNumber->twilio_twiml_app_sid;
        $this->is_active = (bool)$phoneNumber->is_active;
        $this->is_primary = (bool)$phoneNumber->is_primary;
        $this->capabilities = is_array($phoneNumber->capabilities) ? $phoneNumber->capabilities : [];
        $this->existing_voicemail_greeting_path = $phoneNumber->voicemail_greeting_path;

        $this->showForm = true;
    }

    public function save(): void {
        $this->authorize('solicitors.edit');
        $validated = $this->validate($this->rules(), [], $this->validationAttributes());

        $solicitorId = $this->solicitorId();
        $normalized = $this->normalizePhoneNumber($validated['phone_number']);

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

            $payload = [
                'solicitor_id' => $solicitorId,
                '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,
                'twilio_twiml_app_sid' => $validated['twilio_twiml_app_sid'] ?: null,
                'is_active' => (bool)$validated['is_active'],
                'is_primary' => (bool)$validated['is_primary'],
                'capabilities' => array_values($validated['capabilities'] ?? []),
            ];

            if ($this->editingId) {
                $phoneNumber = $this->getOwnedPhoneNumber($this->editingId);
                $phoneNumber->update($payload);
            } else {
                $phoneNumber = SolicitorPhoneNumber::query()->create($payload);
            }

            // Replace the greeting only when a new MP3 was uploaded; leaving the
            // field empty on edit preserves whatever greeting is already stored.
            if ($this->voicemail_greeting) {
                $this->storeVoicemailGreeting($phoneNumber);
            }
        });

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

        $this->resetForm();
        $this->showForm = false;
    }

    /**
     * Store the uploaded MP3 as this line's voicemail greeting, deleting any
     * previous greeting file first, then persist the new path on the record.
     */
    protected function storeVoicemailGreeting(SolicitorPhoneNumber $phoneNumber): void {
        if ($phoneNumber->voicemail_greeting_path) {
            Storage::delete($phoneNumber->voicemail_greeting_path);
        }

        $path = $this->voicemail_greeting->storeAs(
            "solicitors/{$phoneNumber->solicitor_id}/voicemail",
            "greeting-{$phoneNumber->id}.mp3",
        );

        $phoneNumber->update(['voicemail_greeting_path' => $path]);
    }

    public function removeVoicemailGreeting(int $id): void {
        $this->authorize('solicitors.edit');
        $phoneNumber = $this->getOwnedPhoneNumber($id);

        if ($phoneNumber->voicemail_greeting_path) {
            Storage::delete($phoneNumber->voicemail_greeting_path);
            $phoneNumber->update(['voicemail_greeting_path' => null]);
        }

        if ($this->editingId === $id) {
            $this->existing_voicemail_greeting_path = null;
        }

        $this->dispatch('notify', message: 'Voicemail greeting removed.');
    }

    public function setPrimary(int $id): void {
        $this->authorize('solicitors.edit');
        $phoneNumber = $this->getOwnedPhoneNumber($id);

        DB::transaction(function () use ($phoneNumber)
        {
            SolicitorPhoneNumber::query()
                ->where('solicitor_id', $this->solicitorId())
                ->update(['is_primary' => false]);

            $phoneNumber->update([
                'is_primary' => true,
                'is_active' => true,
            ]);
        });

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

    public function toggleActive(int $id): void {
        $this->authorize('solicitors.edit');
        $phoneNumber = $this->getOwnedPhoneNumber($id);

        if ($phoneNumber->is_primary && $phoneNumber->is_active) {
            $activeCount = SolicitorPhoneNumber::query()
                ->where('solicitor_id', $this->solicitorId())
                ->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,
        ]);

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

    public function delete(int $id): void {
        $this->authorize('solicitors.edit');
        $phoneNumber = $this->getOwnedPhoneNumber($id);

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

        if ($phoneNumber->voicemail_greeting_path) {
            Storage::delete($phoneNumber->voicemail_greeting_path);
        }

        $phoneNumber->delete();

        $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 SolicitorPhoneNumber::query()
            ->with('type')
            ->where('solicitor_id', $this->solicitorId())
            ->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('solicitor_phone_numbers', 'normalized_phone_number')
                    ->ignore($this->editingId)
                    ->where(fn($query) => $query->where('solicitor_id', $this->solicitorId())),
            ],
            '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'],
            'twilio_twiml_app_sid' => ['nullable', 'string', 'max:255'],
            'is_active' => ['required', 'boolean'],
            'is_primary' => ['required', 'boolean'],
            'capabilities' => ['nullable', 'array'],
            'capabilities.*' => ['string', Rule::in(['voice', 'sms', 'whatsapp'])],
            'voicemail_greeting' => ['nullable', 'file', 'mimes:mp3', 'max:5120'],
        ];
    }

    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',
            'twilio_twiml_app_sid' => 'Twilio TwiML App SID',
            'voicemail_greeting' => 'voicemail greeting',
        ];
    }

    protected function solicitorId(): int {
        return $this->solicitor->id;
    }

    protected function getOwnedPhoneNumber(int $id): SolicitorPhoneNumber {
        return SolicitorPhoneNumber::query()
            ->where('solicitor_id', $this->solicitorId())
            ->findOrFail($id);
    }

    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',
            'twilio_twiml_app_sid',
            'is_active',
            'is_primary',
            'capabilities',
            'voicemail_greeting',
            'existing_voicemail_greeting_path',
        ]);

        $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.solicitor.phone-numbers');
    }
}
