<?php
namespace App\Modules\System\Controllers;

use Core\Database;
use PDO;

/**
 * OTIS Backup & Restore Controller
 * Handles secure JSON export and safe transactional import of all user data.
 */
class BackupController extends \Core\Controller {

    // All user-data tables to include in backup/restore
    // Format: [table => primary_key]
    private const DATA_TABLES = [
        'health_profile'         => 'user_id',
        'health_weight_logs'     => 'id',
        'health_blood_sugar'     => 'id',
        'health_fasts'           => 'id',
        'health_meals'           => 'id',
        'health_medications'     => 'id',
        'health_med_logs'        => 'id',
        'health_water_logs'      => 'user_id',
        'health_activities'      => 'id',
        'health_labs'            => 'id',
        'health_goals'           => 'id',
        'finance_incomes'        => 'id',
        'finance_expenses'       => 'id',
        'finance_saving_goals'   => 'id',
        'finance_installments'   => 'id',
        'finance_debts'          => 'id',
        'finance_investments'    => 'id',
        'habits'                 => 'id',
        'habit_logs'             => 'id',
        'behavior_habits'        => 'id',
        'behavior_logs'          => 'id',
        'daily_journal'          => 'id',
        'todo_tasks'             => 'id',
        'todo_daily_logs'        => 'id',
        'calendar_events'        => 'id',
        'calendar_time_blocks'   => 'id',
        'edu_programs'           => 'id',
        'edu_courses'            => 'id',
        'edu_skills'             => 'id',
        'spiritual_prayers'      => 'id',
        'spiritual_quran'        => 'id',
        'spiritual_goals'        => 'id',
        'monthly_plans'          => 'id',
        'vault_items'            => 'id',
        'user_settings'          => 'user_id',
        'notifications'          => 'id',
        'advisor_memory'         => 'id',
        'smart_reminders'        => 'id',
    ];

    // System tables that MUST NOT be deleted during restore
    private const PROTECTED_TABLES = ['users', 'action_logs'];

    /**
     * Export all user data as a downloadable JSON file
     */
    public function export() {
        $db = Database::getInstance();
        $userId = $this->userId;

        $backup = [
            'meta' => [
                'version'      => '2.0',
                'system'       => 'OTIS Life System',
                'exported_at'  => date('Y-m-d H:i:s'),
                'user_id'      => $userId,
                'tables_count' => count(self::DATA_TABLES),
            ],
            'data' => []
        ];

        foreach (self::DATA_TABLES as $table => $pk) {
            try {
                // Check table exists
                $exists = $db->query(
                    "SELECT COUNT(*) FROM information_schema.tables WHERE table_schema=DATABASE() AND table_name=?",
                    [$table]
                )->fetchColumn();

                if (!$exists) continue;

                // Special case: health_water_logs has composite key
                if ($pk === 'user_id' && $table !== 'user_settings' && $table !== 'health_profile') {
                    $rows = $db->query("SELECT * FROM `{$table}` WHERE user_id=?", [$userId])->fetchAll(PDO::FETCH_ASSOC);
                } else {
                    $rows = $db->query("SELECT * FROM `{$table}` WHERE user_id=?", [$userId])->fetchAll(PDO::FETCH_ASSOC);
                }

                $backup['data'][$table] = $rows;
            } catch (\Exception $e) {
                // Table might not exist in older installs — skip gracefully
                $backup['data'][$table] = [];
            }
        }

        $json = json_encode($backup, JSON_UNESCAPED_UNICODE | JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES);
        $filename = 'OTIS_backup_' . date('Y-m-d_H-i-s') . '.json';

        // Log the action
        $this->logAction('backup_export', 'system');

        // Stream the file
        header('Content-Type: application/json; charset=utf-8');
        header('Content-Disposition: attachment; filename="' . $filename . '"');
        header('Content-Length: ' . strlen($json));
        header('Pragma: no-cache');
        header('Expires: 0');
        echo $json;
        exit;
    }

    /**
     * Show the backup/restore UI (rendered inside settings)
     */
    public function index() {
        $this->view('System/Views/backup', [
            'title' => 'النسخ الاحتياطي والاستعادة | OTIS',
        ]);
    }

    /**
     * Import/restore from uploaded JSON backup file
     * Requires: password + file upload
     */
    public function import() {
        if ($_SERVER['REQUEST_METHOD'] !== 'POST') {
            abort(405, 'Method Not Allowed');
        }

        verify_csrf_token($_POST['csrf_token'] ?? '');

        // 1. Password verification
        $password = $_POST['restore_password'] ?? '';
        if (empty($password)) {
            set_flash_message('backup', 'يجب إدخال كلمة المرور للمتابعة.', 'danger');
            redirect('/system/backup');
        }

        $db = Database::getInstance();
        $user = $db->query("SELECT password_hash FROM users WHERE id=?", [$this->userId])->fetch(PDO::FETCH_OBJ);
        if (!$user || !password_verify($password, $user->password_hash)) {
            set_flash_message('backup', 'كلمة المرور غير صحيحة. رُفض الاستعادة.', 'danger');
            redirect('/system/backup');
        }

        // 2. File upload validation
        if (!isset($_FILES['backup_file']) || $_FILES['backup_file']['error'] !== UPLOAD_ERR_OK) {
            set_flash_message('backup', 'لم يتم رفع ملف صالح.', 'danger');
            redirect('/system/backup');
        }

        $file = $_FILES['backup_file'];

        // Validate file type (JSON only)
        $finfo = new \finfo(FILEINFO_MIME_TYPE);
        $mime  = $finfo->file($file['tmp_name']);
        if (!in_array($mime, ['application/json', 'text/plain'])) {
            set_flash_message('backup', 'نوع الملف غير مدعوم. يُقبل ملفات JSON فقط.', 'danger');
            redirect('/system/backup');
        }

        // 3. Parse and validate JSON structure
        $json = file_get_contents($file['tmp_name']);
        $backup = json_decode($json, true);

        if (!$backup || !isset($backup['meta']['version'], $backup['data'])) {
            set_flash_message('backup', 'ملف النسخ الاحتياطي تالف أو غير صالح.', 'danger');
            redirect('/system/backup');
        }

        if ($backup['meta']['version'] !== '2.0') {
            set_flash_message('backup', 'إصدار ملف النسخ الاحتياطي غير متوافق.', 'warning');
            redirect('/system/backup');
        }

        // 4. Owner validation — don't allow restoring another user's backup
        // We allow cross-user restore (user imports their own exported data under new account)
        // but sanitize all user_id fields to current user

        // 5. Execute restore inside a transaction
        try {
            $pdo = $db->getPDO();
            $pdo->beginTransaction();

            // 5a. Clear existing user data
            $this->clearUserData($db, $this->userId);

            // 5b. Re-insert backup data (reassigning user_id)
            $importedCount = 0;
            $restoredTables = [];
            foreach ($backup['data'] as $table => $rows) {
                if (!in_array($table, array_keys(self::DATA_TABLES))) continue;
                if (in_array($table, self::PROTECTED_TABLES)) continue;
                if (empty($rows)) continue;

                // Validate table exists
                $exists = $db->query(
                    "SELECT COUNT(*) FROM information_schema.tables WHERE table_schema=DATABASE() AND table_name=?",
                    [$table]
                )->fetchColumn();
                if (!$exists) continue;

                $rowsInserted = $this->insertRows($pdo, $table, $rows, $this->userId);
                if ($rowsInserted > 0) {
                    $restoredTables[] = $table;
                    $importedCount += $rowsInserted;
                }
            }

            $pdo->commit();

            $this->logAction('backup_restore', 'system');
            set_flash_message('backup',
                "✅ تم استعادة البيانات بنجاح! ({$importedCount} سجل في " . count($restoredTables) . " جداول).",
                'success'
            );
        } catch (\Exception $e) {
            if (isset($pdo) && $pdo->inTransaction()) {
                $pdo->rollBack();
            }
            error_log('[OTIS Backup] Restore failed: ' . $e->getMessage());
            set_flash_message('backup', 'فشل الاستعادة! تم التراجع عن كل التغييرات. ' . $e->getMessage(), 'danger');
        }

        redirect('/system/backup');
    }

    /**
     * Clear all user data tables (in safe order respecting FK constraints)
     */
    private function clearUserData(Database $db, int $userId): void {
        // Disable FK checks during delete
        $pdo = $db->getPDO();
        $pdo->exec('SET FOREIGN_KEY_CHECKS = 0');

        foreach (array_reverse(array_keys(self::DATA_TABLES)) as $table) {
            if (in_array($table, self::PROTECTED_TABLES)) continue;
            try {
                $exists = $db->query(
                    "SELECT COUNT(*) FROM information_schema.tables WHERE table_schema=DATABASE() AND table_name=?",
                    [$table]
                )->fetchColumn();
                if ($exists) {
                    $db->query("DELETE FROM `{$table}` WHERE user_id=?", [$userId]);
                }
            } catch (\Exception $e) { /* Table schema mismatch — skip */ }
        }

        $pdo->exec('SET FOREIGN_KEY_CHECKS = 1');
    }

    /**
     * Insert rows into a table with user_id override to prevent data hijacking
     */
    private function insertRows(\PDO $pdo, string $table, array $rows, int $userId): int {
        if (empty($rows)) return 0;

        $count = 0;
        // Get table columns via DESCRIBE
        $cols = $pdo->query("DESCRIBE `{$table}`")->fetchAll(PDO::FETCH_COLUMN);
        if (empty($cols)) return 0;

        foreach ($rows as $row) {
            // Override user_id to current authenticated user
            if (in_array('user_id', $cols)) {
                $row['user_id'] = $userId;
            }

            // Remove primary key (auto-increment)
            $pk = self::DATA_TABLES[$table] ?? 'id';
            if ($pk !== 'user_id') {
                unset($row[$pk]);
            }

            // Filter only valid columns
            $filtered = array_intersect_key($row, array_flip($cols));
            if (empty($filtered)) continue;

            $colNames   = implode(', ', array_map(fn($c) => "`{$c}`", array_keys($filtered)));
            $placeholders = implode(', ', array_fill(0, count($filtered), '?'));

            try {
                $stmt = $pdo->prepare("INSERT IGNORE INTO `{$table}` ({$colNames}) VALUES ({$placeholders})");
                $stmt->execute(array_values($filtered));
                $count++;
            } catch (\Exception $e) {
                // Skip row if conflict
                error_log("[OTIS Backup] Row insert failed [{$table}]: " . $e->getMessage());
            }
        }

        return $count;
    }

    private function logAction(string $action, string $module): void {
        try {
            $db = Database::getInstance();
            $db->query(
                "INSERT INTO action_logs (user_id, module, action, description) VALUES (?, ?, ?, ?)",
                [$this->userId, $module, $action, "Backup {$action} performed"]
            );
        } catch (\Exception $e) { /* Non-critical */ }
    }
}
