#!/usr/bin/env php
<?php

/**
 * cli/yatra — the application's command-line tool (no Artisan/framework).
 *
 * Usage:
 *   php cli/yatra migrate                 Run pending migrations
 *   php cli/yatra migrate:rollback [--step=N]
 *   php cli/yatra migrate:fresh           Drop all tables and re-migrate
 *   php cli/yatra seed [--class=Name]     Run seeders (default: DatabaseSeeder)
 *   php cli/yatra key:generate            Generate and store APP_KEY in .env
 *   php cli/yatra cache:clear             Clear storage/cache
 *   php cli/yatra system:check            Environment / requirements check
 *   php cli/yatra list                    Show available commands
 */

declare(strict_types=1);

if (PHP_SAPI !== 'cli') {
    fwrite(STDERR, "This script must be run from the command line.\n");
    exit(1);
}

require __DIR__ . '/../bootstrap/autoload.php';

use Yatra\Auth\Hash;
use Yatra\Core\Container;
use Yatra\Core\Database;
use Yatra\Database\Migrator;
use Yatra\Queue\DatabaseQueue;
use Yatra\Queue\Worker;
use Yatra\Repositories\RoleRepository;
use Yatra\Repositories\UserRepository;
use Yatra\Seeders\DatabaseSeeder;

/** @var Container $app */
$app = require __DIR__ . '/../bootstrap/app.php';

$argv = $_SERVER['argv'];
array_shift($argv); // drop script name
$command = $argv[0] ?? 'list';

/** Parse --flag=value options into a map. */
$options = [];
foreach (array_slice($argv, 1) as $arg) {
    if (str_starts_with($arg, '--')) {
        $pair = explode('=', substr($arg, 2), 2);
        $options[$pair[0]] = $pair[1] ?? true;
    }
}

function out(string $line = ''): void
{
    fwrite(STDOUT, $line . PHP_EOL);
}

function err(string $line): void
{
    fwrite(STDERR, $line . PHP_EOL);
}

try {
    switch ($command) {
        case 'migrate':
            $migrator = new Migrator($app->make(Database::class), BASE_PATH . '/database/migrations');
            $migrator->migrate();
            break;

        case 'migrate:rollback':
            $steps = (int) ($options['step'] ?? 1);
            $migrator = new Migrator($app->make(Database::class), BASE_PATH . '/database/migrations');
            $migrator->rollback(max(1, $steps));
            break;

        case 'migrate:fresh':
            $migrator = new Migrator($app->make(Database::class), BASE_PATH . '/database/migrations');
            $migrator->fresh();
            break;

        case 'seed':
            $db = $app->make(Database::class);
            $migrator = new Migrator($db, BASE_PATH . '/database/migrations');
            $migrator->ensureMigrationsTable();

            $class = $options['class'] ?? 'DatabaseSeeder';
            $fqcn = str_contains((string) $class, '\\') ? (string) $class : 'Yatra\\Seeders\\' . $class;

            if (!class_exists($fqcn)) {
                err("Seeder [{$fqcn}] not found.");
                exit(1);
            }

            out('Seeding database...');
            (new $fqcn($db))->run();
            out('Done.');
            break;

        case 'key:generate':
            $key = 'base64:' . base64_encode(random_bytes(32));
            $envFile = BASE_PATH . '/.env';

            if (is_file($envFile)) {
                $contents = (string) file_get_contents($envFile);
                if (preg_match('/^APP_KEY=.*$/m', $contents) === 1) {
                    $contents = preg_replace('/^APP_KEY=.*$/m', 'APP_KEY=' . $key, $contents) ?? $contents;
                } else {
                    $contents .= PHP_EOL . 'APP_KEY=' . $key . PHP_EOL;
                }
                file_put_contents($envFile, $contents);
                out('Application key set in .env');
            } else {
                out('No .env file found. Your generated key is:');
                out('  ' . $key);
                out('Add it as APP_KEY in your environment or config/config.php.');
            }
            break;

        case 'cache:clear':
            $dir = BASE_PATH . '/storage/cache';
            $count = 0;
            foreach (glob($dir . '/*') ?: [] as $file) {
                if (is_file($file) && basename($file) !== '.gitkeep') {
                    @unlink($file);
                    $count++;
                }
            }
            out("Cleared {$count} cache file(s).");
            break;

        case 'queue:work':
            /** @var Worker $worker */
            $worker = $app->make(Worker::class);
            $queue = (string) ($options['queue'] ?? 'default');
            $once  = isset($options['once']);
            $sleep = (int) ($options['sleep'] ?? 3);
            $max   = (int) ($options['max'] ?? 0);

            out("Processing queue [{$queue}]" . ($once ? ' (once)' : '') . '...');
            $processed = $worker->work($queue, $once, $sleep, $max);
            out("Processed {$processed} job(s).");
            break;

        case 'queue:failed':
            $db = $app->make(Database::class);
            $rows = $db->select('SELECT id, queue, failed_at, exception FROM failed_jobs ORDER BY id DESC LIMIT 50');
            if ($rows === []) {
                out('No failed jobs.');
                break;
            }
            foreach ($rows as $r) {
                $first = strtok((string) $r['exception'], "\n");
                out(sprintf('  #%-5s [%s] %s  %s', $r['id'], $r['queue'], $r['failed_at'], $first));
            }
            break;

        case 'queue:retry':
            $db = $app->make(Database::class);
            $id = $options['id'] ?? 'all';
            if ($id === 'all' || $id === true) {
                $failed = $db->select('SELECT * FROM failed_jobs');
            } else {
                $failed = $db->select('SELECT * FROM failed_jobs WHERE id = ?', [(int) $id]);
            }
            $count = 0;
            foreach ($failed as $job) {
                $db->transaction(function (Database $db) use ($job, &$count): void {
                    $db->affectingStatement(
                        'INSERT INTO jobs (queue, payload, attempts, reserved_at, available_at, created_at)
                         VALUES (?, ?, 0, NULL, UTC_TIMESTAMP(), UTC_TIMESTAMP())',
                        [$job['queue'], $job['payload']]
                    );
                    $db->affectingStatement('DELETE FROM failed_jobs WHERE id = ?', [$job['id']]);
                    $count++;
                });
            }
            out("Requeued {$count} job(s).");
            break;

        case 'queue:flush':
            $db = $app->make(Database::class);
            $n = $db->affectingStatement('DELETE FROM failed_jobs', []);
            out("Deleted {$n} failed job(s).");
            break;

        case 'create-admin':
            $name = (string) ($options['name'] ?? '');
            $email = (string) ($options['email'] ?? '');
            $password = (string) ($options['password'] ?? '');
            $mobile = isset($options['mobile']) ? (string) $options['mobile'] : null;

            if ($name === '' || $email === '' || $password === '') {
                err('Usage: php cli/yatra create-admin --name="Full Name" --email=you@example.com --password=Secret1! [--mobile=+9779...]');
                exit(1);
            }
            if (filter_var($email, FILTER_VALIDATE_EMAIL) === false) {
                err('Invalid email address.');
                exit(1);
            }
            $policy = Hash::policyErrors($password);
            if ($policy !== []) {
                err('Weak password:');
                foreach ($policy as $p) {
                    err('  - ' . $p);
                }
                exit(1);
            }

            /** @var UserRepository $users */
            $users = $app->make(UserRepository::class);
            /** @var RoleRepository $roles */
            $roles = $app->make(RoleRepository::class);

            if ($users->findByEmail($email) !== null) {
                err("A user with email {$email} already exists.");
                exit(1);
            }
            if ($roles->findBySlug('super-admin') === null) {
                err('The super-admin role is missing. Run: php cli/yatra migrate && php cli/yatra seed');
                exit(1);
            }

            $id = $users->createUser([
                'full_name'     => $name,
                'email'         => $email,
                'mobile'        => $mobile,
                'password_hash' => Hash::make($password),
                'status'        => 1,
            ]);
            $roles->assignToUser($id, 'super-admin');

            out("Super Admin created (id {$id}): {$email}");
            break;

        case 'system:check':
            require __DIR__ . '/system_check.php';
            yatra_system_check($app->make(Database::class));
            break;

        case 'list':
        case 'help':
        default:
            out('Yatra CLI — available commands:');
            out('  migrate                    Run pending migrations');
            out('  migrate:rollback [--step=N] Roll back the last N batches');
            out('  migrate:fresh              Drop all tables and re-migrate');
            out('  seed [--class=Name]        Run seeders (default DatabaseSeeder)');
            out('  key:generate               Generate APP_KEY into .env');
            out('  cache:clear                Clear storage/cache');
            out('  queue:work [--queue=][--once][--sleep=3][--max=0]  Process queued jobs');
            out('  queue:failed               List failed jobs');
            out('  queue:retry [--id=N|all]   Requeue failed job(s)');
            out('  queue:flush                Delete all failed jobs');
            out('  create-admin --name= --email= --password= [--mobile=]  Create a Super Admin');
            out('  system:check               Check PHP/extensions/permissions/DB');
            out('  list                       Show this help');
            break;
    }
} catch (Throwable $e) {
    err('Error: ' . $e->getMessage());
    exit(1);
}

exit(0);
