<?php

namespace App\Controllers;

use CodeIgniter\Controller;
use CodeIgniter\Database\Seeder;
use Config\Database;
use Throwable;

/**
 * One-time browser-based installer. Exists because some XAMPP/Windows
 * setups have a broken or misconfigured CLI php.exe (wrong extension_dir,
 * missing DLLs, etc.) even though the *same* PHP running inside Apache
 * (mod_php) works fine for serving the app. This lets you migrate + seed
 * using that already-working Apache PHP instead of the CLI copy.
 *
 * Deliberately extends the framework's bare Controller, not
 * App\Controllers\BaseController — BaseController initializes the
 * session service, which (with the DatabaseHandler this project uses)
 * requires the sessions table to already exist. This controller has to
 * work against a completely empty database.
 *
 * SECURITY: protected by a token (set INSTALL_TOKEN in .env) and refuses
 * to run again once writable/install.lock exists. Delete this controller
 * (or at least the route) once you're done — see docs/00-xampp-quickstart.md.
 */
class Install extends Controller
{
    private const LOCK_FILE = WRITEPATH . 'install.lock';

    public function index()
    {
        if (file_exists(self::LOCK_FILE)) {
            return $this->response->setStatusCode(403)->setBody(
                $this->wrap('Already installed', '<p>Installation already completed. Delete <code>writable/install.lock</code> if you really need to run this again.</p>')
            );
        }

        $token       = $this->request->getGet('token');
        $expected    = env('INSTALL_TOKEN');
        $confirmStep = $this->request->getGet('confirm');

        if (empty($expected)) {
            return $this->response->setStatusCode(500)->setBody($this->wrap(
                'Not configured',
                '<p>Add <code>INSTALL_TOKEN = some-random-string</code> to your <code>.env</code> file first, then reload this page with <code>?token=that-string</code>.</p>'
            ));
        }

        if (! hash_equals((string) $expected, (string) $token)) {
            return $this->response->setStatusCode(403)->setBody($this->wrap('Forbidden', '<p>Missing or incorrect token.</p>'));
        }

        if ($confirmStep !== 'yes') {
            $confirmUrl = current_url() . '?token=' . urlencode($token) . '&confirm=yes';

            return $this->response->setBody($this->wrap(
                'Confirm installation',
                '<p>This will run all pending database migrations and seed the default '
                . 'Administrator account, Role, Branch, and Department.</p>'
                . '<p>Make sure you have already created the <code>erp_foundation</code> '
                . 'database (or whatever you named it in <code>.env</code>) in phpMyAdmin.</p>'
                . '<p><a class="btn" href="' . esc($confirmUrl) . '">Yes, run migrations &amp; seed now</a></p>'
            ));
        }

        $log = [];

        // --- Migrations -----------------------------------------------
        try {
            $runner = service('migrations');
            $runner->setNamespace(null); // mirrors `spark migrate --all`
            $ok = $runner->latest();
            $log[] = $ok !== false
                ? '✅ Migrations ran successfully.'
                : '⚠️ Migration runner returned false — check writable/logs.';
        } catch (Throwable $e) {
            return $this->response->setStatusCode(500)->setBody($this->wrap(
                'Migration failed',
                '<pre>' . esc($e->getMessage()) . "\n\n" . esc($e->getTraceAsString()) . '</pre>'
            ));
        }

        // --- Seeding -----------------------------------------------------
        try {
            ob_start();
            $seeder = new Seeder(new Database());
            $seeder->call('DatabaseSeeder');
            $seedOutput = ob_get_clean();
            $log[] = '✅ Seeding completed.';
            if ($seedOutput) {
                $log[] = '<pre>' . esc($seedOutput) . '</pre>';
            }
        } catch (Throwable $e) {
            ob_end_clean();

            return $this->response->setStatusCode(500)->setBody($this->wrap(
                'Seeding failed',
                '<p>Migrations succeeded, but seeding failed:</p><pre>' . esc($e->getMessage()) . '</pre>'
                . '<p>If this is because data already exists (re-running after a partial success), '
                . 'that may be fine — check your <code>users</code> table in phpMyAdmin.</p>'
            ));
        }

        // Lock so this can't be run again accidentally.
        if (! is_dir(WRITEPATH)) {
            mkdir(WRITEPATH, 0755, true);
        }
        file_put_contents(self::LOCK_FILE, 'Installed at ' . date('Y-m-d H:i:s') . "\n");

        $log[] = '<p><strong>Copy the admin password shown above (in the seeding output) '
            . 'now — it will not be shown again.</strong></p>';
        $log[] = '<p><a class="btn" href="' . base_url('login') . '">Go to login →</a></p>';

        return $this->response->setBody($this->wrap('Installation complete', implode("\n", $log)));
    }

    private function wrap(string $title, string $body): string
    {
        return '<!DOCTYPE html><html><head><meta charset="utf-8"><title>' . esc($title) . '</title>'
            . '<style>body{font-family:system-ui,sans-serif;max-width:640px;margin:60px auto;padding:0 20px;line-height:1.6;color:#1f2430}'
            . 'pre{background:#f4f6fb;padding:12px;border-radius:8px;overflow:auto;font-size:13px}'
            . 'code{background:#f4f6fb;padding:2px 5px;border-radius:4px}'
            . '.btn{display:inline-block;background:#4f46e5;color:#fff;padding:10px 18px;border-radius:8px;text-decoration:none;margin-top:10px}'
            . 'h1{font-size:1.4rem}</style></head><body>'
            . '<h1>' . esc($title) . '</h1>' . $body . '</body></html>';
    }
}
