<?php

declare(strict_types=1);

namespace App\Filament\Admin\Pages;

use App\Services\Agent\AgentClient;
use BackedEnum;
use Filament\Actions\Action;
use Filament\Actions\Concerns\InteractsWithActions;
use Filament\Actions\Contracts\HasActions;
use Filament\Notifications\Notification;
use Filament\Pages\Page;
use Filament\Support\Icons\Heroicon;
use Filament\Tables\Columns\TextColumn;
use Filament\Tables\Concerns\InteractsWithTable;
use Filament\Tables\Contracts\HasTable;
use Filament\Tables\Table;
use Illuminate\Contracts\Support\Htmlable;
use Illuminate\Pagination\LengthAwarePaginator;
use Illuminate\Support\Str;

class EmailLogs extends Page implements HasActions, HasTable
{
    use InteractsWithActions;
    use InteractsWithTable;

    protected static string|BackedEnum|null $navigationIcon = Heroicon::OutlinedInbox;

    protected static ?int $navigationSort = 14;

    protected static ?string $slug = 'email-logs';

    protected string $view = 'filament.admin.pages.email-logs';

    public string $viewMode = 'logs';

    public array $logs = [];

    public array $queueItems = [];

    protected ?AgentClient $agent = null;

    protected bool $logsLoaded = false;

    protected bool $queueLoaded = false;

    public function getTitle(): string|Htmlable
    {
        return __('Email Logs');
    }

    public static function getNavigationLabel(): string
    {
        return __('Email Logs');
    }

    public function mount(): void
    {
        $this->loadLogs(false);
    }

    protected function getAgent(): AgentClient
    {
        return $this->agent ??= new AgentClient;
    }

    public function loadLogs(bool $refreshTable = true): void
    {
        try {
            $result = $this->getAgent()->send('email.get_logs', [
                'limit' => 200,
            ]);

            $this->logs = $result['logs'] ?? [];
            $this->logsLoaded = true;
        } catch (\Exception $e) {
            $this->logs = [];
            $this->logsLoaded = true;

            Notification::make()
                ->title(__('Failed to load email logs'))
                ->body($e->getMessage())
                ->danger()
                ->send();
        }

        if ($refreshTable) {
            $this->resetTable();
        }
    }

    public function loadQueue(bool $refreshTable = true): void
    {
        try {
            $result = $this->getAgent()->send('mail.queue_list');
            $this->queueItems = $result['queue'] ?? [];
            $this->queueLoaded = true;
        } catch (\Exception $e) {
            $this->queueItems = [];
            $this->queueLoaded = true;
            Notification::make()
                ->title(__('Failed to load mail queue'))
                ->body($e->getMessage())
                ->danger()
                ->send();
        }

        if ($refreshTable) {
            $this->resetTable();
        }
    }

    public function setViewMode(string $mode): void
    {
        $mode = in_array($mode, ['logs', 'queue'], true) ? $mode : 'logs';
        if ($this->viewMode === $mode) {
            return;
        }

        $this->viewMode = $mode;

        if ($mode === 'queue') {
            $this->loadQueue(false);
        } else {
            $this->loadLogs(false);
        }

        $this->resetTable();
    }

    public function table(Table $table): Table
    {
        return $table
            ->paginated([25, 50, 100])
            ->defaultPaginationPageOption(25)
            ->records(function (?array $filters, ?string $search, int|string $page, int|string $recordsPerPage, ?string $sortColumn, ?string $sortDirection) {
                if ($this->viewMode === 'queue') {
                    if (! $this->queueLoaded) {
                        $this->loadQueue(false);
                    }
                    $records = $this->queueItems;
                } else {
                    if (! $this->logsLoaded) {
                        $this->loadLogs(false);
                    }
                    $records = $this->logs;
                }

                $records = $this->filterRecords($records, $search);
                $records = $this->sortRecords($records, $sortColumn, $sortDirection);

                return $this->paginateRecords($records, $page, $recordsPerPage);
            })
            ->columns($this->viewMode === 'queue' ? $this->getQueueColumns() : $this->getLogColumns())
            ->recordActions($this->viewMode === 'queue' ? $this->getQueueActions() : [])
            ->emptyStateHeading($this->viewMode === 'queue' ? __('Mail queue is empty') : __('No email logs found'))
            ->emptyStateDescription($this->viewMode === 'queue' ? __('No deferred messages found.') : __('Mail logs are empty or unavailable.'))
            ->headerActions([
                Action::make('viewLogs')
                    ->label(__('Logs'))
                    ->color($this->viewMode === 'logs' ? 'primary' : 'gray')
                    ->action(fn () => $this->setViewMode('logs')),
                Action::make('viewQueue')
                    ->label(__('Queue'))
                    ->color($this->viewMode === 'queue' ? 'primary' : 'gray')
                    ->action(fn () => $this->setViewMode('queue')),
                Action::make('refresh')
                    ->label(__('Refresh'))
                    ->icon('heroicon-o-arrow-path')
                    ->action(function (): void {
                        if ($this->viewMode === 'queue') {
                            $this->loadQueue();
                        } else {
                            $this->loadLogs();
                        }
                    }),
            ]);
    }

    protected function getLogColumns(): array
    {
        return [
            TextColumn::make('timestamp')
                ->label(__('Time'))
                ->formatStateUsing(function (array $record): string {
                    $timestamp = (int) ($record['timestamp'] ?? 0);

                    return $timestamp > 0 ? date('Y-m-d H:i:s', $timestamp) : '';
                })
                ->sortable(),
            TextColumn::make('queue_id')
                ->label(__('Queue ID'))
                ->fontFamily('mono')
                ->copyable()
                ->toggleable(),
            TextColumn::make('component')
                ->label(__('Component'))
                ->toggleable(isToggledHiddenByDefault: true),
            TextColumn::make('from')
                ->label(__('From'))
                ->wrap()
                ->searchable(),
            TextColumn::make('to')
                ->label(__('To'))
                ->wrap()
                ->searchable(),
            TextColumn::make('status')
                ->label(__('Status'))
                ->badge()
                ->formatStateUsing(fn (array $record): string => (string) ($record['status'] ?? 'unknown')),
            TextColumn::make('relay')
                ->label(__('Relay'))
                ->toggleable(),
            TextColumn::make('message')
                ->label(__('Details'))
                ->wrap()
                ->limit(80)
                ->toggleable(isToggledHiddenByDefault: true),
        ];
    }

    protected function getQueueColumns(): array
    {
        return [
            TextColumn::make('id')
                ->label(__('Queue ID'))
                ->fontFamily('mono')
                ->copyable(),
            TextColumn::make('arrival')
                ->label(__('Arrival')),
            TextColumn::make('sender')
                ->label(__('Sender'))
                ->wrap()
                ->searchable(),
            TextColumn::make('recipients')
                ->label(__('Recipients'))
                ->formatStateUsing(function ($state): string {
                    if (is_array($state)) {
                        $recipients = $state;
                    } elseif ($state === null || $state === '') {
                        $recipients = [];
                    } else {
                        $recipients = [(string) $state];
                    }

                    $recipients = array_values(array_filter(array_map(function ($recipient): ?string {
                        if (is_array($recipient)) {
                            return (string) ($recipient['address']
                                ?? $recipient['recipient']
                                ?? $recipient['email']
                                ?? $recipient[0]
                                ?? '');
                        }

                        return $recipient === null ? null : (string) $recipient;
                    }, $recipients), static fn (?string $value): bool => $value !== null && $value !== ''));

                    if (empty($recipients)) {
                        return __('Unknown');
                    }

                    $first = $recipients[0] ?? '';
                    $count = count($recipients);

                    return $count > 1 ? $first.' +'.($count - 1) : $first;
                })
                ->wrap(),
            TextColumn::make('size')
                ->label(__('Size'))
                ->formatStateUsing(fn ($state): string => is_scalar($state) ? (string) $state : ''),
            TextColumn::make('status')
                ->label(__('Status'))
                ->wrap(),
        ];
    }

    protected function getQueueActions(): array
    {
        return [
            Action::make('retry')
                ->label(__('Retry'))
                ->icon('heroicon-o-arrow-path')
                ->color('info')
                ->action(function (array $record): void {
                    try {
                        $result = $this->getAgent()->send('mail.queue_retry', ['id' => $record['id'] ?? '']);
                        if ($result['success'] ?? false) {
                            Notification::make()->title(__('Message retried'))->success()->send();
                            $this->loadQueue();
                        } else {
                            throw new \Exception($result['error'] ?? __('Failed to retry message'));
                        }
                    } catch (\Exception $e) {
                        Notification::make()->title(__('Retry failed'))->body($e->getMessage())->danger()->send();
                    }
                }),
            Action::make('delete')
                ->label(__('Delete'))
                ->icon('heroicon-o-trash')
                ->color('danger')
                ->requiresConfirmation()
                ->action(function (array $record): void {
                    try {
                        $result = $this->getAgent()->send('mail.queue_delete', ['id' => $record['id'] ?? '']);
                        if ($result['success'] ?? false) {
                            Notification::make()->title(__('Message deleted'))->success()->send();
                            $this->loadQueue();
                        } else {
                            throw new \Exception($result['error'] ?? __('Failed to delete message'));
                        }
                    } catch (\Exception $e) {
                        Notification::make()->title(__('Delete failed'))->body($e->getMessage())->danger()->send();
                    }
                }),
        ];
    }

    protected function filterRecords(array $records, ?string $search): array
    {
        $search = trim((string) $search);
        if ($search === '') {
            return $records;
        }

        $search = Str::lower($search);

        return array_values(array_filter($records, function (array $record) use ($search): bool {
            if ($this->viewMode === 'queue') {
                $recipients = $record['recipients'] ?? [];
                $haystack = implode(' ', array_filter([
                    (string) ($record['id'] ?? ''),
                    (string) ($record['sender'] ?? ''),
                    implode(' ', $recipients),
                    (string) ($record['status'] ?? ''),
                ]));
            } else {
                $haystack = implode(' ', array_filter([
                    (string) ($record['queue_id'] ?? ''),
                    (string) ($record['from'] ?? ''),
                    (string) ($record['to'] ?? ''),
                    (string) ($record['status'] ?? ''),
                    (string) ($record['message'] ?? ''),
                    (string) ($record['component'] ?? ''),
                ]));
            }

            return str_contains(Str::lower($haystack), $search);
        }));
    }

    protected function sortRecords(array $records, ?string $sortColumn, ?string $sortDirection): array
    {
        $direction = $sortDirection === 'asc' ? 'asc' : 'desc';

        if (! $sortColumn) {
            return $records;
        }

        usort($records, function (array $a, array $b) use ($sortColumn, $direction): int {
            $aValue = $a[$sortColumn] ?? null;
            $bValue = $b[$sortColumn] ?? null;

            if (is_numeric($aValue) && is_numeric($bValue)) {
                $result = (float) $aValue <=> (float) $bValue;
            } else {
                $result = strcmp((string) $aValue, (string) $bValue);
            }

            return $direction === 'asc' ? $result : -$result;
        });

        return $records;
    }

    protected function paginateRecords(array $records, int|string $page, int|string $recordsPerPage): LengthAwarePaginator
    {
        $page = max(1, (int) $page);
        $perPage = max(1, (int) $recordsPerPage);

        $total = count($records);
        $items = array_slice($records, ($page - 1) * $perPage, $perPage);

        return new LengthAwarePaginator(
            $items,
            $total,
            $perPage,
            $page,
            [
                'path' => request()->url(),
                'pageName' => $this->getTablePaginationPageName(),
            ],
        );
    }
}
