<?php

namespace App\Services\Install;

use CodeIgniter\CLI\CLI;
use Config\Database;

/**
 * Step 8 engine. The controller calls runStep($stepKey) once per AJAX poll
 * (see InstallController::run()) so a single long-running blocking request
 * never times out on shared hosting — each step is short, and idempotent
 * (checks "already done?" first), matching 08-installation-workflow.md's
 * "safe to resume" requirement.
 *
 * Step order is fixed in STEPS — the controller/view iterate this to drive
 * the progress bar.
 */
class InstallRunner
{
    public const STEPS = [
        'write_env'       => 'Writing configuration file',
        'run_migrations'  => 'Creating database tables',
        'run_seeders'     => 'Seeding default data',
        'create_admin'    => 'Creating Super Admin account',
        'create_storage'  => 'Creating storage folders',
        'write_crontab'   => 'Preparing cron job instructions',
        'write_lock'      => 'Finalizing installation',
    ];

    public function runStep(string $step, array $wizardState): array
    {
        return match ($step) {
            'write_env'      => $this->writeEnv($wizardState),
            'run_migrations' => $this->runMigrations(),
            'run_seeders'    => $this->runSeeders(),
            'create_admin'   => $this->createAdmin($wizardState),
            'create_storage' => $this->createStorageFolders(),
            'write_lock'     => $this->writeLock(),
            'write_crontab'  => $this->writeCrontabReference(),
            default          => ['success' => false, 'message' => "Unknown install step: {$step}"],
        };
    }

    private function writeEnv(array $wizardState): array
    {
        $writer = new EnvWriter();
        if ($writer->isWritten()) {
            return ['success' => true, 'message' => '.env already present — skipped.'];
        }
        $writer->write($wizardState);
        return ['success' => true, 'message' => '.env written.'];
    }

    private function runMigrations(): array
    {
        try {
            $migrate = \Config\Services::migrations();
            $migrate->latest();
            return ['success' => true, 'message' => 'Migrations completed.'];
        } catch (\Throwable $e) {
            return ['success' => false, 'message' => 'Migration failed: ' . $e->getMessage()];
        }
    }

    private function runSeeders(): array
    {
        try {
            $existingRoles = Database::connect()->table('roles')->countAllResults();
            if ($existingRoles > 0) {
                return ['success' => true, 'message' => 'Core data already seeded — skipped.'];
            }

            $seeder = \Config\Database::seeder();
            $seeder->call(\App\Database\Seeds\MasterSeeder::class);
            $seeder->call(\App\Database\Seeds\JobCategoriesAndSkillsSeeder::class);
            $seeder->call(\App\Database\Seeds\BillingSeeder::class);
            $seeder->call(\App\Database\Seeds\NotificationTemplateSeeder::class);
            return ['success' => true, 'message' => 'Seed data created.'];
        } catch (\Throwable $e) {
            return ['success' => false, 'message' => 'Seeding failed: ' . $e->getMessage()];
        }
    }

    private function createAdmin(array $wizardState): array
    {
        $admin = $wizardState['step5'] ?? [];
        $db = Database::connect();

        $existing = $db->table('users')->where('email', $admin['email'] ?? '')->get()->getRow();
        if ($existing) {
            return ['success' => true, 'message' => 'Super Admin already exists — skipped.'];
        }

        $role = $db->table('roles')->where('slug', 'super_admin')->get()->getRow();
        if (! $role) {
            return ['success' => false, 'message' => 'super_admin role not found — did the seed step run?'];
        }

        $db->table('users')->insert([
            'role_id'           => $role->id,
            'agency_id'         => null,
            'name'              => $admin['name'] ?? 'Administrator',
            'email'             => $admin['email'] ?? '',
            'password_hash'     => password_hash($admin['password'] ?? bin2hex(random_bytes(8)), PASSWORD_ARGON2ID),
            'status'            => 'active',
            'email_verified_at' => date('Y-m-d H:i:s'),
            'created_at'        => date('Y-m-d H:i:s'),
            'updated_at'        => date('Y-m-d H:i:s'),
        ]);

        return ['success' => true, 'message' => 'Super Admin account created.'];
    }

    private function createStorageFolders(): array
    {
        $folders = [
            WRITEPATH . 'uploads',
            WRITEPATH . 'uploads/resumes',
            WRITEPATH . 'uploads/verification-docs',
            WRITEPATH . 'uploads/invoices',
            FCPATH . 'assets/uploads/logos',
            FCPATH . 'assets/uploads/avatars',
        ];

        foreach ($folders as $folder) {
            if (! is_dir($folder)) {
                mkdir($folder, 0755, true);
            }
        }

        return ['success' => true, 'message' => 'Storage folders ready.'];
    }

    private function writeLock(): array
    {
        file_put_contents(WRITEPATH . 'install.lock', json_encode([
            'installed_at' => date('c'),
            'version'      => '1.0.0',
        ]));
        return ['success' => true, 'message' => 'Installation locked.'];
    }

    private function writeCrontabReference(): array
    {
        $phpBinary = PHP_BINARY;
        $sparkPath = ROOTPATH . 'spark';

        $content = <<<CRON
        # Add this single line to the deployment user's crontab (crontab -e):
        * * * * * {$phpBinary} {$sparkPath} tasks:run >> /var/log/job-portal-cron.log 2>&1

        # After adding it, verify it's firing correctly by visiting:
        # {$this->baseUrl()}install/verify-cron
        CRON;

        file_put_contents(WRITEPATH . 'crontab.txt', $content);

        return ['success' => true, 'message' => 'Cron instructions written to writable/crontab.txt.'];
    }

    private function baseUrl(): string
    {
        return function_exists('base_url') ? base_url() : '/';
    }
}
