<?php

namespace App\Components\Contact\Admin;

use App\Actions\Communications\DispatchContactMessageAction;
use App\Enum\Contact\MessageChannel;
use App\Enum\Contact\MessageOrigin;
use App\Enum\Contact\MessageStatus;
use App\Models\Contact\ContactTemplate;
use App\Models\Contact\Hub\ContactMessage;
use App\Models\Solicitor\SolicitorEmailSender;
use App\Models\Solicitor\SolicitorSmsSender;
use App\ValueObjects\Communications\DispatchRequest;
use Illuminate\Database\Eloquent\Builder;
use Livewire\Component;
use Livewire\WithPagination;

class CommunicationLogIndex extends Component
{
    use WithPagination;

    /* ---------- filters ---------- */
    public ?string $channel = null;
    public ?string $status = null;
    public ?int $templateId = null;
    public ?int $senderEmailId = null;
    public ?int $smsSenderId = null;
    public ?string $origin = null;
    public ?string $search = null;
    public ?string $dateFrom = null;
    public ?string $dateTo = null;

    /* ---------- detail drawer ---------- */
    public ?int $viewingId = null;

    /* ---------- pagination ---------- */
    protected string $paginationTheme = 'bootstrap';
    public int $perPage = 25;

    public function mount(): void
    {
        // Lower-tier users get filtered to their own; higher-tier see all.
        if (!auth()->user()?->can('communications.logs.view.all')
            && !auth()->user()?->can('communications.logs.view.own')) {
            abort(403);
        }
    }

    public function updated($field): void
    {
        if (in_array($field, ['channel', 'status', 'templateId', 'senderEmailId',
                              'smsSenderId', 'origin', 'search', 'dateFrom', 'dateTo'], true)) {
            $this->resetPage();
        }
    }

    public function clearFilters(): void
    {
        $this->reset(['channel', 'status', 'templateId', 'senderEmailId',
                      'smsSenderId', 'origin', 'search', 'dateFrom', 'dateTo']);
        $this->resetPage();
    }

    public function view(int $id): void
    {
        $this->viewingId = $id;
    }

    public function closeDetail(): void
    {
        $this->viewingId = null;
    }

    public function resend(int $id, DispatchContactMessageAction $dispatch): void
    {
        $this->authorize('communications.send.email'); // baseline; tightened per channel below
        $msg = ContactMessage::with(['template', 'messageable', 'related'])->find($id);
        if (!$msg || !$msg->template || !$msg->messageable) {
            session()->flash('log_error', 'Cannot resend — original message data is missing.');
            return;
        }

        $this->authorize($msg->channel === MessageChannel::EMAIL
            ? 'communications.send.email'
            : 'communications.send.sms');

        $dispatch(new DispatchRequest(
            template: $msg->template,
            messageable: $msg->messageable,
            related: $msg->related,
            user: auth()->user(),
            senderEmailId: $msg->sender_email_id,
            smsSenderId: $msg->sms_sender_id,
            origin: MessageOrigin::REPLAY,
            originDetail: ['replayed_from' => $msg->id],
        ));

        session()->flash('log_success', 'Message re-queued for sending.');
    }

    public function render()
    {
        $base = $this->baseQuery();

        $stats = $this->computeStats($this->baseQuery());

        $messages = $base
            ->with(['template', 'creator', 'messageable', 'senderEmail', 'smsSender'])
            ->orderByDesc('created_at')
            ->paginate($this->perPage);

        $detail = $this->viewingId
            ? ContactMessage::with(['template', 'creator', 'messageable', 'related',
                                    'senderEmail', 'smsSender', 'events', 'attachments'])
                ->find($this->viewingId)
            : null;

        return view('components.contact.admin.communication-log-index', [
            'messages' => $messages,
            'detail' => $detail,
            'stats' => $stats,
            'templates' => ContactTemplate::orderBy('name')->get(['id', 'name']),
            'emailSenders' => SolicitorEmailSender::orderBy('display_name')->get(['id', 'display_name', 'email_address']),
            'smsSenders' => SolicitorSmsSender::orderBy('label')->get(['id', 'label', 'value']),
            'origins' => MessageOrigin::cases(),
            'statuses' => MessageStatus::cases(),
        ]);
    }

    /**
     * Returns a fresh query honouring all current filters.  Used both for the
     * paginated list and for the stats panel so they always agree.
     */
    private function baseQuery(): Builder
    {
        $query = ContactMessage::query();

        // Restrict to created_by = current user when user only has "view own" perm.
        if (!auth()->user()?->can('communications.logs.view.all')) {
            $query->where('created_by', auth()->id());
        }

        return $query
            ->when($this->channel, fn ($q) => $q->where('channel', $this->channel))
            ->when($this->status, fn ($q) => $q->where('status', $this->status))
            ->when($this->templateId, fn ($q) => $q->where('template_id', $this->templateId))
            ->when($this->senderEmailId, fn ($q) => $q->where('sender_email_id', $this->senderEmailId))
            ->when($this->smsSenderId, fn ($q) => $q->where('sms_sender_id', $this->smsSenderId))
            ->when($this->origin, fn ($q) => $q->where('origin_type', $this->origin))
            ->when($this->dateFrom, fn ($q) => $q->where('created_at', '>=', $this->dateFrom))
            ->when($this->dateTo, fn ($q) => $q->where('created_at', '<=', $this->dateTo . ' 23:59:59'))
            ->when($this->search, fn ($q) => $q->where(function ($w) {
                $w->where('subject', 'like', "%{$this->search}%")
                  ->orWhere('recipient_email', 'like', "%{$this->search}%")
                  ->orWhere('recipient_phone', 'like', "%{$this->search}%")
                  ->orWhere('recipient_name', 'like', "%{$this->search}%")
                  ->orWhere('provider_message_id', 'like', "%{$this->search}%")
                  ->orWhere('body_text', 'like', "%{$this->search}%");
            }));
    }

    private function computeStats(Builder $query): array
    {
        $rows = (clone $query)
            ->selectRaw('channel, status, count(*) as c')
            ->groupBy('channel', 'status')
            ->get();

        $byStatus = [];
        $byChannel = ['email' => 0, 'sms' => 0];
        $total = 0;
        foreach ($rows as $r) {
            $key = $r->status instanceof MessageStatus ? $r->status->value : (string) $r->status;
            $byStatus[$key] = ($byStatus[$key] ?? 0) + (int) $r->c;
            $byChannel[$r->channel instanceof MessageChannel ? $r->channel->value : (string) $r->channel]
                = ($byChannel[$r->channel instanceof MessageChannel ? $r->channel->value : (string) $r->channel] ?? 0)
                + (int) $r->c;
            $total += (int) $r->c;
        }

        $sent = ($byStatus['sent'] ?? 0)
            + ($byStatus['delivered'] ?? 0)
            + ($byStatus['opened'] ?? 0)
            + ($byStatus['clicked'] ?? 0);
        $delivered = ($byStatus['delivered'] ?? 0)
            + ($byStatus['opened'] ?? 0)
            + ($byStatus['clicked'] ?? 0);
        $opened = ($byStatus['opened'] ?? 0)
            + ($byStatus['clicked'] ?? 0);
        $clicked = $byStatus['clicked'] ?? 0;
        $bounced = ($byStatus['bounced'] ?? 0)
            + ($byStatus['dropped'] ?? 0);
        $failed = ($byStatus['failed'] ?? 0)
            + ($byStatus['undelivered'] ?? 0);
        $suppressed = $byStatus['suppressed'] ?? 0;
        $queued = ($byStatus['queued'] ?? 0)
            + ($byStatus['sending'] ?? 0);

        $rate = fn (int $num, int $den) => $den > 0 ? round(($num / $den) * 100, 1) : 0;

        return [
            'total' => $total,
            'by_channel' => $byChannel,
            'queued' => $queued,
            'sent' => $sent,
            'delivered' => $delivered,
            'opened' => $opened,
            'clicked' => $clicked,
            'bounced' => $bounced,
            'failed' => $failed,
            'suppressed' => $suppressed,
            'delivery_rate' => $rate($delivered, $sent),
            'open_rate' => $rate($opened, $delivered),
            'click_rate' => $rate($clicked, $delivered),
            'bounce_rate' => $rate($bounced, $sent + $bounced),
        ];
    }
}
