<?php

declare(strict_types=1);

namespace App\Filament\Jabali\Pages;

use BackedEnum;
use Filament\Actions\Action;
use Filament\Actions\Concerns\InteractsWithActions;
use Filament\Actions\Contracts\HasActions;
use Filament\Forms\Components\Select;
use Filament\Forms\Components\Textarea;
use Filament\Forms\Components\TextInput;
use Filament\Forms\Concerns\InteractsWithForms;
use Filament\Forms\Contracts\HasForms;
use Filament\Notifications\Notification;
use Filament\Pages\Page;
use Filament\Support\Enums\FontWeight;
use Filament\Tables\Columns\TextColumn;
use Filament\Tables\Concerns\InteractsWithTable;
use Filament\Tables\Contracts\HasTable;
use Filament\Tables\Table;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Support\Facades\Auth;

class SshKeys extends Page implements HasActions, HasForms, HasTable
{
    use InteractsWithActions;
    use InteractsWithForms;
    use InteractsWithTable;

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

    protected static ?int $navigationSort = 7;

    protected string $view = 'filament.jabali.pages.ssh-keys';

    public static function getNavigationLabel(): string
    {
        return __('SSH & SFTP');
    }

    public function getTitle(): string
    {
        return __('SSH & SFTP Access');
    }

    public array $sshKeys = [];

    public string $sshHost = '';

    public string $sshPort = '22';

    public string $sshUsername = '';

    public string $sshCommand = '';

    public ?array $generatedKey = null;

    public bool $shellEnabled = false;

    public function mount(): void
    {
        $this->loadSshKeys();
        $this->loadShellStatus();
        $this->sshHost = request()->getHost();
        $this->sshPort = '22';
        $this->sshUsername = $this->getUsername();
        $this->sshCommand = 'ssh '.$this->sshUsername.'@'.$this->sshHost;
    }

    protected function loadShellStatus(): void
    {
        try {
            $result = $this->getAgent()->send('ssh.shell_status', ['username' => $this->getUsername()]);
            $this->shellEnabled = $result['shell_enabled'] ?? false;
        } catch (\Exception $e) {
            $this->shellEnabled = false;
        }
    }

    public function toggleShellAccess(): void
    {
        try {
            $command = $this->shellEnabled ? 'ssh.disable_shell' : 'ssh.enable_shell';
            $result = $this->getAgent()->send($command, ['username' => $this->getUsername()]);

            if ($result['success'] ?? false) {
                $this->shellEnabled = ! $this->shellEnabled;
                Notification::make()
                    ->title($this->shellEnabled ? __('SSH Shell Enabled') : __('SSH Shell Disabled'))
                    ->body($this->shellEnabled
                        ? __('You now have jailed shell access with wp-cli support.')
                        : __('Shell access disabled. You can still use SFTP for file transfers.'))
                    ->success()
                    ->send();
            } else {
                throw new \Exception($result['error'] ?? __('Failed to toggle shell access'));
            }
        } catch (\Exception $e) {
            Notification::make()
                ->title(__('Error'))
                ->body($e->getMessage())
                ->danger()
                ->send();
        }
    }

    protected function getUsername(): string
    {
        $user = Auth::user();

        return $user->system_username ?? $user->username ?? $user->email;
    }

    protected function getAgent()
    {
        return app(\App\Services\Agent\AgentClient::class);
    }

    protected function loadSshKeys(): void
    {
        try {
            $result = $this->getAgent()->send('ssh.list_keys', ['username' => $this->getUsername()]);
            $this->sshKeys = $result['keys'] ?? [];
        } catch (\Exception $e) {
            $this->sshKeys = [];
        }
    }

    public function table(Table $table): Table
    {
        return $table
            ->records(fn () => $this->sshKeys)
            ->columns([
                TextColumn::make('name')
                    ->label(__('Key Name'))
                    ->icon('heroicon-o-key')
                    ->iconColor('success')
                    ->weight(FontWeight::Medium)
                    ->searchable(),
                TextColumn::make('fingerprint')
                    ->label(__('Fingerprint'))
                    ->fontFamily('mono')
                    ->size('sm')
                    ->color('gray')
                    ->limit(40),
                TextColumn::make('added_at')
                    ->label(__('Added'))
                    ->date('M d, Y')
                    ->sortable(),
            ])
            ->recordActions([
                Action::make('delete')
                    ->label(__('Delete'))
                    ->icon('heroicon-o-trash')
                    ->color('danger')
                    ->requiresConfirmation()
                    ->modalHeading(__('Delete SSH Key'))
                    ->modalDescription(__('Are you sure you want to delete this SSH key? You will no longer be able to use it to connect to the server.'))
                    ->modalIcon('heroicon-o-trash')
                    ->modalIconColor('danger')
                    ->modalSubmitActionLabel(__('Delete Key'))
                    ->action(function (array $record): void {
                        try {
                            $result = $this->getAgent()->send('ssh.delete_key', [
                                'username' => $this->getUsername(),
                                'key_id' => $record['id'],
                            ]);

                            if ($result['success'] ?? false) {
                                Notification::make()
                                    ->title(__('SSH Key Deleted'))
                                    ->success()
                                    ->send();
                                $this->loadSshKeys();
                                $this->resetTable();
                            } else {
                                throw new \Exception($result['error'] ?? __('Failed to delete key'));
                            }
                        } catch (\Exception $e) {
                            Notification::make()
                                ->title(__('Error'))
                                ->body($e->getMessage())
                                ->danger()
                                ->send();
                        }
                    }),
            ])
            ->emptyStateHeading(__('No SSH keys added yet'))
            ->emptyStateDescription(__('Click "Generate SSH Key" to create a new key pair, or "Add Existing Key" to add your own public key'))
            ->emptyStateIcon('heroicon-o-key')
            ->striped();
    }

    public function getTableRecordKey(Model|array $record): string
    {
        return is_array($record) ? ($record['id'] ?? $record['name']) : $record->getKey();
    }

    protected function getHeaderActions(): array
    {
        return [
            Action::make('generateKey')
                ->label(__('Generate SSH Key'))
                ->icon('heroicon-o-sparkles')
                ->color('success')
                ->modalHeading(__('Generate SSH Key'))
                ->modalDescription(__('Generate a new SSH key pair for secure server access'))
                ->modalIcon('heroicon-o-sparkles')
                ->modalIconColor('success')
                ->modalSubmitActionLabel(__('Generate Key'))
                ->form([
                    TextInput::make('name')
                        ->label(__('Key Name'))
                        ->placeholder(__('My Generated Key'))
                        ->required()
                        ->maxLength(50)
                        ->helperText(__('A descriptive name to identify this key')),
                    Select::make('type')
                        ->label(__('Key Type'))
                        ->options([
                            'ed25519' => __('ED25519 (Recommended)'),
                            'rsa' => __('RSA 4096-bit'),
                        ])
                        ->default('ed25519')
                        ->required()
                        ->helperText(__('ED25519 is faster and more secure')),
                    TextInput::make('passphrase')
                        ->label(__('Passphrase (Optional)'))
                        ->password()
                        ->helperText(__('Leave empty for no passphrase')),
                ])
                ->action(function (array $data): void {
                    try {
                        $result = $this->generateSshKey($data['name'], $data['type'], $data['passphrase'] ?? '');

                        if ($result) {
                            $this->generatedKey = $result;
                            $this->loadSshKeys();

                            Notification::make()
                                ->title(__('SSH Key Generated!'))
                                ->body(__('Download your private key below. The public key has been added to your account.'))
                                ->success()
                                ->persistent()
                                ->send();
                        }
                    } catch (\Exception $e) {
                        Notification::make()
                            ->title(__('Error'))
                            ->body($e->getMessage())
                            ->danger()
                            ->send();
                    }
                }),
            Action::make('addKey')
                ->label(__('Add Existing Key'))
                ->icon('heroicon-o-plus')
                ->color('primary')
                ->modalHeading(__('Add Existing SSH Key'))
                ->modalDescription(__('Add your existing public key to enable SSH access'))
                ->modalIcon('heroicon-o-key')
                ->modalIconColor('primary')
                ->modalSubmitActionLabel(__('Add Key'))
                ->form([
                    TextInput::make('name')
                        ->label(__('Key Name'))
                        ->placeholder(__('My Laptop'))
                        ->required()
                        ->maxLength(50)
                        ->helperText(__('A descriptive name to identify this key')),
                    Textarea::make('public_key')
                        ->label(__('Public Key'))
                        ->placeholder(__('ssh-rsa AAAAB3... or ssh-ed25519 AAAAC3...'))
                        ->required()
                        ->rows(4)
                        ->helperText(__('Paste your public key (usually from ~/.ssh/id_rsa.pub or ~/.ssh/id_ed25519.pub)')),
                ])
                ->action(function (array $data): void {
                    try {
                        $result = $this->getAgent()->send('ssh.add_key', [
                            'username' => $this->getUsername(),
                            'name' => $data['name'],
                            'public_key' => trim($data['public_key']),
                        ]);

                        if ($result['success'] ?? false) {
                            Notification::make()
                                ->title(__('SSH Key Added'))
                                ->success()
                                ->send();
                            $this->loadSshKeys();
                        } else {
                            throw new \Exception($result['error'] ?? __('Failed to add key'));
                        }
                    } catch (\Exception $e) {
                        Notification::make()
                            ->title(__('Error'))
                            ->body($e->getMessage())
                            ->danger()
                            ->send();
                    }
                }),
        ];
    }

    protected function generateSshKey(string $name, string $type, string $passphrase = ''): array
    {
        $result = $this->getAgent()->send('ssh.generate_key', [
            'name' => $name,
            'type' => $type,
            'passphrase' => $passphrase,
        ]);

        if (! ($result['success'] ?? false)) {
            throw new \Exception($result['error'] ?? __('Failed to generate SSH key'));
        }

        // Add public key to user authorized_keys
        $addResult = $this->getAgent()->send('ssh.add_key', [
            'username' => $this->getUsername(),
            'name' => $name,
            'public_key' => trim($result['public_key']),
        ]);

        if (! ($addResult['success'] ?? false)) {
            throw new \Exception($addResult['error'] ?? __('Failed to add key to server'));
        }

        return [
            'name' => $result['name'],
            'type' => $result['type'],
            'private_key' => $result['private_key'],
            'public_key' => $result['public_key'],
            'ppk_key' => $result['ppk_key'] ?? null,
            'fingerprint' => $result['fingerprint'] ?? '',
        ];
    }

    public function clearGeneratedKey(): void
    {
        $this->generatedKey = null;
    }

    public function downloadPrivateKey(): \Symfony\Component\HttpFoundation\StreamedResponse
    {
        if (! $this->generatedKey) {
            abort(404);
        }

        $key = $this->generatedKey['private_key'];
        $name = preg_replace('/[^a-zA-Z0-9_-]/', '_', $this->generatedKey['name']);

        return response()->streamDownload(function () use ($key) {
            echo $key;
        }, "id_{$this->generatedKey['type']}_{$name}", [
            'Content-Type' => 'application/x-pem-file',
        ]);
    }

    public function downloadPpkKey(): \Symfony\Component\HttpFoundation\StreamedResponse
    {
        if (! $this->generatedKey || ! $this->generatedKey['ppk_key']) {
            abort(404);
        }

        $key = $this->generatedKey['ppk_key'];
        $name = preg_replace('/[^a-zA-Z0-9_-]/', '_', $this->generatedKey['name']);

        return response()->streamDownload(function () use ($key) {
            echo $key;
        }, "{$name}.ppk", [
            'Content-Type' => 'application/x-putty-private-key',
        ]);
    }

    public function downloadFileZillaConfig(): \Symfony\Component\HttpFoundation\StreamedResponse
    {
        $xml = $this->generateFileZillaXml();

        return response()->streamDownload(function () use ($xml) {
            echo $xml;
        }, 'jabali-sftp.xml', [
            'Content-Type' => 'application/xml',
        ]);
    }

    public function downloadWinScpConfig(): \Symfony\Component\HttpFoundation\StreamedResponse
    {
        $ini = $this->generateWinScpIni();

        return response()->streamDownload(function () use ($ini) {
            echo $ini;
        }, 'jabali-sftp.ini', [
            'Content-Type' => 'text/plain',
        ]);
    }

    protected function generateFileZillaXml(): string
    {
        $host = htmlspecialchars($this->sshHost);
        $port = htmlspecialchars($this->sshPort);
        $user = htmlspecialchars($this->sshUsername);

        return <<<XML
<?xml version="1.0" encoding="UTF-8"?>
<FileZilla3 version="3.66.4" platform="*">
    <Servers>
        <Server>
            <Host>{$host}</Host>
            <Port>{$port}</Port>
            <Protocol>1</Protocol>
            <Type>0</Type>
            <User>{$user}</User>
            <Logontype>1</Logontype>
            <EncodingType>Auto</EncodingType>
            <BypassProxy>0</BypassProxy>
            <Name>Jabali - {$host}</Name>
            <RemoteDir>/home/{$user}</RemoteDir>
            <SyncBrowsing>0</SyncBrowsing>
            <DirectoryComparison>0</DirectoryComparison>
        </Server>
    </Servers>
</FileZilla3>
XML;
    }

    protected function generateWinScpIni(): string
    {
        $host = $this->sshHost;
        $port = $this->sshPort;
        $user = $this->sshUsername;
        $sessionName = "Jabali - {$host}";

        return <<<INI
[Sessions\\{$sessionName}]
HostName={$host}
PortNumber={$port}
UserName={$user}
FSProtocol=2
RemoteDirectory=/home/{$user}
UpdateDirectories=1
INI;
    }
}
