<?php
namespace App\Controllers;

use App\Core\Controller;
use App\Core\Auth;
use App\Core\DatabaseAlignmentManager;
use App\Core\Database;

class SettingsController extends Controller
{
    public function index(): void
    {
        if (!Auth::check()) { $this->redirect('/login'); }
        Auth::requireRole(['admin']);

        $pdo = Database::pdo();
        // S'assurer que la table et les colonnes existent
        $pdo->exec("CREATE TABLE IF NOT EXISTS settings (
            `key` VARCHAR(64) NOT NULL,
            `value` TEXT NULL,
            PRIMARY KEY (`key`)
        ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4");
        // Lecture défensive
        $settings = [];
        try {
            $settings = $pdo->query('SELECT `key`, `value` FROM settings')->fetchAll();
        } catch (\Throwable $e) {
            try {
                // fallback: prendre toutes les colonnes
                $rows = $pdo->query('SELECT * FROM settings')->fetchAll();
                foreach ($rows as $r) {
                    $keys = array_keys($r);
                    if (count($keys) >= 2) {
                        $settings[] = ['key' => $r[$keys[0]], 'value' => $r[$keys[1]]];
                    }
                }
            } catch (\Throwable $e2) { $settings = []; }
        }
        $kv = [];
        foreach ($settings as $s) { $kv[$s['key']] = $s['value']; }

        $saved = isset($_GET['saved']) && $_GET['saved'] == '1';
        $tested = isset($_GET['tested']) ? ($_GET['tested'] === '1' ? 'success' : 'error') : null;
        $testedMessage = isset($_GET['tested_message']) ? trim((string)$_GET['tested_message']) : null;
        $testedTransport = isset($_GET['tested_transport']) ? trim((string)$_GET['tested_transport']) : null;
        $user = Auth::user();
        $testEmailDefault = $user['email'] ?? '';
        $this->view('settings/index', [
            'settings' => $kv,
            'saved' => $saved,
            'tested' => $tested,
            'tested_message' => $testedMessage,
            'tested_transport' => $testedTransport,
            'test_email_default' => $testEmailDefault,
        ]);
    }

    public function save(): void
    {
        if (!Auth::check()) { $this->redirect('/login'); }
        Auth::requireRole(['admin']);

        if ($_SERVER['REQUEST_METHOD'] !== 'POST') { $this->redirect('/settings'); }
        $pdo = Database::pdo();
        // Assurer table/colonnes de base (ne modifie pas un schéma déjà existant)
        $pdo->exec("CREATE TABLE IF NOT EXISTS settings (
            `key` VARCHAR(64) NOT NULL,
            `value` TEXT NULL,
            PRIMARY KEY (`key`)
        ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4");

        // Normaliser le schéma si possible (colonnes key/value canoniques)
        $this->normalizeSettingsSchema($pdo);

        // Déterminer dynamiquement quelles colonnes utiliser pour (clé, valeur)
        $colKey = 'key';
        $colValue = 'value';
        try {
            $cols = $pdo->query('SHOW COLUMNS FROM settings')->fetchAll(\PDO::FETCH_COLUMN);
            if (!in_array('key', $cols, true) || !in_array('value', $cols, true)) {
                if (!empty($cols) && count($cols) >= 2) {
                    // Fallback: utiliser les deux premières colonnes trouvées
                    $colKey = (string)$cols[0];
                    $colValue = (string)$cols[1];
                }
            }
        } catch (\Throwable $e) {
            // En cas d'erreur, conserver les noms par défaut `key` / `value` et surtout ne pas bloquer la sauvegarde
        }

        // Seules les clés présentes dans le POST doivent être mises à jour
        // (évite d'écraser d'autres paramètres avec des valeurs vides)
        $allowed = ['app_name','default_currency','vat_rate','enforce_2fa','smtp_host','smtp_port','smtp_encryption','smtp_user','smtp_pass','smtp_from_name','operator_service_email','timezone','locale','mobile_api_base'];

        try {
            $sql = sprintf('REPLACE INTO settings (`%s`,`%s`) VALUES (:k,:v)', $colKey, $colValue);
            $stmt = $pdo->prepare($sql);
        } catch (\Throwable $e) {
            // Si même la préparation échoue (schéma très exotique), on abandonne silencieusement la persistance
            $stmt = null;
        }

        if ($stmt) {
            foreach ($allowed as $key) {
                if (array_key_exists($key, $_POST)) {
                    $val = $_POST[$key];
                    // Normaliser case particulière: enforce_2fa est booléen "1"/"0"
                    if ($key === 'enforce_2fa') {
                        $val = (string)((int)!!$val);
                    }
                    try {
                        $stmt->execute([':k' => $key, ':v' => $val]);
                    } catch (\Throwable $e) {
                        // Ne pas faire tomber toute la page pour une seule entrée invalide
                    }
                }
            }
        }

        // Uploads normalisés (logo, favicon, login background)
        // Stocker des chemins relatifs "branding/{fichier}" pour flexibilité base path
        $brandingDir = \App\Core\Config::publicPath() . DIRECTORY_SEPARATOR . 'storage' . DIRECTORY_SEPARATOR . 'uploads' . DIRECTORY_SEPARATOR . 'branding';
        if (!is_dir($brandingDir)) { @mkdir($brandingDir, 0777, true); }

        // Uploads web branding (clé stockée: branding_{field})
        $uploads = [
            'logo'     => 'logo',
            'favicon'  => 'favicon',
            'login_bg' => 'login-bg',
        ];

        foreach ($uploads as $field => $baseName) {
            if (!empty($_FILES[$field]['tmp_name']) && is_uploaded_file($_FILES[$field]['tmp_name'])) {
                $orig = $_FILES[$field]['name'];
                $ext = strtolower(pathinfo($orig, PATHINFO_EXTENSION));
                if (!preg_match('~^[a-z0-9]+$~', $ext)) { $ext = 'dat'; }
                $fileName = $baseName . '.' . $ext;
                $dest = $brandingDir . DIRECTORY_SEPARATOR . $fileName;
                if (@move_uploaded_file($_FILES[$field]['tmp_name'], $dest) && $stmt) {
                    $relative = 'branding/' . $fileName;
                    try {
                        $stmt->execute([':k' => 'branding_' . $field, ':v' => $relative]);
                    } catch (\Throwable $e) { }
                }
            }
        }

        // Uploads mobile (clé stockée: mobile_{field})
        $mobileUploads = [
            'mobile_logo'     => 'mobile-logo',
            'mobile_splash'   => 'mobile-splash',
            'mobile_login_bg' => 'mobile-login-bg',
            'mobile_icon'     => 'mobile-icon',
        ];

        foreach ($mobileUploads as $field => $baseName) {
            if (!empty($_FILES[$field]['tmp_name']) && is_uploaded_file($_FILES[$field]['tmp_name'])) {
                $orig = $_FILES[$field]['name'];
                $ext  = strtolower(pathinfo($orig, PATHINFO_EXTENSION));
                if (!preg_match('~^[a-z0-9]+$~', $ext)) { $ext = 'dat'; }
                $fileName = $baseName . '.' . $ext;
                $dest = $brandingDir . DIRECTORY_SEPARATOR . $fileName;
                if (@move_uploaded_file($_FILES[$field]['tmp_name'], $dest) && $stmt) {
                    $relative = 'branding/' . $fileName;
                    try {
                        $stmt->execute([':k' => $field, ':v' => $relative]);
                    } catch (\Throwable $e) { }
                }
            }
        }

        // Redirection avec indicateur de succès pour affichage toast/alerte
        $this->redirect('/settings?saved=1');
    }

    public function databaseAlignment(): void
    {
        if (!Auth::check()) { $this->redirect('/login'); }
        Auth::requireRole(['admin']);

        $pdo = Database::pdo();
        $inspection = DatabaseAlignmentManager::inspect($pdo);
        $updateReport = $_SESSION['database_alignment_update_report'] ?? null;
        unset($_SESSION['database_alignment_update_report']);

        $this->view('settings/database_alignment', [
            'title' => 'Alignement base de données',
            'inspection' => $inspection,
            'updated' => isset($_GET['updated']) && $_GET['updated'] === '1',
            'resolved_count' => (int)($_GET['resolved_count'] ?? 0),
            'remaining_count' => (int)($_GET['remaining_count'] ?? ($inspection['missing_count'] ?? 0)),
            'resolved_columns_count' => (int)($_GET['resolved_columns_count'] ?? ($updateReport['resolved_columns_count'] ?? 0)),
            'remaining_columns_count' => (int)($_GET['remaining_columns_count'] ?? ($inspection['missing_columns_count'] ?? 0)),
            'update_report' => $updateReport,
        ]);
    }

    public function databaseAlignmentUpdate(): void
    {
        if (!Auth::check()) { $this->redirect('/login'); }
        Auth::requireRole(['admin']);
        if (($_SERVER['REQUEST_METHOD'] ?? 'GET') !== 'POST') {
            $this->redirect('/settings/database-alignment');
            return;
        }

        $pdo = Database::pdo();
        $result = DatabaseAlignmentManager::applyUpdate($pdo);
        $_SESSION['database_alignment_update_report'] = $result;

        $this->redirect('/settings/database-alignment?updated=1&resolved_count=' . rawurlencode((string)($result['resolved_count'] ?? 0)) . '&remaining_count=' . rawurlencode((string)($result['remaining_count'] ?? 0)) . '&resolved_columns_count=' . rawurlencode((string)($result['resolved_columns_count'] ?? 0)) . '&remaining_columns_count=' . rawurlencode((string)($result['remaining_columns_count'] ?? 0)));
    }

    public function testEmail(): void
    {
        if (!Auth::check()) { $this->redirect('/login'); }
        Auth::requireRole(['admin']);
        if ($_SERVER['REQUEST_METHOD'] !== 'POST') { $this->redirect('/settings'); }
        $pdo = Database::pdo();
        $user = Auth::user();
        $email = null;
        $name = 'Admin';

        // Si un email de test est fourni dans le formulaire, on l'utilise en priorité
        if (!empty($_POST['test_email'])) {
            $email = trim((string)$_POST['test_email']);
            $name = $email;
        } else {
            // Sinon, on retombe sur l'email de l'utilisateur connecté (comportement historique)
            $email = $user['email'] ?? null;
            $name = $user['name'] ?? 'Admin';
        }

        $ok = false;
        $testedMessage = '';
        $testedTransport = 'smtp';
        if ($email) {
            try {
                $subject = 'Test SMTP - ' . date('d/m/Y H:i');
                $body = '<p>Ceci est un email de test SMTP généré automatiquement.</p>'
                      . '<p>Heure serveur: ' . htmlspecialchars(date('Y-m-d H:i:s')) . '</p>'
                      . '<p>Si vous voyez cet email, la configuration SMTP est fonctionnelle.</p>';
                $result = \App\Core\Mailer::sendWithReport($email, $name, $subject, $body, $pdo, [
                    'require_smtp' => true,
                    'allow_mail_fallback' => false,
                ]);
                $ok = !empty($result['ok']);
                $testedMessage = (string)($result['message'] ?? '');
                $testedTransport = (string)($result['transport'] ?? 'smtp');
            } catch (\Throwable $e) {
                $ok = false;
                $testedMessage = 'Erreur interne pendant le test email: ' . $e->getMessage();
            }
        } else {
            $testedMessage = 'Aucune adresse email de test n\'a été fournie.';
        }
        $this->redirect('/settings?tested=' . ($ok ? '1' : '0') . '&tested_transport=' . rawurlencode($testedTransport) . '&tested_message=' . rawurlencode($testedMessage));
    }

    /**
     * Tente de normaliser la table settings pour qu'elle expose toujours
     * les colonnes canoniques `key` et `value`.
     *
     * - Si la table possède déjà ces colonnes, ne fait rien.
     * - Si elle expose des colonnes assimilables (config_key/config_value, ...),
     *   migre les données vers une nouvelle table au format key/value puis renomme.
     * - En dernier recours, si la structure est vraiment exotique, laisse en l'état.
     */
    private function normalizeSettingsSchema(\PDO $pdo): void
    {
        try {
            $cols = $pdo->query('SHOW COLUMNS FROM settings')->fetchAll(\PDO::FETCH_ASSOC);
        } catch (\Throwable $e) {
            return; // impossible d'inspecter le schéma, ne rien faire
        }

        if (!$cols) { return; }

        $fields = [];
        foreach ($cols as $c) {
            if (!empty($c['Field'])) { $fields[] = $c['Field']; }
        }

        // Si le schéma attendu est déjà présent, ne rien faire
        if (in_array('key', $fields, true) && in_array('value', $fields, true)) {
            return;
        }

        // Chercher des colonnes "proches" pour essayer de migrer
        $candidatesKey = ['config_key', 'setting_key', 'name', 'code'];
        $candidatesValue = ['config_value', 'setting_value', 'value', 'val'];

        $srcKey = null;
        $srcValue = null;
        foreach ($candidatesKey as $ck) {
            if (in_array($ck, $fields, true)) { $srcKey = $ck; break; }
        }
        foreach ($candidatesValue as $cv) {
            if (in_array($cv, $fields, true)) { $srcValue = $cv; break; }
        }

        $legacyName = 'settings_legacy_' . date('YmdHis');

        try {
            if ($srcKey && $srcValue) {
                // Migration douce vers un schéma key/value canonique
                $pdo->exec("CREATE TABLE IF NOT EXISTS settings_migrate_tmp (
                    `key` VARCHAR(64) NOT NULL,
                    `value` TEXT NULL,
                    PRIMARY KEY (`key`)
                ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4");

                $sqlCopy = sprintf(
                    'INSERT IGNORE INTO settings_migrate_tmp (`key`,`value`) SELECT `%s`, `%s` FROM settings',
                    $srcKey,
                    $srcValue
                );
                $pdo->exec($sqlCopy);

                $pdo->exec(sprintf(
                    'RENAME TABLE settings TO `%s`, settings_migrate_tmp TO settings',
                    $legacyName
                ));
            } else {
                // Schéma trop exotique : on crée une nouvelle table canonique
                $pdo->exec(sprintf('RENAME TABLE settings TO `%s`', $legacyName));
                $pdo->exec("CREATE TABLE IF NOT EXISTS settings (
                    `key` VARCHAR(64) NOT NULL,
                    `value` TEXT NULL,
                    PRIMARY KEY (`key`)
                ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4");
            }
        } catch (\Throwable $e) {
            // En cas d'échec (permissions, etc.), on ne force rien pour ne pas casser l'existant
            return;
        }
    }
}
