<?php

declare(strict_types=1);

namespace App\Console\Commands\Jabali;

use App\Models\Domain;
use App\Models\ServerImport;
use App\Models\ServerImportAccount;
use App\Models\User;
use App\Services\Agent\AgentClient;
use Exception;
use Illuminate\Console\Command;
use Illuminate\Support\Facades\Hash;
use Illuminate\Support\Facades\Storage;
use Illuminate\Support\Str;

class ImportProcessCommand extends Command
{
    protected $signature = 'import:process {import_id : The server import ID to process}';
    protected $description = 'Process a server import job (cPanel/DirectAdmin migration)';

    private ?AgentClient $agent = null;

    public function handle(): int
    {
        $importId = (int) $this->argument('import_id');

        $import = ServerImport::with('accounts')->find($importId);
        if (!$import) {
            $this->error("Import not found: $importId");
            return 1;
        }

        $this->info("Processing import: {$import->name} (ID: {$import->id})");

        $selectedAccountIds = $import->selected_accounts ?? [];
        $options = $import->import_options ?? [];

        if (empty($selectedAccountIds)) {
            $import->update([
                'status' => 'failed',
                'current_task' => null,
            ]);
            $import->addError('No accounts selected for import');
            return 1;
        }

        $accounts = ServerImportAccount::whereIn('id', $selectedAccountIds)
            ->where('server_import_id', $import->id)
            ->get();

        $totalAccounts = $accounts->count();
        $completedAccounts = 0;

        $import->addLog("Starting import of $totalAccounts account(s)");

        foreach ($accounts as $account) {
            try {
                $this->processAccount($import, $account, $options);
                $completedAccounts++;

                $progress = (int) (($completedAccounts / $totalAccounts) * 100);
                $import->update(['progress' => $progress]);
            } catch (Exception $e) {
                $account->update([
                    'status' => 'failed',
                    'error' => $e->getMessage(),
                ]);
                $account->addLog("Import failed: " . $e->getMessage());
                $import->addError("Account {$account->source_username}: " . $e->getMessage());
            }
        }

        $failedCount = $accounts->where('status', 'failed')->count();

        if ($failedCount === $totalAccounts) {
            $import->update([
                'status' => 'failed',
                'current_task' => null,
                'completed_at' => now(),
                'progress' => 100,
            ]);
        } elseif ($failedCount > 0) {
            $import->update([
                'status' => 'completed',
                'current_task' => null,
                'completed_at' => now(),
                'progress' => 100,
            ]);
            $import->addLog("Completed with $failedCount failed account(s)");
        } else {
            $import->update([
                'status' => 'completed',
                'current_task' => null,
                'completed_at' => now(),
                'progress' => 100,
            ]);
            $import->addLog("All accounts imported successfully");
        }

        $this->info("Import completed. Success: " . ($totalAccounts - $failedCount) . ", Failed: $failedCount");

        return 0;
    }

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

    private function processAccount(ServerImport $import, ServerImportAccount $account, array $options): void
    {
        $account->update([
            'status' => 'importing',
            'progress' => 0,
            'current_task' => 'Starting import...',
        ]);
        $account->addLog("Starting import for account: {$account->source_username}");

        $import->update(['current_task' => "Importing account: {$account->source_username}"]);

        // Step 1: Create user
        $account->update(['current_task' => 'Creating user...', 'progress' => 10]);
        $user = $this->createUser($account);
        $account->addLog("Created user: {$user->email}");

        // Step 2: Create domains
        if ($account->main_domain) {
            $account->update(['current_task' => 'Creating domains...', 'progress' => 20]);
            $this->createDomains($account, $user);
            $account->addLog("Created domains");
        }

        // Step 3: Import files
        if ($options['files'] ?? true) {
            $account->update(['current_task' => 'Importing files...', 'progress' => 40]);
            $this->importFiles($import, $account, $user);
            $account->addLog("Files imported");
        }

        // Step 4: Import databases
        if (($options['databases'] ?? true) && !empty($account->databases)) {
            $account->update(['current_task' => 'Importing databases...', 'progress' => 60]);
            $this->importDatabases($import, $account, $user);
            $account->addLog("Databases imported");
        }

        // Step 5: Import emails
        if (($options['emails'] ?? true) && !empty($account->email_accounts)) {
            $account->update(['current_task' => 'Importing email accounts...', 'progress' => 80]);
            $this->importEmails($import, $account, $user);
            $account->addLog("Email accounts imported");
        }

        $account->update([
            'status' => 'completed',
            'progress' => 100,
            'current_task' => null,
        ]);
        $account->addLog("Import completed successfully");
    }

    private function createUser(ServerImportAccount $account): User
    {
        // Check if user already exists with this username
        $existingUser = User::where('username', $account->target_username)->first();
        if ($existingUser) {
            $account->addLog("User already exists: {$account->target_username}");
            return $existingUser;
        }

        // Generate a temporary password
        $password = Str::random(16);

        // Create user via agent
        $result = $this->getAgent()->createUser($account->target_username, $password);

        if (!($result['success'] ?? false)) {
            throw new Exception("Failed to create system user: " . ($result['error'] ?? 'Unknown error'));
        }

        // Create user in database
        $user = User::create([
            'name' => $account->target_username,
            'email' => $account->email ?: "{$account->target_username}@localhost",
            'username' => $account->target_username,
            'password' => Hash::make($password),
        ]);

        $account->addLog("Created user with temporary password. User should reset password.");

        return $user;
    }

    private function createDomains(ServerImportAccount $account, User $user): void
    {
        // Create main domain
        if ($account->main_domain) {
            $existingDomain = Domain::where('domain', $account->main_domain)->first();
            if (!$existingDomain) {
                $result = $this->getAgent()->domainCreate($user->username, $account->main_domain);

                if ($result['success'] ?? false) {
                    Domain::create([
                        'domain' => $account->main_domain,
                        'user_id' => $user->id,
                        'document_root' => "/home/{$user->username}/domains/{$account->main_domain}/public",
                        'is_active' => true,
                    ]);
                    $account->addLog("Created main domain: {$account->main_domain}");
                } else {
                    $account->addLog("Warning: Failed to create main domain: " . ($result['error'] ?? 'Unknown'));
                }
            } else {
                $account->addLog("Main domain already exists: {$account->main_domain}");
            }
        }

        // Create addon domains
        foreach ($account->addon_domains ?? [] as $domain) {
            $existingDomain = Domain::where('domain', $domain)->first();
            if (!$existingDomain) {
                $result = $this->getAgent()->domainCreate($user->username, $domain);

                if ($result['success'] ?? false) {
                    Domain::create([
                        'domain' => $domain,
                        'user_id' => $user->id,
                        'document_root' => "/home/{$user->username}/domains/{$domain}/public",
                        'is_active' => true,
                    ]);
                    $account->addLog("Created addon domain: {$domain}");
                } else {
                    $account->addLog("Warning: Failed to create addon domain: {$domain}");
                }
            }
        }
    }

    private function importFiles(ServerImport $import, ServerImportAccount $account, User $user): void
    {
        if ($import->import_method !== 'backup_file' || !$import->backup_path) {
            $account->addLog("File import skipped - not a backup file import");
            return;
        }

        $backupPath = Storage::disk('local')->path($import->backup_path);
        if (!file_exists($backupPath)) {
            $account->addLog("Warning: Backup file not found");
            return;
        }

        $extractDir = "/tmp/import_{$import->id}_{$account->id}_" . time();
        if (!mkdir($extractDir, 0755, true)) {
            $account->addLog("Warning: Failed to create extraction directory");
            return;
        }

        try {
            $username = $account->source_username;

            if ($import->source_type === 'cpanel') {
                // Extract home directory from cPanel backup
                $cmd = "tar -xzf " . escapeshellarg($backupPath) . " -C " . escapeshellarg($extractDir) .
                    " --wildcards '*/{$username}/homedir/*' '*/homedir/*' 2>/dev/null";
                exec($cmd, $output, $code);

                // Find extracted files
                $homeDirs = glob("$extractDir/**/homedir", GLOB_ONLYDIR) ?:
                    glob("$extractDir/*/homedir", GLOB_ONLYDIR) ?:
                    glob("$extractDir/homedir", GLOB_ONLYDIR) ?: [];

                foreach ($homeDirs as $homeDir) {
                    // Copy public_html to the domain
                    $publicHtml = "$homeDir/public_html";
                    if (is_dir($publicHtml) && $account->main_domain) {
                        $destDir = "/home/{$user->username}/domains/{$account->main_domain}/public";
                        if (is_dir($destDir)) {
                            exec("cp -r " . escapeshellarg($publicHtml) . "/* " . escapeshellarg($destDir) . "/ 2>&1");
                            exec("chown -R " . escapeshellarg($user->username) . ":" . escapeshellarg($user->username) . " " . escapeshellarg($destDir) . " 2>&1");
                            $account->addLog("Copied public_html to {$account->main_domain}");
                        }
                    }
                }
            } else {
                // Extract from DirectAdmin backup
                $cmd = "tar -xzf " . escapeshellarg($backupPath) . " -C " . escapeshellarg($extractDir) .
                    " --wildcards 'domains/*' 'backup/domains/*' 2>/dev/null";
                exec($cmd, $output, $code);

                // Find domain directories
                $domainDirs = glob("$extractDir/**/domains/*", GLOB_ONLYDIR) ?:
                    glob("$extractDir/domains/*", GLOB_ONLYDIR) ?: [];

                foreach ($domainDirs as $domainDir) {
                    $domain = basename($domainDir);
                    $publicHtml = "$domainDir/public_html";

                    if (is_dir($publicHtml)) {
                        $destDir = "/home/{$user->username}/domains/{$domain}/public";
                        if (is_dir($destDir)) {
                            exec("cp -r " . escapeshellarg($publicHtml) . "/* " . escapeshellarg($destDir) . "/ 2>&1");
                            exec("chown -R " . escapeshellarg($user->username) . ":" . escapeshellarg($user->username) . " " . escapeshellarg($destDir) . " 2>&1");
                            $account->addLog("Copied files for domain: {$domain}");
                        }
                    }
                }
            }
        } finally {
            // Cleanup
            exec("rm -rf " . escapeshellarg($extractDir));
        }
    }

    private function importDatabases(ServerImport $import, ServerImportAccount $account, User $user): void
    {
        if ($import->import_method !== 'backup_file' || !$import->backup_path) {
            $account->addLog("Database import skipped - not a backup file import");
            return;
        }

        $backupPath = Storage::disk('local')->path($import->backup_path);
        if (!file_exists($backupPath)) {
            return;
        }

        $extractDir = "/tmp/import_db_{$import->id}_{$account->id}_" . time();
        if (!mkdir($extractDir, 0755, true)) {
            return;
        }

        try {
            // Extract MySQL dumps
            if ($import->source_type === 'cpanel') {
                $cmd = "tar -xzf " . escapeshellarg($backupPath) . " -C " . escapeshellarg($extractDir) .
                    " --wildcards '*/mysql/*.sql' 'mysql/*.sql' 2>/dev/null";
            } else {
                $cmd = "tar -xzf " . escapeshellarg($backupPath) . " -C " . escapeshellarg($extractDir) .
                    " --wildcards 'backup/databases/*.sql' 'databases/*.sql' 2>/dev/null";
            }
            exec($cmd, $output, $code);

            // Find SQL files
            $sqlFiles = [];
            exec("find " . escapeshellarg($extractDir) . " -name '*.sql' -type f 2>/dev/null", $sqlFiles);

            foreach ($sqlFiles as $sqlFile) {
                $dbName = basename($sqlFile, '.sql');

                // Create database name with user prefix
                $newDbName = substr($user->username . '_' . preg_replace('/^[^_]+_/', '', $dbName), 0, 64);

                // Create database via agent
                $result = $this->getAgent()->mysqlCreateDatabase($user->username, $newDbName);

                if ($result['success'] ?? false) {
                    // Import data
                    $cmd = "mysql " . escapeshellarg($newDbName) . " < " . escapeshellarg($sqlFile) . " 2>&1";
                    exec($cmd, $importOutput, $importCode);

                    if ($importCode === 0) {
                        $account->addLog("Imported database: {$newDbName}");
                    } else {
                        $account->addLog("Warning: Database created but import failed: {$newDbName}");
                    }
                } else {
                    $account->addLog("Warning: Failed to create database: {$newDbName}");
                }
            }
        } finally {
            exec("rm -rf " . escapeshellarg($extractDir));
        }
    }

    private function importEmails(ServerImport $import, ServerImportAccount $account, User $user): void
    {
        // Email import is complex and requires the email system to be configured
        // For now, just log the email accounts that would be created
        foreach ($account->email_accounts ?? [] as $emailAccount) {
            $account->addLog("Email account found (not imported): {$emailAccount}@{$account->main_domain}");
        }

        $account->addLog("Note: Email accounts must be recreated manually");
    }
}
