<?php

namespace App\Components\Dialler;

use App\Actions\Communications\StartOutboundCallAction;
use App\Interface\CallableEntity;
use App\Models\Communication\Communication;
use Illuminate\Database\Eloquent\Model;
use Livewire\Attributes\Computed;
use Livewire\Attributes\On;
use Livewire\Component;
use RuntimeException;
use App\Services\HistoryService;
use App\Models\Client\Client;

/**
 * CallManager — per-client dialler panel (contact tab)
 *
 * Responsibility: initiate outbound calls and display recent communications.
 * The full in-call experience (ringing, active call controls, and disposition
 * wrap-up) is handled by the global CallOverlay component mounted in the header.
 */
class CallManager extends Component {
    public Model $communicable;
    public ?int  $caseId = null;

    // Device status (used only to gate the dial button)
    public bool    $deviceReady       = false;
    public bool    $deviceRegistering = false;
    public ?string $errorMessage      = null;

    public function mount(Model $communicable, ?int $caseId = null): void
    {
        if (! $communicable instanceof CallableEntity) {
            throw new RuntimeException('CallManager requires a model implementing CallableEntity.');
        }

        $this->communicable = $communicable;
        $this->caseId       = $caseId;
    }

    // ── Outbound call ─────────────────────────────────────────────────────────

    public function startCall(StartOutboundCallAction $action): void
    {
        abort_unless(auth()->check(), 403);

        if (! $this->deviceReady) {
            $this->errorMessage = 'The browser phone is not ready yet.';
            return;
        }

        /** @var CallableEntity $target */
        $target   = $this->communicable;
        $toNumber = $target->getCallableNumber();

        if (! $toNumber) {
            $this->errorMessage = 'This record does not have a callable phone number.';
            return;
        }

        $this->errorMessage = null;

        try {
            $communication = $action->handle(
                subject:     $target,
                toNumber:    $toNumber,
                targetLabel: $target->getCallableLabel(),
                targetType:  $target->getCallableType(),
                callType:    $target->getCallableType(),
                claimId:     $this->caseId,
                solicitorId: $target->solicitor_id,
                uiOrigin:    'call_manager',
            );

            // Tell the global CallOverlay about this outbound call so it can
            // manage the in-call UI and disposition form.
            $this->dispatch('dialler-call-started',
                communication_id: $communication->id,
                call_id:          $communication->call->id,
                label:            $target->getCallableLabel(),
                direction:        'outbound',
            );

            // Tell the JS layer to actually connect via Twilio SDK.
            $this->dispatch('twilio-start-browser-call', call_id: $communication->call->id);

        } catch (\Throwable $e) {
            $this->errorMessage = $e->getMessage();
            report($e);
        }
    }

    // ── Device events (gate the dial button) ─────────────────────────────────

    #[On('twilio-device-registering')]
    public function handleDeviceRegistering(): void
    {
        $this->deviceRegistering = true;
        $this->deviceReady       = false;
    }

    #[On('twilio-device-registered')]
    public function handleDeviceRegistered(): void
    {
        $this->deviceRegistering = false;
        $this->deviceReady       = true;
        $this->errorMessage      = null;
    }

    #[On('twilio-device-unregistered')]
    public function handleDeviceUnregistered(): void
    {
        $this->deviceRegistering = false;
        $this->deviceReady       = false;
    }

    #[On('twilio-device-error')]
    public function handleDeviceError($message = null): void
    {
        $this->deviceRegistering = false;
        $this->deviceReady       = false;
        $this->errorMessage      = is_string($message) ? $message : 'Softphone error.';
    }

    public function updateContactMade(HistoryService $historyService, bool $value): void
    {
        if (! $this->communicable instanceof Client) {
            return;
        }

        $oldValue = (bool) $this->communicable->contact_made;

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

        $this->communicable->update([
            'contact_made' => $value,
        ]);

        $historyService->add(
            $this->communicable,
            $value ? 'Client contact marked as Contact Made' : 'Client contact marked as No Contact',
            [
                'field' => 'contact_made',
                'old_value' => $oldValue,
                'new_value' => $value,
            ]
        );

        $this->communicable->refresh();

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

    // ── Computed ──────────────────────────────────────────────────────────────

    #[Computed]
    public function callableNumber(): ?string
    {
        return $this->communicable->getCallableNumber();
    }

    #[Computed]
    public function callableLabel(): string
    {
        return $this->communicable->getCallableLabel();
    }

    #[Computed]
    public function recentCommunications()
    {
        return Communication::query()
            ->with(['call', 'agent', 'recordings'])
            ->where('communicable_type', $this->communicable->getMorphClass())
            ->where('communicable_id', $this->communicable->getKey())
            ->latest('started_at')
            ->limit(8)
            ->get();
    }

    public function render() {
        return view('components.dialler.call-manager');
    }
}
