<?php
/**
 * ModuleManagerAjax — AJAX handlers for the module manager UI.
 *
 * @package UltraguardPro\modules\dashboard\ajax
 */

namespace UltraguardPro\modules\dashboard\ajax;

use UltraguardPro\Plugin;
use UltraguardPro\modules\ModuleManager;
use UltraguardPro\support\LicenseInfo;
use UltraguardPro\support\AjaxResponseBuilder;

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

// phpcs:disable WordPress.Security.NonceVerification.Missing -- Module Manager AJAX handlers verify nonce centrally via $this->verify().

class ModuleManagerAjax {

    private Plugin $plugin;

    private const NONCE = 'ultraguard_admin_nonce';

    public function __construct( Plugin $plugin ) {
        $this->plugin = $plugin;
    }

    public function register(): void {
        $actions = [
            'ultraguard_modules_list'      => 'handle_list',
            'ultraguard_module_activate'   => 'handle_activate',
            'ultraguard_module_deactivate' => 'handle_deactivate',
            'ultraguard_module_install'    => 'handle_install',
            'ultraguard_module_uninstall'  => 'handle_uninstall',
            'ultraguard_module_upload_zip' => 'handle_upload_zip',
        ];

        foreach ( $actions as $action => $method ) {
            add_action( 'wp_ajax_' . $action, [ $this, $method ] );
        }
    }

    // =========================================================================
    // Handlers
    // =========================================================================

    public function handle_list(): void {
        $this->verify();
        $manager = $this->get_manager();
        $license = LicenseInfo::instance();
        $is_pro  = $license->isPro();

        $safe = [];
        foreach ( $manager->get_all_modules() as $id => $m ) {
            $safe[ $id ] = [
                'id'               => esc_html(  $m['id']               ?? $id ),
                'name'             => esc_html(  $m['name']             ?? $id ),
                'description'      => esc_html(  $m['description']      ?? '' ),
                'version'          => esc_html(  $m['version']          ?? '' ),
                'author'           => esc_html(  $m['author']           ?? '' ),
                'type'             => esc_attr(  $m['type']             ?? 'feature' ),
                'icon'             => esc_attr(  $m['icon']             ?? 'dashicons-admin-plugins' ),
                'is_builtin'       => (bool) ( $m['is_builtin']         ?? false ),
                'is_active'        => (bool) ( $m['is_active']          ?? false ),
                'is_loaded'        => (bool) ( $m['is_loaded']          ?? false ),
                'removable'        => ! (bool) ( $m['is_builtin']       ?? true ),
                'license_required' => esc_attr(  $m['license_required'] ?? 'free' ),
                'license_ok'       => (bool) ( $m['license_ok']         ?? true ),
                'can_activate'     => (bool) ( $m['can_activate']       ?? false ),
            'upgrade_url'      => esc_url( $m['upgrade_url']        ?? 'https://ultraguard.net/pricing' ),
                'installed_at'     => esc_html(  $m['installed_at']     ?? '' ),
            ];
        }

        AjaxResponseBuilder::make()
            ->success()
            ->addData( 'modules',     array_values( $safe ) )
            ->addData( 'is_pro',      $is_pro )
            ->addData( 'total',       count( $safe ) )
            ->addData( 'active',      count( array_filter( $safe, fn( $m ) => $m['is_active'] ) ) )
            ->addData( 'upgrade_url', esc_url( $manager->get_upgrade_url() ) )
            ->updateText( '#ultraguard-module-manager-total',  (string) count( $safe ) )
            ->updateText( '#ultraguard-module-manager-active', (string) count( array_filter( $safe, fn( $m ) => $m['is_active'] ) ) )
            ->send();
    }

    public function handle_activate(): void {
        $this->verify();
        $id = $this->read_module_id();
        $this->send_result( $this->get_manager()->activate( $id ), $id, 'activated' );
    }

    public function handle_deactivate(): void {
        $this->verify();
        $id = $this->read_module_id();
        $this->send_result( $this->get_manager()->deactivate( $id ), $id, 'deactivated' );
    }

    public function handle_install(): void {
        $this->verify();

        $zip_path = sanitize_text_field( wp_unslash( $_POST['zip_path'] ?? '' ) );

        if ( empty( $zip_path ) || ! file_exists( $zip_path ) ) {
            AjaxResponseBuilder::make()
                ->error( __( 'No ZIP file provided or file not found.', 'ultraguard-security-suite' ) )
                ->addNotification( __( 'No ZIP file provided or file not found.', 'ultraguard-security-suite' ), 'error' )
                ->send();
            return;
        }

        $upload_base = realpath( wp_upload_dir()['basedir'] );
        $real_path   = realpath( $zip_path );

        if ( ! $real_path || ! $upload_base || ! str_starts_with( $real_path, $upload_base ) ) {
            AjaxResponseBuilder::make()
                ->error( __( 'Invalid file path.', 'ultraguard-security-suite' ) )
                ->addNotification( __( 'Invalid file path.', 'ultraguard-security-suite' ), 'error' )
                ->send();
            return;
        }

        $this->send_result( $this->get_manager()->install_from_zip( $real_path ), '', 'installed' );
    }

    public function handle_uninstall(): void {
        $this->verify();
        $id = $this->read_module_id();
        $this->send_result( $this->get_manager()->uninstall( $id ), $id, 'uninstalled' );
    }

    public function handle_upload_zip(): void {
        $this->verify();

        $file_error = (int) ( $_FILES['module_zip']['error'] ?? UPLOAD_ERR_NO_FILE ); // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized -- Upload error code is cast to int before use.

        if ( empty( $_FILES['module_zip'] ) || $file_error !== UPLOAD_ERR_OK ) {
            AjaxResponseBuilder::make()
                ->error( $this->upload_error_message( $file_error ) )
                ->addNotification( $this->upload_error_message( $file_error ), 'error' )
                ->send();
            return;
        }

        $file = isset($_FILES['module_zip'] ) ? $_FILES['module_zip'] : []; // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.MissingUnslash,WordPress.Security.ValidatedSanitizedInput.InputNotSanitized -- FILE upload array, tmp_name and name validated below.

        $type_check = wp_check_filetype( $file['name'], [ 'zip' => 'application/zip' ] );
        if ( $type_check['ext'] !== 'zip' ) {
            AjaxResponseBuilder::make()
                ->error( __( 'Only ZIP files are accepted.', 'ultraguard-security-suite' ) )
                ->addNotification( __( 'Only ZIP files are accepted.', 'ultraguard-security-suite' ), 'error' )
                ->send();
            return;
        }

        if ( (int) $file['size'] > 50 * 1024 * 1024 ) {
            AjaxResponseBuilder::make()
                ->error( __( 'ZIP file must be 50 MB or smaller.', 'ultraguard-security-suite' ) )
                ->addNotification( __( 'ZIP file must be 50 MB or smaller.', 'ultraguard-security-suite' ), 'error' )
                ->send();
            return;
        }

        $manager  = $this->get_manager();
        $dest_dir = $manager->get_modules_dir();
        wp_mkdir_p( $dest_dir );

        $dest = $dest_dir . 'upload_' . time() . '_' . wp_generate_password( 8, false ) . '.zip';

        if ( ! is_uploaded_file( $file['tmp_name'] ) ) {
            AjaxResponseBuilder::make()
                ->error( __( 'Invalid upload source.', 'ultraguard-security-suite' ) )
                ->addNotification( __( 'Invalid upload source.', 'ultraguard-security-suite' ), 'error' )
                ->send();
            return;
        }

        // phpcs:ignore Generic.PHP.ForbiddenFunctions.Found -- validated upload is moved into a plugin-owned temporary install path.
        if ( ! move_uploaded_file( $file['tmp_name'], $dest ) ) {
            AjaxResponseBuilder::make()
                ->error( __( 'Failed to save the uploaded file. Check directory permissions.', 'ultraguard-security-suite' ) )
                ->addNotification( __( 'Failed to save the uploaded file.', 'ultraguard-security-suite' ), 'error' )
                ->send();
            return;
        }

        AjaxResponseBuilder::make()
            ->success( __( 'File uploaded — ready to install.', 'ultraguard-security-suite' ) )
            ->addData( 'zip_path', $dest )
            ->addNotification( __( 'File uploaded successfully.', 'ultraguard-security-suite' ), 'success' )
            ->send();
    }

    // =========================================================================
    // Private helpers
    // =========================================================================

    private function verify(): void {
        if ( ! check_ajax_referer( self::NONCE, 'nonce', false ) ) {
            AjaxResponseBuilder::make()
                ->nonceError( __( 'Security check failed.', 'ultraguard-security-suite' ) )
                ->send( 403 );
        }

        if ( ! \UltraguardPro\ultraguard_current_user_can_manage() ) {
            AjaxResponseBuilder::make()->permissionError()->send( 403 );
        }
    }

    private function get_manager(): ModuleManager {
        $manager = $this->plugin->modules->get_module_manager();
        if ( ! $manager ) {
            AjaxResponseBuilder::make()
                ->error( __( 'Module manager is unavailable.', 'ultraguard-security-suite' ) )
                ->addNotification( __( 'Module manager is unavailable.', 'ultraguard-security-suite' ), 'error' )
                ->send();
        }
        return $manager;
    }

    private function read_module_id(): string {
        $id = sanitize_key( wp_unslash( $_POST['module_id'] ?? '' ) );
        if ( empty( $id ) ) {
            AjaxResponseBuilder::make()
                ->error( __( 'Module ID is required.', 'ultraguard-security-suite' ) )
                ->addNotification( __( 'Module ID is required.', 'ultraguard-security-suite' ), 'error' )
                ->send();
        }
        return $id;
    }

    /**
     * Convert a ModuleManager result array into a standardised AjaxResponseBuilder response.
     * Applies appropriate DOM actions based on the operation performed.
     *
     * @param array<string,mixed> $result     ModuleManager result.
     * @param string              $module_id  Slug of the module (for card selector).
     * @param string              $operation  'activated'|'deactivated'|'installed'|'uninstalled'.
     */
    private function send_result( array $result, string $module_id, string $operation ): void {
        if ( ! $result['success'] ) {
            $builder = AjaxResponseBuilder::make()
                ->error( $result['message'] ?? __( 'Operation failed.', 'ultraguard-security-suite' ) )
                ->addNotification( $result['message'] ?? __( 'Operation failed.', 'ultraguard-security-suite' ), 'error' );

            if ( ! empty( $result['upgrade_url'] ) ) {
                $builder->addData( 'upgrade_url', esc_url( $result['upgrade_url'] ) )
                        ->addData( 'code', $result['code'] ?? 'error' );
            }

            $builder->send();
            return;
        }

        $msg     = $result['message'] ?? '';
        $builder = AjaxResponseBuilder::make()
            ->success( $msg )
            ->addData( 'module_id', $result['module_id'] ?? $module_id )
            ->addNotification( $msg, 'success' );

        // Apply targeted DOM updates for each operation type
        $card_sel = '.ultraguard-module-card[data-module-id="' . esc_js( $module_id ) . '"]';

        if ( $operation === 'activated' ) {
            $builder->addClass( $card_sel, 'is-active' )
                    ->removeClass( $card_sel, 'is-inactive' )
                    ->updateAttribute( $card_sel . ' .ultraguard-module-toggle', 'data-active', '1' )
                    ->updateText( $card_sel . ' .ultraguard-module-status-label', __( 'Active', 'ultraguard-security-suite' ) );
        } elseif ( $operation === 'deactivated' ) {
            $builder->removeClass( $card_sel, 'is-active' )
                    ->addClass( $card_sel, 'is-inactive' )
                    ->updateAttribute( $card_sel . ' .ultraguard-module-toggle', 'data-active', '0' )
                    ->updateText( $card_sel . ' .ultraguard-module-status-label', __( 'Inactive', 'ultraguard-security-suite' ) );
        } elseif ( $operation === 'uninstalled' ) {
            $builder->addRemove( $card_sel, true, 300 );
        }

        // Full reload when the manager explicitly requests it (e.g., after install)
        if ( ! empty( $result['reload'] ) || $operation === 'installed' ) {
            $builder->addReload( 1500 );
        }

        $builder->send();
    }

    private function upload_error_message( int $code ): string {
        return match ( $code ) {
            UPLOAD_ERR_INI_SIZE,
            UPLOAD_ERR_FORM_SIZE => __( 'The file exceeds the server size limit.', 'ultraguard-security-suite' ),
            UPLOAD_ERR_PARTIAL   => __( 'The file was only partially uploaded.', 'ultraguard-security-suite' ),
            UPLOAD_ERR_NO_FILE   => __( 'No file was uploaded.', 'ultraguard-security-suite' ),
            default              => __( 'File upload failed.', 'ultraguard-security-suite' ),
        };
    }
}

// phpcs:enable
