<?php

namespace App\Http\Controllers\Installer;

use App\Core\App;
use App\Core\Controller;
use App\Core\Database;
use App\Core\Request;
use App\Core\Response;
use App\Core\Translator;
use App\Database\Migrator;

class InstallerController extends Controller
{
    private array $steps = [
        1 => 'welcome',
        2 => 'requirements',
        3 => 'database',
        4 => 'tables',
        5 => 'admin',
        6 => 'site',
        7 => 'complete',
    ];

    public function index(Request $request, array $params = []): void
    {
        if ($this->app->isInstalled()) {
            Response::redirect('/');
            return;
        }
        Response::redirect('/installer/step/1');
    }

    /**
     * Handle language selection from installer step 1.
     * Sets locale in session and reloads step 1.
     */
    public function setLanguage(Request $request, array $params = []): void
    {
        $this->validateCsrf();
        $language = $request->input('language', 'de');
        if (Translator::isSupported($language)) {
            $this->session()->set('locale', $language);
            $this->session()->set('installer_language', $language);
            // Update translator locale immediately
            if ($this->app->hasService('translator')) {
                $this->app->service('translator')->setLocale($language);
                $this->app->service('translator')->clearCache();
            }
        }
        Response::redirect('/installer/step/1');
    }

    public function step(Request $request, array $params = []): void
    {
        $step = (int) ($params['step'] ?? 1);

        if (!isset($this->steps[$step])) {
            Response::redirect('/installer/step/1');
            return;
        }

        // Steps 1–6: redirect if already installed.
        // Step 7 (completion page): always render — it writes the installed flag on display
        // so the user sees the success message before the installer is locked.
        if ($step !== 7 && $this->app->isInstalled()) {
            Response::redirect('/');
            return;
        }

        $data = [
            'step'       => $step,
            'totalSteps' => count($this->steps),
            'stepName'   => $this->steps[$step],
            'steps'      => $this->steps,
            'csrf'       => $this->csrf(),
        ];

        // Step-specific data preparation
        switch ($step) {
            case 2:
                $data['checks']    = $this->checkRequirements();
                $data['allPassed'] = !in_array(false, array_column($data['checks'], 'passed'));
                break;
            case 3:
                $data['dbConfig'] = $this->session()->get('installer_db', [
                    'host' => '127.0.0.1', 'port' => '3306',
                    'name' => '', 'user' => '', 'password' => '', 'prefix' => 'iw_',
                ]);
                $data['error'] = $this->session()->getFlash('db_error');
                break;
            case 5:
                $data['error'] = $this->session()->getFlash('admin_error');
                break;
            case 6:
                $data['themes']    = $this->getAvailableThemes();
                $data['error']     = $this->session()->getFlash('seed_error');
                // Repopulate form values if the user is returning after a seed error
                $data['siteConfig'] = $this->session()->get('installer_site', []);
                // Clear saved site config from session once repopulated in view
                $this->session()->remove('installer_site');
                break;
            case 7:
                // Write the installed flag when the completion page is displayed.
                // This ensures step 7 is reachable after step 6 finishes.
                if (!$this->app->isInstalled()) {
                    file_put_contents(
                        $this->app->basePath('storage/.installed'),
                        date('Y-m-d H:i:s')
                    );
                    $this->installerLog('Step 7: Installation completed – .installed flag written');
                }
                // Clear installer session data (keep locale for admin)
                $this->session()->remove('installer_db');
                $this->session()->remove('installer_admin');
                $this->session()->remove('installer_site');
                break;
        }

        $view = $this->app->service('view');
        $html = $view->renderInstaller('step_' . $step, $data);
        Response::html($html);
    }

    public function processStep(Request $request, array $params = []): void
    {
        if ($this->app->isInstalled()) {
            Response::redirect('/');
            return;
        }

        $this->validateCsrf();

        $step = (int) ($params['step'] ?? 1);

        switch ($step) {
            case 3:
                $this->processDatabaseStep($request);
                break;
            case 4:
                $this->processTablesStep($request);
                break;
            case 5:
                $this->processAdminStep($request);
                break;
            case 6:
                $this->processSiteStep($request);
                break;
            default:
                Response::redirect('/installer/step/' . ($step + 1));
        }
    }

    // ------------------------------------------------------------------
    // Private helpers
    // ------------------------------------------------------------------

    private function checkRequirements(): array
    {
        $basePath = $this->app->basePath();
        $loaded  = __('installer.req_loaded');
        $missing = __('installer.req_missing');
        $writable    = __('installer.req_writable');
        $notWritable = __('installer.req_not_writable');

        return [
            ['label' => __('installer.req_php_version'),       'passed' => version_compare(PHP_VERSION, '8.1.0', '>='), 'value' => PHP_VERSION],
            ['label' => __('installer.req_pdo_mysql'),         'passed' => extension_loaded('pdo_mysql'),  'value' => extension_loaded('pdo_mysql')  ? $loaded : $missing],
            ['label' => __('installer.req_mbstring'),          'passed' => extension_loaded('mbstring'),   'value' => extension_loaded('mbstring')   ? $loaded : $missing],
            ['label' => __('installer.req_gd'),                'passed' => extension_loaded('gd'),         'value' => extension_loaded('gd')         ? $loaded : $missing],
            ['label' => __('installer.req_json'),              'passed' => extension_loaded('json'),       'value' => extension_loaded('json')       ? $loaded : $missing],
            ['label' => __('installer.req_fileinfo'),          'passed' => extension_loaded('fileinfo'),   'value' => extension_loaded('fileinfo')   ? $loaded : $missing],
            ['label' => __('installer.req_storage_writable'),  'passed' => is_writable($basePath . '/storage'),        'value' => is_writable($basePath . '/storage')        ? $writable : $notWritable],
            ['label' => __('installer.req_uploads_writable'),  'passed' => is_writable($basePath . '/public/uploads'), 'value' => is_writable($basePath . '/public/uploads') ? $writable : $notWritable],
            ['label' => __('installer.req_config_writable'),   'passed' => is_writable($basePath . '/app/Config'),     'value' => is_writable($basePath . '/app/Config')     ? $writable : $notWritable],
        ];
    }

    private function processDatabaseStep(Request $request): void
    {
        $config = [
            'host'     => trim($request->input('db_host', '127.0.0.1')),
            'port'     => trim($request->input('db_port', '3306')),
            'name'     => trim($request->input('db_name', '')),
            'user'     => trim($request->input('db_user', '')),
            'password' => $request->input('db_password', ''),
            'prefix'   => trim($request->input('db_prefix', 'iw_')),
        ];

        $this->session()->set('installer_db', $config);

        if (!Database::testConnection($config)) {
            $this->installerLog('Step 3: Database connection test FAILED for host=' . $config['host']);
            $this->session()->flash('db_error', __('installer.db_error'));
            Response::redirect('/installer/step/3');
            return;
        }

        $this->installerLog('Step 3: Database connection OK (host=' . $config['host'] . ', db=' . $config['name'] . ')');

        // Write database config file with restricted permissions
        $dbConfigPath = $this->app->basePath('app/Config/database.php');
        $configContent = "<?php\n\n// Generated by ITSWEBER CMS Installer\nreturn " . var_export($config, true) . ";\n";
        file_put_contents($dbConfigPath, $configContent);
        if (PHP_OS_FAMILY !== 'Windows') {
            @chmod($dbConfigPath, 0600);
        }

        Response::redirect('/installer/step/4');
    }

    private function processTablesStep(Request $request): void
    {
        try {
            $dbConfig = require $this->app->basePath('app/Config/database.php');
            $db       = new Database($dbConfig);
            $migrator = new Migrator($db, $this->app->basePath('database/migrations'));
            $migrator->runAll();
            $this->installerLog('Step 4: Database tables created successfully');
            Response::redirect('/installer/step/5');
        } catch (\Throwable $e) {
            $this->installerLog('Step 4: Table creation FAILED: ' . $e->getMessage());
            $this->session()->flash('db_error', __('installer.tables_error', [':error' => $e->getMessage()]));
            Response::redirect('/installer/step/3');
        }
    }

    private function processAdminStep(Request $request): void
    {
        $username       = trim($request->input('admin_username', ''));
        $email          = trim($request->input('admin_email', ''));
        $password       = $request->input('admin_password', '');
        $passwordConfirm = $request->input('admin_password_confirm', '');

        if (empty($username) || empty($email) || empty($password)) {
            $this->session()->flash('admin_error', __('installer.admin_error_required'));
            Response::redirect('/installer/step/5');
            return;
        }
        if (!filter_var($email, FILTER_VALIDATE_EMAIL)) {
            $this->session()->flash('admin_error', __('installer.admin_error_invalid_email'));
            Response::redirect('/installer/step/5');
            return;
        }
        if ($password !== $passwordConfirm) {
            $this->session()->flash('admin_error', __('installer.admin_error_mismatch'));
            Response::redirect('/installer/step/5');
            return;
        }
        if (strlen($password) < \App\Core\Auth::MIN_PASSWORD_LENGTH) {
            $this->session()->flash('admin_error', __('installer.admin_error_short'));
            Response::redirect('/installer/step/5');
            return;
        }

        try {
            $dbConfig = require $this->app->basePath('app/Config/database.php');
            $db       = new Database($dbConfig);
            $db->insert('iw_users', [
                'username'      => $username,
                'email'         => $email,
                'password_hash' => password_hash($password, PASSWORD_DEFAULT),
                'role'          => 'admin',
                'is_active'     => 1,
                'created_at'    => date('Y-m-d H:i:s'),
                'updated_at'    => date('Y-m-d H:i:s'),
            ]);
            $this->installerLog('Step 5: Admin user created (username=' . $username . ', email=' . $email . ')');
            Response::redirect('/installer/step/6');
        } catch (\Throwable $e) {
            $this->installerLog('Step 5: Admin user creation FAILED: ' . $e->getMessage());
            $this->session()->flash('admin_error', __('installer.admin_error_failed', [':error' => $e->getMessage()]));
            Response::redirect('/installer/step/5');
        }
    }

    private function processSiteStep(Request $request): void
    {
        $allowedTransports  = ['mail', 'smtp'];
        $allowedEncryptions = ['tls', 'ssl', 'none'];

        $siteConfig = [
            'site_name'     => trim($request->input('site_name', 'My Website')),
            'site_tagline'  => trim($request->input('site_tagline', '')),
            'contact_email' => trim($request->input('contact_email', '')),
            'theme'         => trim($request->input('theme', 'default')),

            // Mail config – also preserved for form repopulation on error
            'mail_transport'    => in_array($request->input('mail_transport', 'mail'), $allowedTransports)
                                   ? $request->input('mail_transport', 'mail') : 'mail',
            'mail_from_address' => trim($request->input('mail_from_address', '')),
            'mail_from_name'    => trim($request->input('mail_from_name', '')),
            'smtp_host'         => trim($request->input('smtp_host', '')),
            'smtp_port'         => trim($request->input('smtp_port', '587')),
            'smtp_username'     => trim($request->input('smtp_username', '')),
            'smtp_password'     => $request->input('smtp_password', ''),
            'smtp_encryption'   => in_array($request->input('smtp_encryption', 'tls'), $allowedEncryptions)
                                   ? $request->input('smtp_encryption', 'tls') : 'tls',
        ];

        // Validate email fields if non-empty
        if (!empty($siteConfig['contact_email']) && !filter_var($siteConfig['contact_email'], FILTER_VALIDATE_EMAIL)) {
            $this->session()->flash('seed_error', __('installer.site_error_invalid_email'));
            $this->session()->set('installer_site', $siteConfig);
            Response::redirect('/installer/step/6');
            return;
        }
        if (!empty($siteConfig['mail_from_address']) && !filter_var($siteConfig['mail_from_address'], FILTER_VALIDATE_EMAIL)) {
            $this->session()->flash('seed_error', __('installer.site_error_invalid_from_email'));
            $this->session()->set('installer_site', $siteConfig);
            Response::redirect('/installer/step/6');
            return;
        }

        // Validate SMTP fields when SMTP transport is selected
        if ($siteConfig['mail_transport'] === 'smtp' && empty($siteConfig['smtp_host'])) {
            $this->session()->flash('seed_error', __('installer.site_error_smtp_host_required') ?: 'SMTP host is required when using SMTP transport.');
            $this->session()->set('installer_site', $siteConfig);
            Response::redirect('/installer/step/6');
            return;
        }
        if ($siteConfig['mail_transport'] === 'smtp') {
            $port = (int) $siteConfig['smtp_port'];
            if ($port < 1 || $port > 65535) {
                $siteConfig['smtp_port'] = '587'; // fallback to safe default
            }
        }

        // Validate theme exists
        $themePath = $this->app->basePath('themes/' . $siteConfig['theme']);
        if (!is_dir($themePath) || !file_exists($themePath . '/theme.json')) {
            $siteConfig['theme'] = 'default'; // Fallback to default
        }

        // Determine installation language from session
        $installerLanguage = $this->session()->get('installer_language', 'de');
        if (!Translator::isSupported($installerLanguage)) {
            $installerLanguage = 'de';
        }

        try {
            $dbConfig = require $this->app->basePath('app/Config/database.php');
            $db       = new Database($dbConfig);

            require_once $this->app->basePath('database/seeds/default_data.php');
            seedDefaultData($db, $siteConfig, $installerLanguage);

            // Starter / demo content – only on fresh installs, never during updates
            require_once $this->app->basePath('database/seeds/starter_content.php');
            seedStarterContent($db, $this->app->basePath(), $siteConfig, $installerLanguage);

            // Persist system language in settings
            $db->insert('iw_settings', [
                'setting_key'   => 'system_language',
                'setting_value' => $installerLanguage,
                'setting_group' => 'general',
            ]);

            // Write app/Config/app.php with installer-provided mail settings and locale
            $this->writeAppConfig($siteConfig, $installerLanguage);

            $this->installerLog('Step 6: Site configured (name=' . $siteConfig['site_name'] . ', theme=' . $siteConfig['theme'] . ', lang=' . $installerLanguage . ')');

            // Do NOT write .installed here.
            // The installed flag is written when step 7 is displayed,
            // ensuring the completion page is always reachable after step 6.
            Response::redirect('/installer/step/7');
        } catch (\Throwable $e) {
            $this->installerLog('Step 6: Site configuration FAILED: ' . $e->getMessage());
            $this->session()->flash('seed_error', __('installer.site_error', [':error' => $e->getMessage()]));
            // Preserve form values so the user doesn't have to retype everything
            $this->session()->set('installer_site', $siteConfig);
            Response::redirect('/installer/step/6');
        }
    }

    /**
     * Write app/Config/app.php with the mail settings collected during installation.
     * All other config values are kept at their defaults.
     */
    private function writeAppConfig(array $siteConfig, string $locale = 'de'): void
    {
        $fromAddress = $siteConfig['mail_from_address'] ?: ($siteConfig['contact_email'] ?: 'noreply@example.com');
        $fromName    = $siteConfig['mail_from_name']    ?: ($siteConfig['site_name']      ?: 'ITSWEBER CMS');
        $transport   = $siteConfig['mail_transport']   ?? 'mail';
        $smtpHost    = $siteConfig['smtp_host']        ?? '';
        $smtpPort    = (int) ($siteConfig['smtp_port'] ?: 587);
        $smtpUser    = $siteConfig['smtp_username']    ?? '';
        $smtpPass    = $siteConfig['smtp_password']    ?? '';
        $smtpEnc     = $siteConfig['smtp_encryption']  ?? 'tls';

        $mailConfig = [
            'transport'       => $transport,
            'from_address'    => $fromAddress,
            'from_name'       => $fromName,
            'reply_to'        => '',
            'smtp_host'       => $smtpHost,
            'smtp_port'       => $smtpPort,
            'smtp_username'   => $smtpUser,
            'smtp_password'   => $smtpPass,
            'smtp_encryption' => $smtpEnc,
        ];

        $mailExport  = var_export($mailConfig, true);
        $generatedAt = date('Y-m-d H:i:s');
        $localeStr   = var_export($locale, true);

        $content = <<<PHP
<?php

/**
 * ITSWEBER CMS - Application Configuration
 * Updated by installer on {$generatedAt}
 */
return [
    'name'     => 'ITSWEBER CMS',
    'version'  => '1.0.0',
    'debug'    => false,
    'timezone' => 'Europe/Berlin',
    'locale'   => {$localeStr},
    'charset'  => 'UTF-8',

    // URL settings
    'base_url' => '',  // Set manually if needed (e.g. 'https://example.com')

    // Upload settings
    'upload' => [
        'max_size'      => 10 * 1024 * 1024,
        'allowed_types' => ['jpg', 'jpeg', 'png', 'gif', 'webp', 'svg', 'pdf', 'doc', 'docx'],
        'image_types'   => ['jpg', 'jpeg', 'png', 'gif', 'webp', 'svg'],
        'upload_path'   => 'public/uploads',
    ],

    // Session settings
    'session' => [
        'lifetime' => 7200,
        'name'     => 'iw_session',
    ],

    // Mail settings (used by App\\Services\\Mailer)
    // transport: 'mail' = PHP mail(), 'smtp' = direct SMTP connection
    // smtp_encryption: 'tls' (STARTTLS/587), 'ssl' (implicit/465), 'none' (plain/25)
    'mail' => {$mailExport},
];
PHP;

        $appConfigPath = $this->app->basePath('app/Config/app.php');
        file_put_contents($appConfigPath, $content);
        if (PHP_OS_FAMILY !== 'Windows') {
            @chmod($appConfigPath, 0644);
        }
    }

    /**
     * AJAX endpoint: send a test e-mail using the settings POSTed from Step 6.
     * Returns JSON {"ok": true|false, "message": "..."}.
     * Only available while the installer is active (not yet installed).
     */
    public function testMail(Request $request, array $params = []): void
    {
        header('Content-Type: application/json; charset=UTF-8');

        if ($this->app->isInstalled()) {
            echo json_encode(['ok' => false, 'message' => 'Installer is already locked.']);
            exit;
        }

        // Rate-limit test mails: max 5 per hour per IP
        $limiter = new \App\Core\RateLimiter(
            $this->app->basePath('storage/cache'),
            5,    // max attempts
            3600, // lockout: 1 hour
            3600  // window: 1 hour
        );
        $ip = $_SERVER['REMOTE_ADDR'] ?? 'unknown';
        if ($limiter->isLocked('installer_test_mail_' . $ip)) {
            echo json_encode(['ok' => false, 'message' => __('installer.test_mail_rate_limited') ?: 'Too many test mails. Please wait before trying again.']);
            exit;
        }

        $allowedTransports  = ['mail', 'smtp'];
        $allowedEncryptions = ['tls', 'ssl', 'none'];

        $recipient  = trim($request->input('test_recipient', ''));
        $transport  = $request->input('mail_transport', 'mail');
        $fromAddr   = trim($request->input('mail_from_address', '')) ?: 'noreply@example.com';
        $fromName   = trim($request->input('mail_from_name', '')) ?: 'ITSWEBER CMS Installer';
        $smtpHost   = trim($request->input('smtp_host', ''));
        $smtpPort   = (int) ($request->input('smtp_port', 587) ?: 587);
        $smtpUser   = trim($request->input('smtp_username', ''));
        $smtpPass   = $request->input('smtp_password', '');
        $smtpEnc    = $request->input('smtp_encryption', 'tls');

        if (!in_array($transport, $allowedTransports, true)) {
            $transport = 'mail';
        }
        if (!in_array($smtpEnc, $allowedEncryptions, true)) {
            $smtpEnc = 'tls';
        }

        if (empty($recipient) || !filter_var($recipient, FILTER_VALIDATE_EMAIL)) {
            echo json_encode(['ok' => false, 'message' => __('installer.test_mail_invalid_email')]);
            exit;
        }

        if ($transport === 'smtp' && empty($smtpHost)) {
            echo json_encode(['ok' => false, 'message' => __('installer.test_mail_smtp_required')]);
            exit;
        }

        $config = [
            'transport'       => $transport,
            'from_address'    => $fromAddr,
            'from_name'       => $fromName,
            'reply_to'        => '',
            'smtp_host'       => $smtpHost,
            'smtp_port'       => $smtpPort,
            'smtp_username'   => $smtpUser,
            'smtp_password'   => $smtpPass,
            'smtp_encryption' => $smtpEnc,
        ];

        try {
            $mailer  = new \App\Services\Mailer($config);
            $subject = 'ITSWEBER CMS – Mail Configuration Test';
            $body    = "This is a test message sent from the ITSWEBER CMS Installer.\r\n\r\n"
                     . "If you received this e-mail, your mail configuration is working correctly.\r\n\r\n"
                     . 'Transport: ' . $transport . "\r\n"
                     . 'From: ' . $fromAddr;

            $ok = $mailer->send($recipient, $subject, $body);
            $limiter->recordFailure('installer_test_mail_' . $ip); // count every send attempt

            if ($ok) {
                echo json_encode([
                    'ok'      => true,
                    'message' => __('installer.test_mail_sent_to', [':recipient' => htmlspecialchars($recipient, ENT_QUOTES, 'UTF-8')]),
                ]);
            } else {
                echo json_encode([
                    'ok'      => false,
                    'message' => __('installer.test_mail_failed'),
                ]);
            }
        } catch (\Throwable $e) {
            echo json_encode([
                'ok'      => false,
                'message' => __('installer.test_mail_error_prefix') . htmlspecialchars($e->getMessage(), ENT_QUOTES, 'UTF-8'),
            ]);
        }

        exit;
    }

    private function getAvailableThemes(): array
    {
        $themes = [];
        foreach (glob($this->app->basePath('themes') . '/*/theme.json') as $file) {
            $data = json_decode(file_get_contents($file), true);
            if ($data) {
                $themes[] = $data;
            }
        }
        return $themes;
    }

    /**
     * Write a timestamped entry to the installer log file.
     * The log is stored in storage/logs/installer.log for audit purposes.
     */
    private function installerLog(string $message): void
    {
        $logDir = $this->app->basePath('storage/logs');
        if (!is_dir($logDir)) {
            @mkdir($logDir, 0755, true);
        }
        $line = '[' . date('Y-m-d H:i:s') . '] ' . $message . "\n";
        @file_put_contents($logDir . '/installer.log', $line, FILE_APPEND | LOCK_EX);
    }
}
