<?php

declare(strict_types=1);

namespace App\Filament\Admin\Pages;

use App\Filament\Admin\Widgets\SslStatsOverview;
use App\Models\Domain;
use App\Models\SslCertificate;
use App\Models\User;
use App\Services\Agent\AgentClient;
use BackedEnum;
use Exception;
use Filament\Actions\Action;
use Filament\Forms\Components\Select;
use Filament\Notifications\Notification;
use Filament\Pages\Page;
use Filament\Tables\Columns\TextColumn;
use Filament\Tables\Concerns\InteractsWithTable;
use Filament\Tables\Contracts\HasTable;
use Filament\Tables\Filters\SelectFilter;
use Filament\Tables\Table;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Support\Facades\Artisan;

class SslManager extends Page implements HasTable
{
    use InteractsWithTable;

    protected static string|BackedEnum|null $navigationIcon = 'heroicon-o-shield-check';

    protected static ?int $navigationSort = 8;

    public static function getNavigationLabel(): string
    {
        return __('SSL Manager');
    }

    public function getTitle(): string
    {
        return __('SSL Manager');
    }

    protected string $view = 'filament.admin.pages.ssl-manager';

    public bool $isRunning = false;

    public string $autoSslLog = '';

    public ?string $lastUpdated = null;

    protected ?AgentClient $agent = null;

    protected function getHeaderWidgets(): array
    {
        return [
            SslStatsOverview::class,
        ];
    }

    public function getHeaderWidgetsColumns(): int|array
    {
        return 6;
    }

    public function getAgent(): AgentClient
    {
        if ($this->agent === null) {
            $this->agent = new AgentClient;
        }

        return $this->agent;
    }

    public function mount(): void
    {
        $this->lastUpdated = now()->format('H:i:s');
    }

    public function table(Table $table): Table
    {
        return $table
            ->query(Domain::with(['user', 'sslCertificate']))
            ->columns([
                TextColumn::make('domain')
                    ->label(__('Domain'))
                    ->searchable()
                    ->sortable()
                    ->description(fn (Domain $record) => $record->user?->username ?? __('Unknown')),
                TextColumn::make('sslCertificate.type')
                    ->label(__('Type'))
                    ->badge()
                    ->color('gray')
                    ->formatStateUsing(fn ($state) => $state ? ucfirst(str_replace('_', ' ', $state)) : __('No SSL')),
                TextColumn::make('sslCertificate.status')
                    ->label(__('Status'))
                    ->badge()
                    ->color(fn ($state) => match ($state) {
                        'active' => 'success',
                        'expired' => 'danger',
                        'expiring' => 'warning',
                        'failed' => 'danger',
                        default => 'gray',
                    })
                    ->formatStateUsing(fn ($state) => $state ? ucfirst($state) : __('No Certificate')),
                TextColumn::make('sslCertificate.expires_at')
                    ->label(__('Expires'))
                    ->date('M d, Y')
                    ->description(fn (Domain $record) => $record->sslCertificate?->days_until_expiry !== null
                        ? __(':days days', ['days' => $record->sslCertificate->days_until_expiry])
                        : null)
                    ->color(fn (Domain $record) => match (true) {
                        $record->sslCertificate?->days_until_expiry <= 7 => 'danger',
                        $record->sslCertificate?->days_until_expiry <= 30 => 'warning',
                        default => 'gray',
                    }),
                TextColumn::make('sslCertificate.last_check_at')
                    ->label(__('Last Check'))
                    ->since()
                    ->sortable(),
                TextColumn::make('sslCertificate.last_error')
                    ->label(__('Error'))
                    ->limit(30)
                    ->tooltip(fn ($state) => $state)
                    ->color('danger')
                    ->placeholder('-'),
            ])
            ->filters([
                SelectFilter::make('ssl_status')
                    ->label(__('Status'))
                    ->options([
                        'active' => __('Active'),
                        'no_ssl' => __('No SSL'),
                        'expiring' => __('Expiring Soon'),
                        'expired' => __('Expired'),
                        'failed' => __('Failed'),
                    ])
                    ->query(function (Builder $query, array $data) {
                        if (! $data['value']) {
                            return $query;
                        }

                        return match ($data['value']) {
                            'active' => $query->whereHas('sslCertificate', fn ($q) => $q->where('status', 'active')),
                            'no_ssl' => $query->whereDoesntHave('sslCertificate'),
                            'expiring' => $query->whereHas('sslCertificate', fn ($q) => $q->where('status', 'active')
                                ->where('expires_at', '<=', now()->addDays(30))
                                ->where('expires_at', '>', now())),
                            'expired' => $query->whereHas('sslCertificate', fn ($q) => $q->where('status', 'expired')
                                ->orWhere('expires_at', '<', now())),
                            'failed' => $query->whereHas('sslCertificate', fn ($q) => $q->where('status', 'failed')),
                            default => $query,
                        };
                    }),
                SelectFilter::make('user_id')
                    ->label(__('User'))
                    ->relationship('user', 'username'),
            ])
            ->recordActions([
                Action::make('issue')
                    ->label(__('Issue'))
                    ->icon('heroicon-o-lock-closed')
                    ->color('success')
                    ->visible(fn (Domain $record) => ! $record->sslCertificate || $record->sslCertificate->status === 'failed')
                    ->action(fn (Domain $record) => $this->issueSslForDomain($record->id)),
                Action::make('renew')
                    ->label(__('Renew'))
                    ->icon('heroicon-o-arrow-path')
                    ->color('primary')
                    ->visible(fn (Domain $record) => $record->sslCertificate?->type === 'lets_encrypt' && $record->sslCertificate?->status === 'active')
                    ->action(fn (Domain $record) => $this->renewSslForDomain($record->id)),
                Action::make('check')
                    ->label(__('Check'))
                    ->icon('heroicon-o-magnifying-glass')
                    ->color('gray')
                    ->action(fn (Domain $record) => $this->checkSslForDomain($record->id)),
            ])
            ->heading(__('Domain Certificates'))
            ->poll('30s');
    }

    public function issueSslForDomain(int $domainId): void
    {
        try {
            $domain = Domain::with('user')->findOrFail($domainId);

            $result = $this->getAgent()->sslIssue(
                $domain->domain,
                $domain->user->username,
                $domain->user->email,
                true
            );

            if ($result['success'] ?? false) {
                SslCertificate::updateOrCreate(
                    ['domain_id' => $domain->id],
                    [
                        'type' => 'lets_encrypt',
                        'status' => 'active',
                        'issuer' => "Let's Encrypt",
                        'certificate' => $result['certificate'] ?? null,
                        'issued_at' => now(),
                        'expires_at' => isset($result['valid_to']) ? \Carbon\Carbon::parse($result['valid_to']) : now()->addMonths(3),
                        'last_check_at' => now(),
                        'last_error' => null,
                        'renewal_attempts' => 0,
                        'auto_renew' => true,
                    ]
                );

                $domain->update(['ssl_enabled' => true]);

                Notification::make()
                    ->title(__('SSL Certificate Issued'))
                    ->body(__('Certificate issued for :domain', ['domain' => $domain->domain]))
                    ->success()
                    ->send();
            } else {
                SslCertificate::updateOrCreate(
                    ['domain_id' => $domain->id],
                    [
                        'type' => 'lets_encrypt',
                        'status' => 'failed',
                        'last_check_at' => now(),
                        'last_error' => $result['error'] ?? __('Unknown error'),
                    ]
                );

                Notification::make()
                    ->title(__('SSL Certificate Failed'))
                    ->body($result['error'] ?? __('Unknown error'))
                    ->danger()
                    ->send();
            }
        } catch (Exception $e) {
            Notification::make()
                ->title(__('Error'))
                ->body($e->getMessage())
                ->danger()
                ->send();
        }

        $this->lastUpdated = now()->format('H:i:s');
    }

    public function renewSslForDomain(int $domainId): void
    {
        try {
            $domain = Domain::with('user')->findOrFail($domainId);

            $result = $this->getAgent()->sslRenew($domain->domain, $domain->user->username);

            if ($result['success'] ?? false) {
                $ssl = $domain->sslCertificate;
                if ($ssl) {
                    $ssl->update([
                        'status' => 'active',
                        'issued_at' => now(),
                        'expires_at' => isset($result['valid_to']) ? \Carbon\Carbon::parse($result['valid_to']) : now()->addMonths(3),
                        'last_check_at' => now(),
                        'last_error' => null,
                        'renewal_attempts' => 0,
                    ]);
                }

                Notification::make()
                    ->title(__('Certificate Renewed'))
                    ->body(__('SSL certificate renewed for :domain', ['domain' => $domain->domain]))
                    ->success()
                    ->send();
            } else {
                Notification::make()
                    ->title(__('Renewal Failed'))
                    ->body($result['error'] ?? __('Unknown error'))
                    ->danger()
                    ->send();
            }
        } catch (Exception $e) {
            Notification::make()
                ->title(__('Error'))
                ->body($e->getMessage())
                ->danger()
                ->send();
        }

        $this->lastUpdated = now()->format('H:i:s');
    }

    public function checkSslForDomain(int $domainId): void
    {
        try {
            $domain = Domain::with('user')->findOrFail($domainId);

            $result = $this->getAgent()->sslCheck($domain->domain, $domain->user->username);

            if ($result['success'] ?? false) {
                $sslData = $result['ssl'] ?? [];

                if ($sslData['has_ssl'] ?? false) {
                    SslCertificate::updateOrCreate(
                        ['domain_id' => $domain->id],
                        [
                            'type' => $sslData['type'] ?? 'custom',
                            'status' => ($sslData['is_expired'] ?? false) ? 'expired' : 'active',
                            'issuer' => $sslData['issuer'],
                            'certificate' => $sslData['certificate'] ?? null,
                            'issued_at' => isset($sslData['valid_from']) ? \Carbon\Carbon::parse($sslData['valid_from']) : null,
                            'expires_at' => isset($sslData['valid_to']) ? \Carbon\Carbon::parse($sslData['valid_to']) : null,
                            'last_check_at' => now(),
                        ]
                    );
                    $domain->update(['ssl_enabled' => true]);
                }

                Notification::make()
                    ->title(__('Certificate Checked'))
                    ->body($sslData['has_ssl'] ? __('Found: :issuer', ['issuer' => $sslData['issuer']]) : __('No certificate found'))
                    ->success()
                    ->send();
            } else {
                Notification::make()
                    ->title(__('Check Failed'))
                    ->body($result['error'] ?? __('Unknown error'))
                    ->danger()
                    ->send();
            }
        } catch (Exception $e) {
            Notification::make()
                ->title(__('Error'))
                ->body($e->getMessage())
                ->danger()
                ->send();
        }

        $this->lastUpdated = now()->format('H:i:s');
    }

    public function runAutoSsl(?string $domain = null): void
    {
        $this->isRunning = true;
        $this->autoSslLog = '';

        try {
            // Ensure log directory exists with proper permissions
            $logDir = storage_path('logs/ssl');
            if (! is_dir($logDir)) {
                @mkdir($logDir, 0775, true);
            }

            $params = [];
            if ($domain) {
                $params['--domain'] = $domain;
            }

            Artisan::call('jabali:ssl-check', $params);
            $this->autoSslLog = Artisan::output();

            Notification::make()
                ->title(__('SSL Check Complete'))
                ->body($domain
                    ? __('SSL check completed for :domain', ['domain' => $domain])
                    : __('SSL certificate check completed for all domains'))
                ->success()
                ->send();
        } catch (Exception $e) {
            $this->autoSslLog = __('Error: :message', ['message' => $e->getMessage()]);

            Notification::make()
                ->title(__('SSL Check Failed'))
                ->body($e->getMessage())
                ->danger()
                ->send();
        }

        $this->isRunning = false;
        $this->lastUpdated = now()->format('H:i:s');
    }

    public function runSslCheckForUser(int $userId): void
    {
        $this->isRunning = true;
        $this->autoSslLog = '';

        try {
            $user = User::findOrFail($userId);
            $domains = Domain::where('user_id', $userId)->pluck('domain')->toArray();

            if (empty($domains)) {
                $this->autoSslLog = __('No domains found for user :user', ['user' => $user->username]);
                Notification::make()
                    ->title(__('No Domains'))
                    ->body(__('User :user has no domains', ['user' => $user->username]))
                    ->warning()
                    ->send();
                $this->isRunning = false;

                return;
            }

            $this->autoSslLog = __('Checking SSL for :count domains of user :user', ['count' => count($domains), 'user' => $user->username])."\n\n";

            foreach ($domains as $domain) {
                Artisan::call('jabali:ssl-check', ['--domain' => $domain]);
                $this->autoSslLog .= Artisan::output()."\n";
            }

            Notification::make()
                ->title(__('SSL Check Complete'))
                ->body(__('SSL check completed for :count domains of user :user', ['count' => count($domains), 'user' => $user->username]))
                ->success()
                ->send();
        } catch (Exception $e) {
            $this->autoSslLog = __('Error: :message', ['message' => $e->getMessage()]);

            Notification::make()
                ->title(__('SSL Check Failed'))
                ->body($e->getMessage())
                ->danger()
                ->send();
        }

        $this->isRunning = false;
        $this->lastUpdated = now()->format('H:i:s');
    }

    public function issueAllPending(): void
    {
        $domainsWithoutSsl = Domain::whereDoesntHave('sslCertificate')
            ->orWhereHas('sslCertificate', function ($q) {
                $q->where('status', 'failed');
            })
            ->with('user')
            ->get();

        $issued = 0;
        $failed = 0;

        foreach ($domainsWithoutSsl as $domain) {
            try {
                $result = $this->getAgent()->sslIssue(
                    $domain->domain,
                    $domain->user->username,
                    $domain->user->email,
                    true
                );

                if ($result['success'] ?? false) {
                    SslCertificate::updateOrCreate(
                        ['domain_id' => $domain->id],
                        [
                            'type' => 'lets_encrypt',
                            'status' => 'active',
                            'issuer' => "Let's Encrypt",
                            'certificate' => $result['certificate'] ?? null,
                            'issued_at' => now(),
                            'expires_at' => isset($result['valid_to']) ? \Carbon\Carbon::parse($result['valid_to']) : now()->addMonths(3),
                            'last_check_at' => now(),
                            'last_error' => null,
                            'renewal_attempts' => 0,
                            'auto_renew' => true,
                        ]
                    );
                    $domain->update(['ssl_enabled' => true]);
                    $issued++;
                } else {
                    SslCertificate::updateOrCreate(
                        ['domain_id' => $domain->id],
                        [
                            'type' => 'lets_encrypt',
                            'status' => 'failed',
                            'last_check_at' => now(),
                            'last_error' => $result['error'] ?? __('Unknown error'),
                        ]
                    );
                    $failed++;
                }
            } catch (Exception $e) {
                $failed++;
            }
        }

        Notification::make()
            ->title(__('Bulk SSL Issuance Complete'))
            ->body(__('Issued: :issued, Failed: :failed', ['issued' => $issued, 'failed' => $failed]))
            ->success()
            ->send();

        $this->lastUpdated = now()->format('H:i:s');
    }

    public function getLetsEncryptLog(): string
    {
        $logFiles = [
            '/var/log/letsencrypt/letsencrypt.log',
            '/var/log/certbot/letsencrypt.log',
            '/var/log/certbot.log',
        ];

        $logContent = '';
        $foundFile = null;

        foreach ($logFiles as $logFile) {
            if (file_exists($logFile)) {
                $foundFile = $logFile;
                $lines = file($logFile);
                $lastLines = array_slice($lines, -500);
                $logContent .= "=== {$logFile} ===\n".implode('', $lastLines);
                break;
            }
        }

        if (! $foundFile) {
            $certbotLogs = glob('/var/log/letsencrypt/*.log');
            if (! empty($certbotLogs)) {
                $foundFile = end($certbotLogs);
                $lines = file($foundFile);
                $lastLines = array_slice($lines, -500);
                $logContent = "=== {$foundFile} ===\n".implode('', $lastLines);
            }
        }

        if (! $foundFile) {
            return __("No Let's Encrypt/Certbot log files found.")."\n\n".__('Searched locations:')."\n".implode("\n", $logFiles)."\n/var/log/letsencrypt/*.log";
        }

        return $logContent;
    }

    protected function getHeaderActions(): array
    {
        return [
            Action::make('runAutoSsl')
                ->label(__('Run SSL Check'))
                ->icon('heroicon-o-play')
                ->color('success')
                ->modalHeading(__('Run SSL Check'))
                ->modalDescription(__('Check SSL certificates and automatically issue/renew them.'))
                ->modalWidth('md')
                ->form([
                    Select::make('scope')
                        ->label(__('Scope'))
                        ->options([
                            'all' => __('All Domains'),
                            'user' => __('Specific User'),
                            'domain' => __('Specific Domain'),
                        ])
                        ->default('all')
                        ->live()
                        ->required(),
                    Select::make('user_id')
                        ->label(__('User'))
                        ->options(fn () => User::pluck('username', 'id')->toArray())
                        ->searchable()
                        ->visible(fn ($get) => $get('scope') === 'user')
                        ->required(fn ($get) => $get('scope') === 'user'),
                    Select::make('domain')
                        ->label(__('Domain'))
                        ->options(fn () => Domain::pluck('domain', 'domain')->toArray())
                        ->searchable()
                        ->visible(fn ($get) => $get('scope') === 'domain')
                        ->required(fn ($get) => $get('scope') === 'domain'),
                ])
                ->action(function (array $data): void {
                    match ($data['scope']) {
                        'user' => $this->runSslCheckForUser((int) $data['user_id']),
                        'domain' => $this->runAutoSsl($data['domain']),
                        default => $this->runAutoSsl(),
                    };
                }),
            Action::make('issueAllPending')
                ->label(__('Issue All Pending'))
                ->icon('heroicon-o-shield-check')
                ->color('primary')
                ->requiresConfirmation()
                ->modalHeading(__('Issue SSL for All Pending Domains'))
                ->modalDescription(__('This will attempt to issue SSL certificates for all domains without active certificates. This may take a while.'))
                ->action(fn () => $this->issueAllPending()),
            Action::make('viewLog')
                ->label(__('View Log'))
                ->icon('heroicon-o-document-text')
                ->color('gray')
                ->modalHeading(__("Let's Encrypt Log"))
                ->modalWidth('4xl')
                ->modalContent(fn () => view('filament.admin.pages.ssl-log-modal', ['log' => $this->getLetsEncryptLog()]))
                ->modalSubmitAction(false)
                ->modalCancelActionLabel(__('Close')),
        ];
    }
}
