<?php

namespace PortalPeople\NgaugeBackupSql;

use Yii;
use yii\console\Controller;
use yii\console\ExitCode;
use yii\helpers\Console;
use PortalPeople\NgaugeBackupSql\components\BackupManager;

/**
 * Database Backup and Restore Console Command
 * 
 * Provides CLI interface for database table backup and restore operations.
 * 
 * @property Module $module The module instance
 */
class BackupCommand extends Controller
{
    /**
     * @var string|null Table name to backup/restore
     */
    public $table;
    
    /**
     * @var string|null Custom filename for backup
     */
    public $filename;
    
    /**
     * @var bool Whether to compress backups
     */
    public $compress = true;
    
    /**
     * @var bool Force operation without confirmation
     */
    public $force = false;
    
    /**
     * @var int Days to keep backups (for cleanup)
     */
    public $days;
    
    /**
     * @var string|null Backup file to restore
     */
    public $file;

    /**
     * @var BackupManager
     */
    private $_manager;

    /**
     * @inheritdoc
     */
    public function options($actionID)
    {
        $options = parent::options($actionID);
        
        switch ($actionID) {
            case 'create':
                return array_merge($options, ['table', 'filename', 'compress']);
            case 'list':
                return array_merge($options, ['table']);
            case 'restore':
                return array_merge($options, ['file', 'force']);
            case 'cleanup':
                return array_merge($options, ['days', 'force']);
            default:
                return $options;
        }
    }

    /**
     * @inheritdoc
     */
    public function optionAliases()
    {
        return array_merge(parent::optionAliases(), [
            't' => 'table',
            'f' => 'file',
            'n' => 'filename',
            'c' => 'compress',
            'd' => 'days',
        ]);
    }

    /**
     * @inheritdoc
     */
    public function init()
    {
        parent::init();
        $this->_manager = Yii::createObject(BackupManager::class);
    }

    /**
     * Creates a database table backup
     * 
     * Usage:
     *   ./yii db-backup/create
     *   ./yii db-backup/create --table=users
     *   ./yii db-backup/create -t users -n users-backup.sql.gz
     *   ./yii db-backup/create --table=pricing --compress=0
     * 
     * @return int Exit code
     */
    public function actionCreate()
    {
        $this->stdout("Database Backup Utility\n", Console::BOLD);
        $this->stdout(str_repeat('=', 50) . "\n\n");
        
        // Get table name
        if ($this->table === null) {
            $tables = $this->_manager->listTables();
            
            if (empty($tables)) {
                $this->stderr("No tables found in database.\n", Console::FG_RED);
                return ExitCode::DATAERR;
            }
            
            $this->stdout("Available tables:\n", Console::FG_CYAN);
            foreach ($tables as $i => $table) {
                $this->stdout(sprintf("  [%d] %s\n", $i + 1, $table));
            }
            $this->stdout("\n");
            
            $selection = $this->prompt("Select table to backup (number or name):", [
                'required' => true,
            ]);
            
            // Check if numeric selection
            if (is_numeric($selection)) {
                $index = (int)$selection - 1;
                if (!isset($tables[$index])) {
                    $this->stderr("Invalid selection.\n", Console::FG_RED);
                    return ExitCode::DATAERR;
                }
                $this->table = $tables[$index];
            } else {
                $this->table = $selection;
            }
        }
        
        // Validate table exists
        $allTables = $this->_manager->listTables(false);
        if (!in_array($this->table, $allTables)) {
            $this->stderr("Table '{$this->table}' does not exist.\n", Console::FG_RED);
            return ExitCode::DATAERR;
        }
        
        $this->stdout("\nCreating backup of table: ", Console::FG_YELLOW);
        $this->stdout($this->table . "\n", Console::BOLD);
        
        if ($this->filename) {
            $this->stdout("Custom filename: {$this->filename}\n");
        }
        
        $this->stdout("Compression: " . ($this->compress ? "enabled" : "disabled") . "\n");
        $this->stdout("\n");
        
        try {
            $startTime = microtime(true);
            
            $backupFile = $this->_manager->createBackup(
                $this->table,
                $this->filename,
                $this->compress
            );
            
            $duration = round(microtime(true) - $startTime, 2);
            $fileSize = $this->formatBytes(filesize($backupFile));
            
            $this->stdout("✓ Backup created successfully!\n", Console::FG_GREEN);
            $this->stdout("\n");
            $this->stdout("  File: " . basename($backupFile) . "\n");
            $this->stdout("  Size: {$fileSize}\n");
            $this->stdout("  Time: {$duration}s\n");
            $this->stdout("  Path: {$backupFile}\n");
            
            return ExitCode::OK;
            
        } catch (\Exception $e) {
            $this->stderr("\n✗ Backup failed!\n", Console::FG_RED);
            $this->stderr("  Error: " . $e->getMessage() . "\n");
            return ExitCode::SOFTWARE;
        }
    }

    /**
     * Lists available backup files
     * 
     * Usage:
     *   ./yii db-backup/list
     *   ./yii db-backup/list --table=users
     *   ./yii db-backup/list -t pricing
     * 
     * @return int Exit code
     */
    public function actionList()
    {
        $this->stdout("Available Backups\n", Console::BOLD);
        $this->stdout(str_repeat('=', 80) . "\n\n");
        
        $restorePoints = $this->_manager->listRestorePoints($this->table);
        
        if (empty($restorePoints)) {
            $tableMsg = $this->table ? " for table '{$this->table}'" : "";
            $this->stdout("No backups found{$tableMsg}.\n", Console::FG_YELLOW);
            return ExitCode::OK;
        }
        
        // Group by table
        $grouped = [];
        foreach ($restorePoints as $point) {
            $table = $point['table'];
            if (!isset($grouped[$table])) {
                $grouped[$table] = [];
            }
            $grouped[$table][] = $point;
        }
        
        foreach ($grouped as $tableName => $points) {
            $this->stdout("{$tableName}", Console::FG_CYAN);
            $this->stdout(" ({count} backup(s))\n", ['count' => count($points)]);
            $this->stdout(str_repeat('-', 80) . "\n");
            
            foreach ($points as $i => $point) {
                $this->stdout(sprintf(
                    "  [%d] %-40s %10s  %s\n",
                    $i + 1,
                    $point['filename'],
                    $point['size_formatted'],
                    $point['created_formatted']
                ));
            }
            
            $this->stdout("\n");
        }
        
        $this->stdout("Total: " . count($restorePoints) . " backup file(s)\n", Console::FG_GREEN);
        
        return ExitCode::OK;
    }

    /**
     * Restores database from a backup file
     * 
     * Usage:
     *   ./yii db-backup/restore
     *   ./yii db-backup/restore --file=users_20260210_1430.sql.gz
     *   ./yii db-backup/restore -f users_20260210_1430.sql.gz --force
     * 
     * @return int Exit code
     */
    public function actionRestore()
    {
        $this->stdout("Database Restore Utility\n", Console::BOLD);
        $this->stdout(str_repeat('=', 50) . "\n\n");
        
        // Get backup file
        if ($this->file === null) {
            $restorePoints = $this->_manager->listRestorePoints();
            
            if (empty($restorePoints)) {
                $this->stderr("No backup files found.\n", Console::FG_RED);
                return ExitCode::DATAERR;
            }
            
            $this->stdout("Available restore points:\n", Console::FG_CYAN);
            foreach ($restorePoints as $i => $point) {
                $this->stdout(sprintf(
                    "  [%d] %-40s %10s  %s\n",
                    $i + 1,
                    $point['filename'],
                    $point['size_formatted'],
                    $point['created_formatted']
                ));
            }
            $this->stdout("\n");
            
            $selection = $this->prompt("Select backup to restore (number or filename):", [
                'required' => true,
            ]);
            
            // Check if numeric selection
            if (is_numeric($selection)) {
                $index = (int)$selection - 1;
                if (!isset($restorePoints[$index])) {
                    $this->stderr("Invalid selection.\n", Console::FG_RED);
                    return ExitCode::DATAERR;
                }
                $this->file = $restorePoints[$index]['filename'];
            } else {
                $this->file = $selection;
            }
        }
        
        $this->stdout("\nRestore from: ", Console::FG_YELLOW);
        $this->stdout($this->file . "\n", Console::BOLD);
        
        // Confirm unless forced
        if (!$this->force) {
            $this->stdout("\n");
            $this->stdout("WARNING: This will overwrite existing data!\n", Console::FG_RED);
            $confirm = $this->confirm("Are you sure you want to continue?", false);
            
            if (!$confirm) {
                $this->stdout("Restore cancelled.\n", Console::FG_YELLOW);
                return ExitCode::OK;
            }
        }
        
        $this->stdout("\nRestoring database...\n");
        
        try {
            $startTime = microtime(true);
            
            $this->_manager->restore($this->file);
            
            $duration = round(microtime(true) - $startTime, 2);
            
            $this->stdout("\n✓ Restore completed successfully!\n", Console::FG_GREEN);
            $this->stdout("  Time: {$duration}s\n");
            
            return ExitCode::OK;
            
        } catch (\Exception $e) {
            $this->stderr("\n✗ Restore failed!\n", Console::FG_RED);
            $this->stderr("  Error: " . $e->getMessage() . "\n");
            return ExitCode::SOFTWARE;
        }
    }

    /**
     * Cleanup old backup files based on retention policy
     * 
     * Usage:
     *   ./yii db-backup/cleanup
     *   ./yii db-backup/cleanup --days=7
     *   ./yii db-backup/cleanup -d 7 --force
     * 
     * @return int Exit code
     */
    public function actionCleanup()
    {
        $this->stdout("Backup Cleanup Utility\n", Console::BOLD);
        $this->stdout(str_repeat('=', 50) . "\n\n");
        
        $days = $this->days ?? $this->module->retentionDays;
        
        if ($days <= 0) {
            $this->stdout("Retention policy is disabled (retentionDays = 0).\n", Console::FG_YELLOW);
            return ExitCode::OK;
        }
        
        $this->stdout("Retention period: {$days} days\n");
        $this->stdout("Backups older than " . date('Y-m-d', time() - ($days * 86400)) . " will be deleted.\n\n");
        
        // Confirm unless forced
        if (!$this->force) {
            $confirm = $this->confirm("Continue with cleanup?", false);
            
            if (!$confirm) {
                $this->stdout("Cleanup cancelled.\n", Console::FG_YELLOW);
                return ExitCode::OK;
            }
        }
        
        try {
            $deleted = $this->_manager->cleanupOldBackups($days);
            
            if ($deleted > 0) {
                $this->stdout("✓ Deleted {$deleted} old backup file(s).\n", Console::FG_GREEN);
            } else {
                $this->stdout("No old backups to delete.\n", Console::FG_YELLOW);
            }
            
            return ExitCode::OK;
            
        } catch (\Exception $e) {
            $this->stderr("✗ Cleanup failed: " . $e->getMessage() . "\n", Console::FG_RED);
            return ExitCode::SOFTWARE;
        }
    }

    /**
     * Display help information
     * 
     * @return int Exit code
     */
    public function actionIndex()
    {
        $this->stdout("Database Backup & Restore Utility\n", Console::BOLD);
        $this->stdout(str_repeat('=', 50) . "\n\n");
        
        $this->stdout("Available commands:\n\n", Console::FG_CYAN);
        
        $commands = [
            'create' => 'Create a database table backup',
            'list' => 'List available backup files',
            'restore' => 'Restore database from backup',
            'cleanup' => 'Remove old backups based on retention policy',
        ];
        
        foreach ($commands as $cmd => $desc) {
            $this->stdout("  db-backup/{$cmd}", Console::FG_GREEN);
            $this->stdout(str_repeat(' ', 20 - strlen($cmd)));
            $this->stdout($desc . "\n");
        }
        
        $this->stdout("\nFor detailed help on a command:\n");
        $this->stdout("  ./yii help db-backup/create\n");
        $this->stdout("  ./yii help db-backup/restore\n");
        $this->stdout("\n");
        
        return ExitCode::OK;
    }

    /**
     * Format bytes to human-readable size
     * 
     * @param int $bytes File size in bytes
     * @return string Formatted size
     */
    protected function formatBytes($bytes)
    {
        $units = ['B', 'KB', 'MB', 'GB', 'TB'];
        $bytes = max($bytes, 0);
        $pow = floor(($bytes ? log($bytes) : 0) / log(1024));
        $pow = min($pow, count($units) - 1);
        $bytes /= pow(1024, $pow);
        
        return round($bytes, 2) . ' ' . $units[$pow];
    }
}
