<?php

declare(strict_types=1);

namespace ShieldX\WPCLI;

defined('ABSPATH') || exit;

use ShieldX\Scanner\LocalScanner;
use ShieldX\Scanner\ScheduledScanSettings;
use ShieldX\Scanner\VulnerabilityScanner;

final class ScanCommand
{
    /**
     * Run or inspect SHIELD-X scans.
     *
     * @synopsis [<status|run>] [<type>] [--type=<type>] [--depth=<quick|deep>] [--scopes=<scopes>] [--max-files=<max-files>] [--format=<format>]
     *
     * @param array<int,string>   $args
     * @param array<string,mixed> $assocArgs
     */
    public function __invoke(array $args, array $assocArgs): void
    {
        AdminGate::requireManageOptions();

        $subcommand = isset($args[0]) ? (string) $args[0] : 'status';
        $json = CommandOutput::isJson($assocArgs);

        switch ($subcommand) {
            case 'run':
                $this->run($args, $assocArgs, $json);
                return;

            case 'status':
                $this->status($json);
                return;

            default:
                \WP_CLI::error('Unknown scan command. Use status or run.');
        }
    }

    private function status(bool $json): void
    {
        $scanner = LocalScanner::fromWordPress()->summary();
        $last = isset($scanner['last_scan']) && is_array($scanner['last_scan']) ? $scanner['last_scan'] : [];

        if ($json) {
            CommandOutput::writeJson([
                'command' => 'scan',
                'subcommand' => 'status',
                'scanner' => $scanner,
            ]);
            return;
        }

        \WP_CLI::line('Active issues: ' . (string) ($scanner['active_issues'] ?? 0));
        \WP_CLI::line('Tracked files: ' . (string) ($scanner['tracked_files'] ?? 0));
        \WP_CLI::line('Last scan: ' . (!empty($last) ? (string) ($last['status'] ?? 'unknown') . ' at ' . (string) ($last['started_at'] ?? '') : 'not run'));
        \WP_CLI::success('SHIELD-X scan status completed.');
    }

    /**
     * @param array<int,string>   $args
     * @param array<string,mixed> $assocArgs
     */
    private function run(array $args, array $assocArgs, bool $json): void
    {
        $type = isset($assocArgs['type']) ? (string) $assocArgs['type'] : (isset($args[1]) ? (string) $args[1] : ScheduledScanSettings::TYPE_FILE);
        $type = strtolower(trim($type));
        $types = $type === 'all' ? ScheduledScanSettings::types() : [ScheduledScanSettings::sanitizeType($type)];
        $results = [];
        $failedMessage = '';

        foreach ($types as $scanType) {
            $result = $this->runType($scanType, $assocArgs);
            $results[$scanType] = $result;
            if (!$json) {
                $this->printResult($scanType, $result);
            }
            if ((string) ($result['status'] ?? '') === 'failed') {
                $failedMessage = (string) ($result['error'] ?? 'Scan failed.');
                if (!$json) {
                    \WP_CLI::error($failedMessage);
                }
            }
        }

        if ($json) {
            CommandOutput::writeJson([
                'command' => 'scan',
                'subcommand' => 'run',
                'ok' => $failedMessage === '',
                'results' => $results,
            ]);
            if ($failedMessage !== '') {
                \WP_CLI::halt(1);
            }
            return;
        }

        \WP_CLI::success('SHIELD-X scan run completed.');
    }

    /**
     * @param array<string,mixed> $assocArgs
     * @return array<string,mixed>
     */
    private function runType(string $type, array $assocArgs): array
    {
        if ($type === ScheduledScanSettings::TYPE_VULNERABILITY) {
            return VulnerabilityScanner::fromWordPress()->scan();
        }

        $maxFiles = isset($assocArgs['max-files']) ? max(1, min(50000, (int) $assocArgs['max-files'])) : LocalScanner::DEFAULT_MAX_FILES;
        $scopes = [];

        if ($type === ScheduledScanSettings::TYPE_DATABASE) {
            $scopes = ['database'];
        } elseif (isset($assocArgs['scopes'])) {
            $scopes = array_values(array_filter(array_map('trim', explode(',', (string) $assocArgs['scopes']))));
        }

        $scanner = LocalScanner::fromWordPress();
        $depth = isset($assocArgs['depth']) && strtolower(trim((string) $assocArgs['depth'])) === 'deep' ? 'deep' : 'quick';
        if ($type === ScheduledScanSettings::TYPE_DATABASE && $depth === 'deep') {
            $job = $scanner->startProgressiveScan($maxFiles, ['database'], 'shield-x-scan', true, 'scan', 'deep');
            $scanId = (string) ($job['scan_id'] ?? '');
            $result = $job;
            $steps = 0;
            while ($scanId !== '' && (string) ($result['status'] ?? '') === 'running' && $steps < 100000) {
                $result = $scanner->runProgressiveScanBatch($scanId);
                $steps++;
            }

            return isset($result['result']) && is_array($result['result']) ? $result['result'] : $result;
        }

        return $scanner->runQuickScan($maxFiles, $scopes ?: null, 'cli');
    }

    /**
     * @param array<string,mixed> $result
     */
    private function printResult(string $type, array $result): void
    {
        \WP_CLI::line(sprintf(
            '%s scan: %s, issues=%d, files=%d/%d, db=%d, duration=%dms',
            $type,
            (string) ($result['status'] ?? 'unknown'),
            (int) ($result['issues_found'] ?? 0),
            (int) ($result['files_scanned'] ?? 0),
            (int) ($result['files_total'] ?? 0),
            (int) ($result['database_items_scanned'] ?? 0),
            (int) ($result['duration_ms'] ?? 0)
        ));
    }
}
