<?php

namespace App\Controllers;

use App\Controllers\BaseController;
use CodeIgniter\Exceptions\PageNotFoundException;
use PDO;
use PDOException;

class Install extends BaseController
{
    protected $lockFile;
    protected $envFile;

    public function __construct()
    {
        $this->lockFile = WRITEPATH . 'installed.lock';
        $this->envFile  = ROOTPATH . '.env';
    }

    /**
     * Checks if the application is already installed.
     * Block installer access if installed.lock exists.
     */
    private function checkInstalled()
    {
        if (file_exists($this->lockFile)) {
            throw PageNotFoundException::forPageNotFound("Installer has been locked for security. If you need to re-run, delete writable/installed.lock");
        }
    }

    /**
     * Installer landing page
     */
    public function index()
    {
        $this->checkInstalled();

        // 1. Check requirements
        $requirements = [
            'php_version' => [
                'name' => 'PHP Version >= 8.2',
                'met'  => version_compare(PHP_VERSION, '8.2.0', '>='),
                'current' => PHP_VERSION
            ],
            'pdo_mysql' => [
                'name' => 'PDO MySQL Extension',
                'met'  => extension_loaded('pdo_mysql'),
                'current' => extension_loaded('pdo_mysql') ? 'Enabled' : 'Disabled'
            ],
            'zip' => [
                'name' => 'ZipArchive Extension',
                'met'  => class_exists('ZipArchive'),
                'current' => class_exists('ZipArchive') ? 'Enabled' : 'Disabled'
            ],
            'curl' => [
                'name' => 'cURL Extension',
                'met'  => extension_loaded('curl'),
                'current' => extension_loaded('curl') ? 'Enabled' : 'Disabled'
            ],
            'writable_dir' => [
                'name' => 'Writable Directory Permission',
                'met'  => is_writable(WRITEPATH),
                'current' => is_writable(WRITEPATH) ? 'Writable' : 'Not Writable'
            ],
            'env_writable' => [
                'name' => 'Root Directory Permission (for .env)',
                'met'  => is_writable(ROOTPATH),
                'current' => is_writable(ROOTPATH) ? 'Writable' : 'Not Writable'
            ]
        ];

        $allMet = true;
        foreach ($requirements as $req) {
            if (!$req['met']) {
                $allMet = false;
                break;
            }
        }

        return view('install/index', [
            'requirements' => $requirements,
            'allMet'       => $allMet
        ]);
    }

    /**
     * Process database configuration, migration, seeding and admin setup
     */
    public function process()
    {
        $this->checkInstalled();

        $rules = [
            'db_host'          => 'required',
            'db_name'          => 'required',
            'db_user'          => 'required',
            'school_name'      => 'required|min_length[5]',
            'school_type'      => 'required',
            'accreditation'    => 'required',
            'admin_email'      => 'required|valid_email',
            'admin_password'   => 'required|min_length[8]',
        ];

        if (!$this->validate($rules)) {
            return $this->response->setJSON([
                'success' => false,
                'message' => 'Validasi gagal. Periksa data inputan Anda.',
                'errors'  => $this->validator->getErrors()
            ]);
        }

        $dbHost     = $this->request->getPost('db_host');
        $dbPort     = $this->request->getPost('db_port') ?: '3306';
        $dbName     = $this->request->getPost('db_name');
        $dbUser     = $this->request->getPost('db_user');
        $dbPass     = $this->request->getPost('db_pass') ?? '';
        
        $schoolName = $this->request->getPost('school_name');
        $schoolType = $this->request->getPost('school_type');
        $accreditation = $this->request->getPost('accreditation');

        $adminEmail = $this->request->getPost('admin_email');
        $adminPass  = $this->request->getPost('admin_password');

        // 1. Test Database Connection via PDO
        try {
            $dsn = "mysql:host={$dbHost};port={$dbPort};dbname={$dbName};charset=utf8mb4";
            $pdo = new PDO($dsn, $dbUser, $dbPass, [
                PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
                PDO::ATTR_TIMEOUT => 5
            ]);
        } catch (PDOException $e) {
            return $this->response->setJSON([
                'success' => false,
                'message' => 'Gagal terhubung ke database: ' . $e->getMessage()
            ]);
        }

        // 2. Write configuration to .env file
        // Check for .env, env (without dot), or .env.example templates
        $envContent = "";
        $envTemplatePath = ROOTPATH . 'env';
        $envExamplePath  = ROOTPATH . '.env.example';

        if (file_exists($this->envFile)) {
            $envContent = file_get_contents($this->envFile);
        } elseif (file_exists($envTemplatePath)) {
            $envContent = file_get_contents($envTemplatePath);
        } elseif (file_exists($envExamplePath)) {
            $envContent = file_get_contents($envExamplePath);
        } else {
            $envContent = "CI_ENVIRONMENT = production\n";
        }

        // Helper function inline to set/append env variables
        $setEnv = function (&$content, $key, $value) {
            $pattern = '/#?\s*' . preg_quote($key, '/') . '\s*=\s*.*/';
            if (preg_match($pattern, $content)) {
                $content = preg_replace($pattern, "{$key} = '{$value}'", $content);
            } else {
                $content .= "\n{$key} = '{$value}'";
            }
        };

        // Dynamically detect and write app.baseURL to .env
        $scheme = (isset($_SERVER['HTTPS']) && $_SERVER['HTTPS'] === 'on') ? 'https' : 'http';
        $baseUrl = "{$scheme}://{$_SERVER['HTTP_HOST']}/";

        // Set or append all variables
        $setEnv($envContent, 'CI_ENVIRONMENT', 'production');
        $setEnv($envContent, 'app.baseURL', $baseUrl);
        $setEnv($envContent, 'database.default.hostname', $dbHost);
        $setEnv($envContent, 'database.default.database', $dbName);
        $setEnv($envContent, 'database.default.username', $dbUser);
        $setEnv($envContent, 'database.default.password', $dbPass);
        $setEnv($envContent, 'database.default.port', $dbPort);
        $setEnv($envContent, 'database.default.DBDriver', 'MySQLi');

        // Write updated .env file
        if (file_put_contents($this->envFile, $envContent) === false) {
            return $this->response->setJSON([
                'success' => false,
                'message' => 'Gagal menulis file konfigurasi .env. Pastikan hak akses root folder writable.'
            ]);
        }

        // Override the database configuration in memory for the current request execution
        $dbConfig = config('Database');
        $dbConfig->default['hostname'] = $dbHost;
        $dbConfig->default['database'] = $dbName;
        $dbConfig->default['username'] = $dbUser;
        $dbConfig->default['password'] = $dbPass;
        $dbConfig->default['port']     = (int)$dbPort;

        // Force reconnect using the modified configuration in memory
        \Config\Database::connect('default', false); 

        // 3. Run migrations programmatically
        try {
            $migrations = \Config\Services::migrations();
            $migrations->latest();
        } catch (\Throwable $e) {
            return $this->response->setJSON([
                'success' => false,
                'message' => 'Gagal menjalankan migrasi database: ' . $e->getMessage()
            ]);
        }

        // 4. Run seeders programmatically
        try {
            $seeder = \Config\Database::seeder();
            $seeder->call('ReleaseSeeder');
        } catch (\Throwable $e) {
            return $this->response->setJSON([
                'success' => false,
                'message' => 'Gagal memasukkan data bawaan (seeder): ' . $e->getMessage()
            ]);
        }

        // 5. Update initial superadmin account and school settings in the database
        try {
            $dbConnection = \Config\Database::connect();
            
            // Update Superadmin Account
            $dbConnection->table('users')
                ->where('role', 'superadmin')
                ->update([
                    'username'   => $adminEmail,
                    'email'      => $adminEmail,
                    'password'   => password_hash($adminPass, PASSWORD_DEFAULT),
                    'updated_at' => date('Y-m-d H:i:s')
                ]);

            // Update School Settings
            $dbConnection->table('school_settings')
                ->where('id', 1)
                ->update([
                    'school_name'   => $schoolName,
                    'school_type'   => $schoolType,
                    'accreditation' => $accreditation,
                    'email'         => $adminEmail,
                    'updated_at'    => date('Y-m-d H:i:s')
                ]);

        } catch (\Throwable $e) {
            return $this->response->setJSON([
                'success' => false,
                'message' => 'Gagal mengkonfigurasi akun admin di database: ' . $e->getMessage()
            ]);
        }

        // 6. Create installed.lock file to lock the installer
        if (file_put_contents($this->lockFile, date('Y-m-d H:i:s')) === false) {
            return $this->response->setJSON([
                'success' => false,
                'message' => 'Koneksi database & migrasi sukses, tetapi GAGAL membuat file pengunci writable/installed.lock. Tolong buat secara manual agar sistem aman.'
            ]);
        }

        return $this->response->setJSON([
            'success' => true,
            'message' => 'Instalasi selesai dengan sukses!'
        ]);
    }
}
