<?php

declare(strict_types=1);

namespace SiteFort\Modules\Scanner\Service;

use SiteFort\Includes\RemoteUrlValidator;
use SiteFort\Modules\Cloud\Service\CloudSettings;
use SiteFort\Modules\Scanner\ScanTypes;

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

/**
 * Handles file repair by fetching clean baseline content from Intel Cloud sources.
 */
class RepairService {

    private static function allowsRepair(): bool {
        if ( ! class_exists( '\SiteFort\Modules\Cloud\Service\Entitlements' ) ) {
            $path = dirname( dirname( __DIR__ ) ) . '/Cloud/Service/Entitlements.php';
            if ( file_exists( $path ) ) {
                require_once $path;
            }
        }

        return class_exists( '\SiteFort\Modules\Cloud\Service\Entitlements' )
            && \SiteFort\Modules\Cloud\Service\Entitlements::allowsRepair();
    }

    /**
     * Returns extensions eligible for repair. Mirrors ScanTypes::CODE_EXTENSIONS
     * so the repair allowlist stays in sync with what the scanner flags.
     *
     * @return string[]
     */
    private static function get_allowed_extensions(): array {
        return ScanTypes::CODE_EXTENSIONS;
    }

    private static function normalize_wp_locale( string $locale = '' ): string {
        $normalized = trim( (string) $locale );
        return $normalized === '' ? 'en_US' : $normalized;
    }

    private static function current_wp_locale(): string {
        return self::normalize_wp_locale( function_exists( 'get_locale' ) ? (string) get_locale() : 'en_US' );
    }

    private static function is_core_slug( string $slug ): bool {
        $normalized = trim( (string) $slug );
        return $normalized === 'wordpress' || strpos( $normalized, 'wordpress-' ) === 0;
    }

    private static function core_locale_from_slug( string $slug ): string {
        $normalized = trim( (string) $slug );
        if ( $normalized === 'wordpress' ) {
            return 'en_US';
        }
        if ( strpos( $normalized, 'wordpress-' ) === 0 ) {
            return self::normalize_wp_locale( substr( $normalized, strlen( 'wordpress-' ) ) );
        }
        return 'en_US';
    }

    private static function resolve_core_repair_locale( string $locale = '', string $slug = '' ): string {
        $normalized_locale = trim( (string) $locale );
        if ( $normalized_locale !== '' ) {
            return self::normalize_wp_locale( $normalized_locale );
        }
        if ( self::is_core_slug( $slug ) ) {
            return self::core_locale_from_slug( $slug );
        }
        return self::current_wp_locale();
    }

    private static function localized_core_slug( string $locale = '' ): string {
        $normalized_locale = self::normalize_wp_locale( $locale );
        return $normalized_locale === 'en_US' ? 'wordpress' : 'wordpress-' . $normalized_locale;
    }

    /**
     * @return array{repair_available:bool,repair_type:string,repair_slug:string,repair_version:string,repair_locale:string,repair_rel_path:string,repair_file_path:string}
     */
    private static function unsupported_repair_context( string $file_path ): array {
        return [
            'repair_available' => false,
            'repair_type' => '',
            'repair_slug' => '',
            'repair_version' => '',
            'repair_locale' => '',
            'repair_rel_path' => '',
            'repair_file_path' => $file_path,
        ];
    }

	/**
	 * Repair a file by ID (path).
	 *
	 * @param string $file_path Relative path to ABSPATH.
	 * @param string $type 'core', 'plugin', or 'theme'.
	 * @param string $slug Plugin or Theme slug.
	 * @param string $version Version number.
	 * @param string $expected_hash Expected SHA-256 of the clean file.
	 * @param string $locale Scan-time locale for localized WordPress core requests.
	 * @return true|\WP_Error
	 */
	public function repair_file( $file_path, $type, $slug = '', $version = '', $expected_hash = '', $locale = '' ) {
        if ( ! self::allowsRepair() ) {
            return new \WP_Error( 'repair_disabled', 'Repair is available on Pro and Managed plans.' );
        }

        $ext = pathinfo( $file_path, PATHINFO_EXTENSION );
        if ( ! in_array( strtolower( $ext ), self::get_allowed_extensions(), true ) ) {
            return new \WP_Error( 'invalid_type', 'Repair not supported for this file type.' );
        }

        $context = self::resolve_repair_context( $file_path, $type, $slug, $version, $locale );
        if ( empty( $context['repair_available'] ) ) {
            return new \WP_Error( 'not_supported', 'Repair not supported for this file context.' );
        }

        $expected_hash = strtolower( (string) $expected_hash );
        if ( ! preg_match( '/^[a-f0-9]{64}$/', $expected_hash ) ) {
            return new \WP_Error( 'missing_expected_hash', 'Repair requires a valid expected hash to verify integrity.' );
        }

        $file_path = $context['repair_file_path'];
        $type = $context['repair_type'];
        $slug = $context['repair_slug'];
        $version = $context['repair_version'];
        $locale = $context['repair_locale'];
        $rel_path = $context['repair_rel_path'];

        $cloud_result = $this->repair_from_cloud( $file_path, $type, $slug, $version, $locale, $rel_path, $expected_hash );
        if ( $cloud_result === true ) {
            return true;
        }

        return $cloud_result;
	}

	/**
	 * Fetch clean content from Intel Cloud Vault.
	 */
	public function get_clean_content( $file_path, $type, $slug, $version, $locale = '' ) {
        $file_path = ltrim( (string) $file_path, '/\\' );
        $type = sanitize_key( (string) $type );
        $slug = sanitize_text_field( (string) $slug );
        $version = sanitize_text_field( (string) $version );

        $context = self::resolve_repair_context( $file_path, $type, $slug, $version, $locale );
        if ( empty( $context['repair_available'] ) ) {
            $inferred = $this->infer_context_from_path( $file_path, $version, $locale );
            if ( is_wp_error( $inferred ) ) {
                return $inferred;
            }
            $context = $inferred;
        }

        return $this->get_clean_content_from_cloud(
            (string) $context['repair_type'],
            (string) $context['repair_slug'],
            (string) $context['repair_version'],
            (string) $context['repair_locale'],
            (string) $context['repair_rel_path']
        );
	}

    /**
     * Infer clean-content context from file path when caller-supplied metadata is incomplete.
     *
     * @param string $file_path
     * @param string $fallback_version
     * @param string $fallback_locale
     * @return array|\WP_Error
     */
    private function infer_context_from_path( string $file_path, string $fallback_version, string $fallback_locale = '' ) {
        $file_path = ltrim( $file_path, '/\\' );

        if ( preg_match( '#^wp-content/plugins/([^/]+)/(.+)$#', $file_path, $matches ) ) {
            $resolved_version = $fallback_version !== '' ? $fallback_version : $this->resolve_installed_component_version( 'plugin', (string) $matches[1] );
            return [
                'repair_available' => true,
                'repair_type' => 'plugin',
                'repair_slug' => sanitize_key( (string) $matches[1] ),
                'repair_version' => $resolved_version,
                'repair_locale' => '',
                'repair_rel_path' => (string) $matches[2],
                'repair_file_path' => $file_path,
            ];
        }

        if ( preg_match( '#^wp-content/themes/([^/]+)/(.+)$#', $file_path, $matches ) ) {
            $resolved_version = $fallback_version !== '' ? $fallback_version : $this->resolve_installed_component_version( 'theme', (string) $matches[1] );
            return [
                'repair_available' => true,
                'repair_type' => 'theme',
                'repair_slug' => sanitize_key( (string) $matches[1] ),
                'repair_version' => $resolved_version,
                'repair_locale' => '',
                'repair_rel_path' => (string) $matches[2],
                'repair_file_path' => $file_path,
            ];
        }

        if ( strpos( $file_path, 'wp-admin/' ) === 0 || strpos( $file_path, 'wp-includes/' ) === 0 || strpos( $file_path, 'wp-content/' ) !== 0 ) {
            if ( $fallback_version === '' ) {
                $fallback_version = $this->resolve_installed_component_version( 'core', 'wordpress' );
            }
            $resolved_locale = self::resolve_core_repair_locale( $fallback_locale );
            return [
                'repair_available' => true,
                'repair_type' => 'core',
                'repair_slug' => self::localized_core_slug( $resolved_locale ),
                'repair_version' => $fallback_version,
                'repair_locale' => $resolved_locale,
                'repair_rel_path' => $file_path,
                'repair_file_path' => $file_path,
            ];
        }

        return new \WP_Error( 'not_supported', 'Diff source unsupported for this file context.' );
    }

    /**
     * Resolve currently installed component version.
     *
     * @param string $type
     * @param string $slug
     * @return string
     */
    private function resolve_installed_component_version( string $type, string $slug ): string {
        if ( $type === 'core' ) {
            global $wp_version;
            return sanitize_text_field( (string) ( $wp_version ?? '' ) );
        }

        if ( $type === 'theme' ) {
            $themes = wp_get_themes();
            if ( isset( $themes[ $slug ] ) ) {
                return sanitize_text_field( (string) $themes[ $slug ]->get( 'Version' ) );
            }
            return '';
        }

        if ( $type === 'plugin' ) {
            if ( ! function_exists( 'get_plugins' ) ) {
                require_once ABSPATH . 'wp-admin/includes/plugin.php';
            }
            $plugins = get_plugins();
            foreach ( $plugins as $plugin_file => $plugin_data ) {
                $dir = dirname( (string) $plugin_file );
                $is_match = $dir === $slug || basename( (string) $plugin_file, '.php' ) === $slug;
                if ( $is_match ) {
                    return sanitize_text_field( (string) ( $plugin_data['Version'] ?? '' ) );
                }
            }
        }

        return '';
    }

    /**
     * Download clean content from Intel Cloud Vault via signed secure URL.
     *
     * @param string $type
     * @param string $slug
     * @param string $version
     * @param string $locale
     * @param string $rel_path
     * @return string|\WP_Error
     */
    private function get_clean_content_from_cloud( string $type, string $slug, string $version, string $locale, string $rel_path ) {
        if ( $type === '' || $slug === '' || $version === '' || $rel_path === '' ) {
            return new \WP_Error( 'invalid_context', 'Missing cloud content context.' );
        }

        if ( ! self::allowsRepair() ) {
            return new \WP_Error( 'cloud_source_missing', 'Clean source is not available for this license.' );
        }

        if ( ! class_exists( '\SiteFort\Modules\Cloud\SecureApi' ) ) {
            require_once dirname( dirname( __DIR__ ) ) . '/Cloud/SecureApi.php';
        }
        if ( ! class_exists( '\SiteFort\Modules\Cloud\Services\Repair' ) ) {
            require_once dirname( dirname( __DIR__ ) ) . '/Cloud/Services/Repair.php';
        }

        $settings = CloudSettings::get_all();
        $license  = sanitize_text_field( (string) ( $settings['license_key'] ?? '' ) );
        $token    = sanitize_text_field( (string) ( $settings['site_token'] ?? '' ) );
        $secret   = sanitize_text_field( (string) ( $settings['site_secret'] ?? '' ) );

        if ( $token === '' || $secret === '' ) {
            return new \WP_Error( 'cloud_auth_missing', 'Intel Cloud credentials missing. Reconnect this site in SiteFort Console.' );
        }

        $api = new \SiteFort\Modules\Cloud\SecureApi( $license, $token, $secret );
        $repair = new \SiteFort\Modules\Cloud\Services\Repair( $api );
        $download_url = $repair->get_download_url( $type, $slug, $version, $rel_path, $locale );
        if ( ! is_string( $download_url ) || $download_url === '' ) {
            return new \WP_Error( 'cloud_source_missing', 'Could not retrieve clean source from Intel Cloud Vault.' );
        }
        if ( ! RemoteUrlValidator::is_trusted_vault_url( $download_url ) ) {
            return new \WP_Error( 'cloud_source_untrusted', 'Intel Cloud Vault returned an untrusted repair URL.' );
        }

        $request_args = [
            'timeout' => 15,
            'reject_unsafe_urls' => true,
        ];
        $response = function_exists( 'wp_safe_remote_get' )
            ? wp_safe_remote_get( $download_url, $request_args )
            : wp_remote_get( $download_url, $request_args );
        if ( is_wp_error( $response ) ) {
            return $response;
        }

        $status = wp_remote_retrieve_response_code( $response );
        if ( $status !== 200 ) {
            return new \WP_Error( 'cloud_fetch_error', 'Intel Cloud Vault returned status ' . (string) $status . '.' );
        }

        return (string) wp_remote_retrieve_body( $response );
    }

    /**
     * Resolve repair eligibility and metadata from a file context.
     *
     * @param string $file_path Relative path from ABSPATH.
     * @param string $type      Component type ('core'|'plugin'|'theme').
     * @param string $slug      Component slug.
     * @param string $version   Component version.
     * @param string $locale    Scan-time locale for localized WordPress core requests.
     * @return array{repair_available:bool,repair_type:string,repair_slug:string,repair_version:string,repair_locale:string,repair_rel_path:string,repair_file_path:string}
     */
    public static function resolve_repair_context( $file_path, $type, $slug = '', $version = '', $locale = '' ) {
        $file_path = ltrim( (string) $file_path, '/\\' );
        $type = sanitize_key( (string) $type );
        $raw_slug = sanitize_text_field( (string) $slug );
        $version = sanitize_text_field( (string) $version );
        $locale = sanitize_text_field( (string) $locale );

        $ext = pathinfo( $file_path, PATHINFO_EXTENSION );
        if ( empty( $ext ) || ! in_array( strtolower( $ext ), self::get_allowed_extensions(), true ) ) {
            return self::unsupported_repair_context( $file_path );
        }

        if ( $type === 'core' ) {
            if ( empty( $version ) ) {
                global $wp_version;
                $version = $wp_version ?? '';
            }
            $locale = self::resolve_core_repair_locale( $locale, $raw_slug );
            $slug = self::localized_core_slug( $locale );
        } else {
            $slug = $raw_slug;
            $locale = '';
        }

        if ( $slug !== '' && ( strpos( $slug, '/' ) !== false || strpos( $slug, '\\' ) !== false || strpos( $slug, '..' ) !== false ) ) {
            return self::unsupported_repair_context( $file_path );
        }

        if ( ! in_array( $type, [ 'core', 'plugin', 'theme' ], true ) ) {
            return self::unsupported_repair_context( $file_path );
        }

        if ( $type !== 'core' && ( empty( $slug ) || empty( $version ) ) ) {
            return self::unsupported_repair_context( $file_path );
        }

        $rel_path = self::resolve_component_relative_path( $file_path, $type, $slug );
        if ( empty( $rel_path ) ) {
            return self::unsupported_repair_context( $file_path );
        }

        return [
            'repair_available' => true,
            'repair_type' => $type,
            'repair_slug' => $slug,
            'repair_version' => $version,
            'repair_locale' => $locale,
            'repair_rel_path' => $rel_path,
            'repair_file_path' => $file_path,
        ];
    }

    /**
     * Resolve component-relative path for cloud repair.
     *
     * @param string $file_path
     * @param string $type
     * @param string $slug
     * @return string
     */
    private static function resolve_component_relative_path( $file_path, $type, $slug ) {
        $file_path = ltrim( (string) $file_path, '/\\' );

        if ( $type === 'core' ) {
            return $file_path;
        }

        if ( $type === 'plugin' ) {
            $pattern = '#^wp-content/plugins/' . preg_quote( $slug, '#' ) . '/(.*)$#';
        } elseif ( $type === 'theme' ) {
            $pattern = '#^wp-content/themes/' . preg_quote( $slug, '#' ) . '/(.*)$#';
        } else {
            return '';
        }

        if ( preg_match( $pattern, $file_path, $matches ) ) {
            return $matches[1] ?? '';
        }

        return '';
    }

    /**
     * Repair file from Cloud Vault.
     *
     * @param string $file_path Relative path from ABSPATH.
     * @param string $type
     * @param string $slug
     * @param string $version
     * @param string $rel_path
     * @return bool|\WP_Error
     */
    private function repair_from_cloud( $file_path, $type, $slug, $version, $locale, $rel_path, $expected_hash ) {
        if ( ! class_exists( '\SiteFort\Modules\Cloud\SecureApi' ) ) {
            require_once dirname( dirname( __DIR__ ) ) . '/Cloud/SecureApi.php';
        }
        if ( ! class_exists( '\SiteFort\Modules\Cloud\Services\Repair' ) ) {
            require_once dirname( dirname( __DIR__ ) ) . '/Cloud/Services/Repair.php';
        }

        $settings = CloudSettings::get_all();
        $license  = sanitize_text_field( $settings['license_key'] ?? '' );
        $token    = sanitize_text_field( $settings['site_token'] ?? '' );
        $secret   = sanitize_text_field( $settings['site_secret'] ?? '' );

        if ( $token === '' || $secret === '' ) {
            return new \WP_Error( 'cloud_auth_missing', 'Intel Cloud credentials missing. Reconnect this site in SiteFort Console.' );
        }

        $api = new \SiteFort\Modules\Cloud\SecureApi( $license, $token, $secret );
        $repair = new \SiteFort\Modules\Cloud\Services\Repair( $api );

        $abs_path = ABSPATH . ltrim( $file_path, '/\\' );

        $result = $repair->repair_file( $abs_path, $type, $slug, $version, $rel_path, $expected_hash, $locale );

        if ( $result === true ) {
            return true;
        }

        return new \WP_Error( 'cloud_repair_failed', is_string( $result ) ? $result : 'Cloud repair failed.' );
    }

}
