<?php

declare(strict_types=1);

namespace Admin\Controllers;

use App\Core\Mail;

/**
 * AdminSmtpController — Dedicated SMTP Configuration Page
 *
 * Allows admin to:
 *  - View & edit SMTP settings via a dedicated form
 *  - Test SMTP connection
 *  - Send a test email to verify everything works
 *  - See PHPMailer installation status & debug info
 */
class AdminSmtpController extends AdminController
{
    /**
     * SMTP setting keys in the settings table.
     */
    private const SMTP_KEYS = [
        'smtp_host',
        'smtp_port',
        'smtp_username',
        'smtp_password',
        'smtp_encryption',
        'smtp_from_email',
        'smtp_from_name',
    ];

    /**
     * Default values for SMTP settings.
     */
    private const SMTP_DEFAULTS = [
        'smtp_host'       => '',
        'smtp_port'       => '587',
        'smtp_username'   => '',
        'smtp_password'   => '',
        'smtp_encryption' => 'tls',
        'smtp_from_email' => '',
        'smtp_from_name'  => 'Gmind Institute',
    ];

    /**
     * Show SMTP settings form.
     */
    public function index(): never
    {
        // Read current SMTP settings DIRECTLY from database
        $dbSettings = $this->readSettingsFromDb();

        // Merge: DB values override defaults
        $config = array_merge(self::SMTP_DEFAULTS, $dbSettings);

        // Get Mail helper status — inject DB settings so isConfigured() works
        $mailHelper       = new Mail();
        $mailHelper->setConfig($config);
        $mailerAvailable  = $mailHelper->isAvailable();
        $smtpConfigured   = $mailHelper->isConfigured();
        $phpMailerPath    = $mailHelper->getPHPMailerPathFound();

        $this->adminView('smtp_settings', [
            'title'           => 'SMTP / Email Settings',
            'currentPage'     => 'smtp-settings',
            'config'          => $config,
            'mailerAvailable' => $mailerAvailable,
            'smtpConfigured'  => $smtpConfigured,
            'phpMailerPath'   => $phpMailerPath,
            'dbSettings'      => $dbSettings,      // raw DB values for debug display
            'dbColumns'       => $this->detectColumns(), // which columns exist
        ]);
    }

    /**
     * Save SMTP settings.
     */
    public function save(): never
    {
        $this->validateCsrf();

        $fields = [
            'smtp_host'       => trim($_POST['smtp_host'] ?? ''),
            'smtp_port'       => trim($_POST['smtp_port'] ?? '587'),
            'smtp_username'   => trim($_POST['smtp_username'] ?? ''),
            'smtp_password'   => trim($_POST['smtp_password'] ?? ''),
            'smtp_encryption' => trim($_POST['smtp_encryption'] ?? 'tls'),
            'smtp_from_email' => trim($_POST['smtp_from_email'] ?? ''),
            'smtp_from_name'  => trim($_POST['smtp_from_name'] ?? 'Gmind Institute'),
        ];

        // Validate: if host is set, from_email must also be set
        if (!empty($fields['smtp_host']) && (empty($fields['smtp_from_email']) || !filter_var($fields['smtp_from_email'], FILTER_VALIDATE_EMAIL))) {
            flash('error', 'SMTP Host is set but From Email is missing or invalid. Both are required together.');
            redirect(BASE_PATH . '/admin/smtp-settings');
        }

        // Detect which columns exist in the settings table
        $columns = $this->detectColumns();

        // Save each setting
        $saved = 0;
        $errors = [];
        foreach ($fields as $key => $value) {
            // Don't overwrite password with mask
            if ($key === 'smtp_password' && $value === '••••••••') {
                continue;
            }

            $result = $this->saveSetting($key, $value, $columns);
            if ($result === true) {
                $saved++;
            } else {
                $errors[] = "{$key}: " . ($result ?: 'unknown error');
            }
        }

        if (count($errors) > 0 && $saved > 0) {
            flash('warning', "SMTP settings partially saved ({$saved} ok, " . count($errors) . " failed). Errors: " . implode('; ', $errors));
        } elseif (count($errors) > 0) {
            flash('error', "Failed to save SMTP settings. Errors: " . implode('; ', $errors));
        } else {
            flash('success', "SMTP settings saved successfully ({$saved} fields updated).");
        }

        redirect(BASE_PATH . '/admin/smtp-settings');
    }

    /**
     * Test SMTP connection only (no email sent).
     */
    public function testConnection(): never
    {
        $this->validateCsrf();

        // Read settings DIRECTLY from database — Mail's own DB reading may fail
        $dbSettings = $this->readSettingsFromDb();

        $mailHelper = new Mail();
        $mailHelper->setConfig(array_merge(self::SMTP_DEFAULTS, $dbSettings));

        if (!$mailHelper->isAvailable()) {
            flash('error', 'PHPMailer library not found. Please install it first.');
            redirect(BASE_PATH . '/admin/smtp-settings');
        }

        $result = $mailHelper->testConnection();

        if ($result['success']) {
            flash('success', 'SMTP Connection Test: SUCCESS — ' . $result['message']);
        } else {
            flash('error', 'SMTP Connection Test: FAILED — ' . $result['message']);
        }

        redirect(BASE_PATH . '/admin/smtp-settings');
    }

    /**
     * Send a test email to verify SMTP is fully working.
     */
    public function sendTestEmail(): never
    {
        $this->validateCsrf();

        $testEmail = trim($_POST['test_email'] ?? '');

        if (empty($testEmail) || !filter_var($testEmail, FILTER_VALIDATE_EMAIL)) {
            flash('error', 'Please enter a valid test email address.');
            redirect(BASE_PATH . '/admin/smtp-settings');
        }

        // Read settings DIRECTLY from database — Mail's own DB reading may fail
        $dbSettings = $this->readSettingsFromDb();

        $mailHelper = new Mail();
        $mailHelper->setConfig(array_merge(self::SMTP_DEFAULTS, $dbSettings));

        if (!$mailHelper->isAvailable()) {
            flash('error', 'PHPMailer library not found. Please install it first.');
            redirect(BASE_PATH . '/admin/smtp-settings');
        }

        if (!$mailHelper->isConfigured()) {
            flash('error', 'SMTP is not configured. Please fill in SMTP Host and From Email first.');
            redirect(BASE_PATH . '/admin/smtp-settings');
        }

        $subject = 'Gmind Institute — SMTP Test Email';
        $bodyHtml = '<div style="font-family:Arial,sans-serif;max-width:600px;margin:0 auto;padding:20px;">'
            . '<div style="background:linear-gradient(135deg,#1a237e,#0d47a1);padding:30px;text-align:center;border-radius:8px 8px 0 0;">'
            . '<h1 style="color:#fff;margin:0;font-size:22px;">SMTP Test Successful!</h1>'
            . '</div>'
            . '<div style="padding:20px;background:#fff;border:1px solid #e0e0e0;border-radius:0 0 8px 8px;">'
            . '<p style="font-size:15px;color:#333;">Hello,</p>'
            . '<p style="font-size:15px;color:#555;">This is a test email from <strong>Gmind Institute</strong> admin panel. If you are seeing this email, it means your SMTP configuration is working correctly!</p>'
            . '<table style="width:100%;border-collapse:collapse;margin:15px 0;">'
            . '<tr><td style="padding:8px 12px;border:1px solid #eee;font-weight:600;background:#f5f5f5;">Sent At</td><td style="padding:8px 12px;border:1px solid #eee;">' . date('Y-m-d H:i:s') . '</td></tr>'
            . '<tr><td style="padding:8px 12px;border:1px solid #eee;font-weight:600;background:#f5f5f5;">Server</td><td style="padding:8px 12px;border:1px solid #eee;">' . htmlspecialchars(php_uname('n'), ENT_QUOTES, 'UTF-8') . '</td></tr>'
            . '</table>'
            . '<p style="font-size:13px;color:#999;margin-top:20px;">This email was sent from the SMTP Settings page in the admin panel.</p>'
            . '</div>'
            . '</div>';

        $result = $mailHelper->send($testEmail, 'Test Recipient', $subject, $bodyHtml);

        if ($result) {
            flash('success', "Test email sent successfully to <strong>{$testEmail}</strong>! Please check your inbox (and spam folder).");
        } else {
            $errMsg = $mailHelper->getLastError();
            flash('error', 'Failed to send test email. Error: ' . htmlspecialchars($errMsg, ENT_QUOTES, 'UTF-8'));
        }

        redirect(BASE_PATH . '/admin/smtp-settings');
    }

    // =========================================================================
    // PRIVATE HELPER METHODS
    // =========================================================================

    /**
     * Read SMTP settings directly from the database.
     *
     * This bypasses the Mail class and reads raw values so we can
     * show the actual saved data on the form, even if Mail::getSetting()
     * has issues with empty string detection.
     *
     * @return array<string, string> Key-value pairs of SMTP settings
     */
    private function readSettingsFromDb(): array
    {
        $result = [];

        if (!$this->db) {
            return $result;
        }

        // Method 1: Read using setting_key / setting_value
        try {
            $placeholders = implode(',', array_fill(0, count(self::SMTP_KEYS), '?'));
            $stmt = $this->db->prepare(
                "SELECT `setting_key`, `setting_value` FROM `settings`
                 WHERE `setting_key` IN ({$placeholders})"
            );
            $stmt->execute(array_values(self::SMTP_KEYS));
            $rows = $stmt->fetchAll(\PDO::FETCH_ASSOC);

            foreach ($rows as $row) {
                $key = $row['setting_key'] ?? '';
                $val = $row['setting_value'] ?? '';
                if (in_array($key, self::SMTP_KEYS, true)) {
                    $result[$key] = $val;
                }
            }
        } catch (\Throwable $e) {
            error_log('AdminSmtpController::readSettingsFromDb — Method 1 failed: ' . $e->getMessage());
        }

        // Method 2: Try key_name / value columns — fill any missing or empty values
        // DO NOT return early — always check BOTH column pairs
        try {
            $placeholders = implode(',', array_fill(0, count(self::SMTP_KEYS), '?'));
            $stmt = $this->db->prepare(
                "SELECT `key_name`, `value` FROM `settings`
                 WHERE `key_name` IN ({$placeholders})"
            );
            $stmt->execute(array_values(self::SMTP_KEYS));
            $rows = $stmt->fetchAll(\PDO::FETCH_ASSOC);

            foreach ($rows as $row) {
                $key = $row['key_name'] ?? '';
                $val = $row['value'] ?? '';
                if (in_array($key, self::SMTP_KEYS, true)) {
                    // Use key_name/value only if setting_key value is missing or empty
                    if (!isset($result[$key]) || $result[$key] === '') {
                        $result[$key] = $val;
                    }
                }
            }
        } catch (\Throwable $e) {
            error_log('AdminSmtpController::readSettingsFromDb — Method 2 failed: ' . $e->getMessage());
        }

        return $result;
    }

    /**
     * Detect which columns exist in the settings table.
     *
     * @return array<string, bool> Column name => exists
     */
    private function detectColumns(): array
    {
        $columns = [
            'setting_key'   => false,
            'setting_value' => false,
            'key_name'      => false,
            'value'         => false,
        ];

        if (!$this->db) {
            return $columns;
        }

        try {
            $stmt = $this->db->query('DESCRIBE `settings`');
            $rows = $stmt->fetchAll(\PDO::FETCH_ASSOC);

            foreach ($rows as $row) {
                $field = $row['Field'] ?? '';
                if (isset($columns[$field])) {
                    $columns[$field] = true;
                }
            }
        } catch (\Throwable $e) {
            error_log('AdminSmtpController::detectColumns failed: ' . $e->getMessage());
        }

        return $columns;
    }

    /**
     * Save a setting value to the settings table.
     *
     * CRITICAL: The settings table has UNIQUE indexes on BOTH
     * `setting_key` and `key_name` columns. If we don't set key_name
     * properly, it defaults to '' which causes UNIQUE constraint
     * collisions (all rows with key_name='' overwrite each other).
     *
     * So we MUST always set BOTH key_name and setting_key to the same
     * value in every INSERT.
     *
     * @param string $key     Setting key (e.g. 'smtp_host')
     * @param string $value   Setting value
     * @param array  $columns Result of detectColumns()
     * @return bool|string    true on success, error message string on failure
     */
    private function saveSetting(string $key, string $value, array $columns): bool|string
    {
        if (!$this->db) {
            return 'Database connection not available';
        }

        $hasSettingKey   = $columns['setting_key'] ?? false;
        $hasSettingValue = $columns['setting_value'] ?? false;
        $hasKeyName      = $columns['key_name'] ?? false;
        $hasValue        = $columns['value'] ?? false;

        // ─── Strategy 1: Both column sets exist ──────────────────────
        // Use INSERT with all 4 columns (same pattern as AdminSettingsController)
        // Also UPDATE key_name in case existing row has key_name='' (legacy)
        if ($hasSettingKey && $hasSettingValue && $hasKeyName && $hasValue) {
            try {
                $stmt = $this->db->prepare(
                    'INSERT INTO `settings` (`key_name`, `value`, `setting_key`, `setting_value`)
                     VALUES (:key, :val, :key2, :val2)
                     ON DUPLICATE KEY UPDATE `setting_value` = :val3, `value` = :val4, `key_name` = :key3'
                );
                $stmt->execute([
                    ':key'  => $key,
                    ':val'  => $value,
                    ':key2' => $key,
                    ':val2' => $value,
                    ':val3' => $value,
                    ':val4' => $value,
                    ':key3' => $key,
                ]);
                return true;
            } catch (\Throwable $e) {
                error_log("AdminSmtpController::saveSetting — Strategy 1 failed for {$key}: " . $e->getMessage());
                // Fall through to next strategy
            }
        }

        // ─── Strategy 2: Only setting_key / setting_value exist ──────
        if ($hasSettingKey && $hasSettingValue) {
            try {
                $stmt = $this->db->prepare(
                    'INSERT INTO `settings` (`setting_key`, `setting_value`)
                     VALUES (:key, :val)
                     ON DUPLICATE KEY UPDATE `setting_value` = :val2'
                );
                $stmt->execute([
                    ':key'  => $key,
                    ':val'  => $value,
                    ':val2' => $value,
                ]);
                return true;
            } catch (\Throwable $e) {
                error_log("AdminSmtpController::saveSetting — Strategy 2 failed for {$key}: " . $e->getMessage());
            }
        }

        // ─── Strategy 3: Only key_name / value exist ────────────────
        if ($hasKeyName && $hasValue) {
            try {
                $stmt = $this->db->prepare(
                    'INSERT INTO `settings` (`key_name`, `value`)
                     VALUES (:key, :val)
                     ON DUPLICATE KEY UPDATE `value` = :val2'
                );
                $stmt->execute([
                    ':key'  => $key,
                    ':val'  => $value,
                    ':val2' => $value,
                ]);
                return true;
            } catch (\Throwable $e) {
                error_log("AdminSmtpController::saveSetting — Strategy 3 failed for {$key}: " . $e->getMessage());
            }
        }

        // ─── Strategy 4: Brute force — try all combinations ──────────
        // This handles cases where DESCRIBE failed or gave wrong results
        $strategies = [
            // Dual columns (also fix key_name on update)
            'INSERT INTO `settings` (`key_name`, `value`, `setting_key`, `setting_value`) VALUES (:k, :v, :k2, :v2) ON DUPLICATE KEY UPDATE `setting_value` = :v3, `value` = :v4, `key_name` = :k3',
            // setting_key only
            'INSERT INTO `settings` (`setting_key`, `setting_value`) VALUES (:k, :v) ON DUPLICATE KEY UPDATE `setting_value` = :v2',
            // key_name only
            'INSERT INTO `settings` (`key_name`, `value`) VALUES (:k, :v) ON DUPLICATE KEY UPDATE `value` = :v2',
        ];

        foreach ($strategies as $sql) {
            try {
                $stmt = $this->db->prepare($sql);
                $params = [
                    ':k'  => $key,
                    ':v'  => $value,
                    ':k2' => $key,
                    ':v2' => $value,
                    ':v3' => $value,
                    ':v4' => $value,
                    ':k3' => $key,
                ];
                // Remove extra params that don't exist in the SQL
                $requiredParams = [];
                preg_match_all('/:([a-z0-9_]+)/i', $sql, $matches);
                foreach ($matches[1] as $paramName) {
                    $pdoKey = ':' . $paramName;
                    if (isset($params[$pdoKey])) {
                        $requiredParams[$pdoKey] = $params[$pdoKey];
                    }
                }
                $stmt->execute($requiredParams);
                return true;
            } catch (\Throwable $e) {
                // Try next strategy
                error_log("AdminSmtpController::saveSetting — Brute force strategy failed for {$key}: " . $e->getMessage());
            }
        }

        return "All save strategies failed for {$key}";
    }
}
