<?php

namespace App\Console\Commands;

use Illuminate\Console\Command;
use Illuminate\Support\Facades\Log;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Storage;
use Illuminate\Support\Facades\Artisan;
use ZipArchive;

class BackupRunCommand extends Command
{
    /**
     * The name and signature of the console command.
     *
     * @var string
     */
    protected $signature = 'backup:run {--type=full}';

    /**
     * The console command description.
     *
     * @var string
     */
    protected $description = 'Create a backup of the application';

    /**
     * Execute the console command.
     */
    public function handle()
    {
        try {
            $backupType = $this->option('type');
            $this->info("Starting {$backupType} backup...");
            
            $backupPath = $this->createBackup($backupType);
            
            if ($backupPath) {
                // Update backup settings
                DB::table('settings')
                    ->updateOrInsert(
                        ['key' => 'last_backup_date'],
                        ['value' => now()->toISOString(), 'updated_at' => now()]
                    );

                DB::table('settings')
                    ->updateOrInsert(
                        ['key' => 'last_backup_path'],
                        ['value' => $backupPath, 'updated_at' => now()]
                    );

                $message = "Backup completed successfully: {$backupPath}";
                $this->info($message);
                Log::info($message);
                
                return Command::SUCCESS;
            } else {
                throw new \Exception('Backup creation failed');
            }
            
        } catch (\Exception $e) {
            $error = 'Backup failed: ' . $e->getMessage();
            $this->error($error);
            Log::error($error);
            return Command::FAILURE;
        }
    }

    /**
     * Create backup based on type
     */
    private function createBackup($type)
    {
        $timestamp = now()->format('Y-m-d_H-i-s');
        $backupDir = storage_path('app/backups');
        
        // Ensure backup directory exists
        if (!is_dir($backupDir)) {
            mkdir($backupDir, 0755, true);
        }

        switch ($type) {
            case 'database':
                return $this->createDatabaseBackup($timestamp);
            case 'files':
                return $this->createFilesBackup($timestamp);
            case 'settings':
                return $this->createSettingsBackup($timestamp);
            case 'full':
            default:
                return $this->createFullBackup($timestamp);
        }
    }

    /**
     * Create database backup
     */
    private function createDatabaseBackup($timestamp)
    {
        $backupPath = storage_path("app/backups/database_backup_{$timestamp}.sql");
        
        $dbConfig = config('database.connections.' . config('database.default'));
        $command = sprintf(
            'mysqldump -h%s -u%s -p%s %s > %s',
            $dbConfig['host'],
            $dbConfig['username'],
            $dbConfig['password'],
            $dbConfig['database'],
            $backupPath
        );

        exec($command, $output, $returnCode);
        
        if ($returnCode === 0 && file_exists($backupPath)) {
            $this->info("Database backup created: " . basename($backupPath));
            return $backupPath;
        }
        
        return false;
    }

    /**
     * Create files backup
     */
    private function createFilesBackup($timestamp)
    {
        $backupPath = storage_path("app/backups/files_backup_{$timestamp}.zip");
        
        $zip = new ZipArchive();
        if ($zip->open($backupPath, ZipArchive::CREATE) === TRUE) {
            // Add storage/app files
            $this->addDirectoryToZip($zip, storage_path('app/public'), 'storage/');
            
            // Add uploads if they exist
            $uploadsPath = public_path('uploads');
            if (is_dir($uploadsPath)) {
                $this->addDirectoryToZip($zip, $uploadsPath, 'uploads/');
            }
            
            $zip->close();
            $this->info("Files backup created: " . basename($backupPath));
            return $backupPath;
        }
        
        return false;
    }

    /**
     * Create settings backup
     */
    private function createSettingsBackup($timestamp)
    {
        $backupPath = storage_path("app/backups/settings_backup_{$timestamp}.json");
        
        // Export all settings
        $settings = DB::table('settings')->get();
        $aiSettings = DB::table('ai_settings')->get();
        
        $backupData = [
            'timestamp' => now()->toISOString(),
            'type' => 'settings',
            'settings' => $settings,
            'ai_settings' => $aiSettings,
        ];
        
        if (file_put_contents($backupPath, json_encode($backupData, JSON_PRETTY_PRINT))) {
            $this->info("Settings backup created: " . basename($backupPath));
            return $backupPath;
        }
        
        return false;
    }

    /**
     * Create full backup
     */
    private function createFullBackup($timestamp)
    {
        $backupPath = storage_path("app/backups/full_backup_{$timestamp}.zip");
        
        $zip = new ZipArchive();
        if ($zip->open($backupPath, ZipArchive::CREATE) === TRUE) {
            // Add database dump
            $dbBackup = $this->createDatabaseBackup($timestamp);
            if ($dbBackup) {
                $zip->addFile($dbBackup, 'database.sql');
            }
            
            // Add files
            $this->addDirectoryToZip($zip, storage_path('app/public'), 'storage/');
            
            // Add settings
            $settingsBackup = $this->createSettingsBackup($timestamp);
            if ($settingsBackup) {
                $zip->addFile($settingsBackup, 'settings.json');
            }
            
            $zip->close();
            
            // Clean up temporary files
            if ($dbBackup) unlink($dbBackup);
            if ($settingsBackup) unlink($settingsBackup);
            
            $this->info("Full backup created: " . basename($backupPath));
            return $backupPath;
        }
        
        return false;
    }

    /**
     * Add directory to zip recursively
     */
    private function addDirectoryToZip($zip, $directory, $zipPath = '')
    {
        if (!is_dir($directory)) {
            return;
        }

        $iterator = new \RecursiveIteratorIterator(
            new \RecursiveDirectoryIterator($directory)
        );

        foreach ($iterator as $file) {
            if ($file->isFile()) {
                $filePath = $file->getRealPath();
                $relativePath = $zipPath . substr($filePath, strlen($directory) + 1);
                $zip->addFile($filePath, $relativePath);
            }
        }
    }
}