<?php

declare(strict_types=1);

namespace SecurWP\Modules\Scanner;

use SecurWP\Modules\Scanner\Repository\CloudJobRepository;
use SecurWP\Modules\Scanner\Repository\FileStateRepository;
use SecurWP\Modules\Scanner\Repository\IssueRepository;
use SecurWP\Includes\SettingsStore;

if ( ! defined( 'ABSPATH' ) ) { exit; }


/**
 * Manages the execution of scans and result retrieval.
 * Uses IssueRepository for normalized issue persistence.
 */
class ScanController {

    /**
     * @var ScanEngine[]
     */
    private $scanners = [];

    public function __construct() {}

    /**
     * Ignore an issue by hash.
     *
     * @param string $hash Issue hash.
     * @return bool Success.
     */
    public function ignore_item( $hash ) {
        if ( IssueRepository::ignore( $hash ) ) {
            // Log to audit trail
            \SecurWP\Modules\AuditLog\AuditLog::add_log( 'Issue Ignored', [ 'hash' => $hash ], 'info', 'scanner' );
            return true;
        }
        return false;
    }

    /**
     * Unignore an issue by hash.
     *
     * @param string $hash Issue hash.
     * @return bool Success.
     */
    public function unignore_item( $hash ) {
        if ( IssueRepository::unignore( $hash ) ) {
            // Log to audit trail
            \SecurWP\Modules\AuditLog\AuditLog::add_log( 'Issue Unignored', [ 'hash' => $hash ], 'info', 'scanner' );
            return true;
        }
        return false;
    }

    /**
     * Register a scanner engine.
     */
    public function register_scanner( $slug, $scanner ) {
        $this->scanners[$slug] = $scanner;
    }

    /**
     * Get a registered scanner by slug.
     */
    public function get_scanner( $slug ) {
        return $this->scanners[$slug] ?? null;
    }

    /**
     * Build a controller with the built-in system-check scanners.
     *
     * @return self
     */
    public static function build_default_controller(): self {
        $controller = new self();
        $controller->register_scanner( 'reputation', new ReputationScanner() );
        $controller->register_scanner( 'users', new UserScanner() );
        $controller->register_scanner( 'site_health', new SiteHealth() );
        $controller->register_scanner( 'content', new ContentScanner() );
        $controller->register_scanner( 'sensitive_data', new SensitiveDataScanner() );
        $controller->register_scanner( 'vulnerabilities', new VulnerabilityScanner() );

        return $controller;
    }

    /**
     * Run a specific scanner by slug.
     * Called by ScanWorker for system checks (users, reputation, vulnerabilities).
     * 
     * @param string $slug Scanner slug
     * @param string $scan_id Current scan session ID
     * @return array Scan results with 'issues' array
     */
    public function run_scan( $slug, $scan_id = null ) {
        if ( ! isset( $this->scanners[$slug] ) ) {
            return [ 'issues' => [], 'complete' => true, 'failed' => false ];
        }

        $scanner = $this->scanners[$slug];
        
        if ( method_exists( $scanner, 'set_controller' ) ) {
            $scanner->set_controller( $this );
        }
        if ( method_exists( $scanner, 'set_scan_context' ) ) {
            $scanner->set_scan_context( $scan_id );
        }

        $result = $scanner->run();
        // Handle both return formats: ['issues' => [...]] or direct array.
        $issues = isset( $result['issues'] ) ? $result['issues'] : ( is_array( $result ) ? $result : [] );
        $is_complete = true;
        $is_failed = false;
        if ( is_array( $result ) ) {
            if ( array_key_exists( 'complete', $result ) ) {
                $is_complete = (bool) $result['complete'];
            }
            if ( ! empty( $result['failed'] ) ) {
                $is_failed = true;
            }
        }
        
        if ( ! empty( $issues ) && $scan_id ) {
            foreach ( $issues as $issue ) {
                if ( ! isset( $issue['scan_id'] ) ) $issue['scan_id'] = $scan_id;
                IssueRepository::stage( $issue );
            }
        }
        
        return [
            'issues' => $issues,
            'complete' => $is_complete,
            'failed' => $is_failed,
        ];
    }

    /**
     * Get all issues for frontend display.
     * 
     * @param int|null $limit
     * @param int|null $offset
     * @return array All issues with is_ignored flag
     */
    public function get_all_issues( $limit = null, $offset = 0, $scan_id = null ) {
        return IssueRepository::getAll( $limit, $offset, $scan_id );
    }

    /**
     * Get active (non-ignored) issues.
     * 
     * @param string|null $type Filter by type
     * @return array Active issues
     */
    public function get_active_issues( $type = null ) {
        return IssueRepository::getActive( $type );
    }

    /**
     * Get ignored issues.
     * 
     * @return array Ignored issues
     */
    public function get_ignored_issues() {
        return IssueRepository::getIgnored();
    }

    /**
     * Get issue counts for dashboard.
     * 
     * @return array ['active' => int, 'ignored' => int, 'by_type' => [...]]
     */
    public function get_issue_counts() {
        return IssueRepository::getCounts();
    }

    /**
     * Refresh scanner counts and broadcast an issue-state change.
     *
     * @param string $action
     * @param string $item_hash
     * @return void
     */
    public static function emit_issue_state_change( string $action, string $item_hash ): void {
        $counts = IssueRepository::getCounts();
        $active_count = is_array( $counts ) ? (int) ( $counts['active'] ?? 0 ) : 0;

        SettingsStore::set_field(
            SettingsStore::COL_STATE,
            'scan_issues_count',
            $active_count
        );

        do_action(
            'securwp_scan_results_changed',
            [
                'action'       => $action,
                'issue_hash'   => $item_hash,
                'active_count' => $active_count,
            ]
        );
    }

    /**
     * Clear non-ignored issues for a new scan.
     * Preserves user's ignore decisions.
     */
    public function clear_for_new_scan() {
        IssueRepository::clearActive();

        $job_repo = new CloudJobRepository();
        $job_repo->clear_all_active();

        $file_repo = new FileStateRepository();
        $file_repo->clear_all_active();
    }

    /**
     * Delete a specific issue by hash.
     * Used for "Mark as Fixed" action.
     * 
     * @param string $hash Issue hash
     * @return bool Success
     */
    public function delete_issue( $hash ) {
        // Read before deleting so the audit log entry is self-contained.
        $issue = IssueRepository::getByHash( (string) $hash );

        if ( IssueRepository::delete( $hash ) ) {
            $issue_severity = $issue['severity'] ?? 'low';
            $log_severity   = in_array( $issue_severity, [ 'critical', 'high' ], true ) ? 'warning' : 'info';

            $log_data = [
                'severity' => $issue_severity,
                'type'     => $issue['type'] ?? 'unknown',
                'detail'   => $issue['message'] ?? '',
            ];

            if ( ! empty( $issue['file_path'] ) ) {
                $log_data['file'] = $issue['file_path'];
            }

            \SecurWP\Modules\AuditLog\AuditLog::add_log( 'Scan Issue Dismissed', $log_data, $log_severity, 'scanner' );
            return true;
        }
        return false;
    }

    /**
     * Save a batch of issues to repository.
     * 
     * @param array $issues Array of issue data
     * @param string $scan_id Scan session ID
     * @return int Number saved
     */
    public function save_issues( $issues, $scan_id = null ) {
        return IssueRepository::saveBatch( $issues, $scan_id );
    }

    /**
     * Generate an issue hash.
     *
     * @param string $type Issue type.
     * @param string $stable_key Stable identifier.
     * @return string Hash.
     */
    public function generate_hash( $type, $stable_key ) {
        return IssueRepository::generateHash( $type, $stable_key );
    }

}
