<?php
/**
 * Options, résultats de scan et paliers d'accès.
 *
 * @package PCC
 * @license GPL-2.0-or-later
 */

defined( 'ABSPATH' ) || exit;

class PCC_Settings {

	const OPTION_KEY          = 'pcc_settings';
	const SCAN_STATE_KEY      = 'pcc_scan_state';
	const LAST_SCAN_KEY       = 'pcc_last_scan';
	const FALSE_POSITIVE_KEY  = 'pcc_false_positives';
	const SCAN_HISTORY_KEY    = 'pcc_scan_history';
	const EXPORT_VERSION      = '1.0.0';

	/** @var PCC_Plugin */
	private $plugin;

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

	public static function defaults() {
		return array(
			'target_php'          => '',
			'target_wp'           => '',
			'baseline_php'        => '',
			'delta_mode'          => false,
			'default_delta_mode'  => false,
			'errors_only'         => false,
			'scan_plugins'        => true,
			'scan_themes'         => true,
			'scan_mu_plugins'     => false,
			'scan_dropins'        => false,
			'scan_active_first'   => true,
			'selected_plugins'    => array(),
			'selected_themes'     => array(),
			'scan_parent'         => false,
			'skip_vendor'         => true,
			'custom_excludes'     => array(),
			'batch_size'          => 0,
			'runtime_monitor'     => true,
			'auto_rescan_update'  => false,
			'max_warnings_verdict'=> 0,
		);
	}

	public function all() {
		return wp_parse_args( get_option( self::OPTION_KEY, array() ), self::defaults() );
	}

	public function get( $key, $default = null ) {
		$all = $this->all();
		return $all[ $key ] ?? $default;
	}

	public function save( array $input ) {
		$clean = $this->sanitize( $input );
		update_option( self::OPTION_KEY, $clean );
		return $clean;
	}

	public function sanitize( array $input ) {
		$out = self::defaults();

		$allowed_targets = $this->plugin->scanner->allowed_target_versions();
		$target          = isset( $input['target_php'] ) ? sanitize_text_field( $input['target_php'] ) : '';
		if ( in_array( $target, $allowed_targets, true ) ) {
			$out['target_php'] = $target;
		} elseif ( ! empty( $allowed_targets ) ) {
			$out['target_php'] = $allowed_targets[0];
		}

		$out['scan_plugins']       = ! empty( $input['scan_plugins'] );
		$out['scan_themes']        = ! empty( $input['scan_themes'] );
		$out['scan_parent']        = $this->plugin->has_feature( 'scan_parent_theme' ) && ! empty( $input['scan_parent'] );
		$out['scan_mu_plugins']    = $this->plugin->has_feature( 'scan_all_plugins' ) && PCC_Scanner::has_mu_plugins() && ! empty( $input['scan_mu_plugins'] );
		$out['scan_dropins']       = $this->plugin->has_feature( 'scan_all_plugins' ) && PCC_Scanner::has_dropins() && ! empty( $input['scan_dropins'] );
		$out['scan_active_first']  = ! isset( $input['scan_active_first'] ) || ! empty( $input['scan_active_first'] );
		$out['skip_vendor']        = ! isset( $input['skip_vendor'] ) || ! empty( $input['skip_vendor'] );
		$out['delta_mode']         = ! empty( $input['delta_mode'] );
		$out['default_delta_mode'] = ! empty( $input['default_delta_mode'] );
		$out['errors_only']        = ! empty( $input['errors_only'] );
		$out['runtime_monitor']    = ! isset( $input['runtime_monitor'] ) || ! empty( $input['runtime_monitor'] );
		$out['auto_rescan_update'] = $this->plugin->has_feature( 'scan_all_plugins' ) && ! empty( $input['auto_rescan_update'] );

		$target_wp = isset( $input['target_wp'] ) ? sanitize_text_field( $input['target_wp'] ) : '';
		if ( $target_wp && $this->plugin->has_feature( 'scan_all_plugins' ) && in_array( $target_wp, PCC_Wp_Deprecations::available_targets(), true ) ) {
			$out['target_wp'] = $target_wp;
		} else {
			$out['target_wp'] = '';
		}

		$out['custom_excludes'] = array();
		if ( $this->plugin->has_feature( 'scan_all_plugins' ) && ! empty( $input['custom_excludes'] ) ) {
			$lines = is_array( $input['custom_excludes'] ) ? $input['custom_excludes'] : preg_split( '/\r\n|\r|\n/', (string) $input['custom_excludes'] );
			foreach ( (array) $lines as $line ) {
				$line = trim( sanitize_text_field( (string) $line ) );
				if ( $line ) {
					$out['custom_excludes'][] = $line;
				}
			}
		}

		$max_warn = isset( $input['max_warnings_verdict'] ) ? (int) $input['max_warnings_verdict'] : 0;
		$out['max_warnings_verdict'] = max( 0, min( 9999, $max_warn ) );

		$baseline = isset( $input['baseline_php'] ) ? sanitize_text_field( $input['baseline_php'] ) : '';
		if ( $baseline && preg_match( '/^\d+\.\d+$/', $baseline ) ) {
			$out['baseline_php'] = $baseline;
		} else {
			$out['baseline_php'] = phpversion();
		}

		$batch = isset( $input['batch_size'] ) ? (int) $input['batch_size'] : 0;
		$out['batch_size'] = in_array( $batch, array( 0, 10, 25, 50, 100 ), true ) ? $batch : 0;

		$out['selected_plugins'] = array();
		if ( $this->plugin->has_feature( 'scan_all_plugins' ) && ! empty( $input['selected_plugins'] ) && is_array( $input['selected_plugins'] ) ) {
			foreach ( $input['selected_plugins'] as $slug ) {
				$slug = sanitize_key( $slug );
				if ( $slug ) {
					$out['selected_plugins'][] = $slug;
				}
			}
		}

		$out['selected_themes'] = array();
		if ( $this->plugin->has_feature( 'scan_all_plugins' ) && ! empty( $input['selected_themes'] ) && is_array( $input['selected_themes'] ) ) {
			foreach ( $input['selected_themes'] as $slug ) {
				$slug = sanitize_key( $slug );
				if ( $slug ) {
					$out['selected_themes'][] = $slug;
				}
			}
		}

		return $out;
	}

	public function apply_preset( $slug ) {
		$preset = PCC_Presets::get( $slug );
		if ( ! $preset ) {
			return new WP_Error( 'invalid_preset', __( 'Unknown preset.', 'php-compatibility-checker-by-volade' ) );
		}
		return $this->save( $preset['settings'] );
	}

	/**
	 * @return array<string, mixed>
	 */
	public function get_scan_state() {
		$state = get_option( self::SCAN_STATE_KEY, array() );
		return is_array( $state ) ? $state : array();
	}

	public function set_scan_state( array $state ) {
		update_option( self::SCAN_STATE_KEY, $state, false );
	}

	public function clear_scan_state() {
		delete_option( self::SCAN_STATE_KEY );
	}

	/**
	 * @return array<string, mixed>
	 */
	public function get_last_scan() {
		$scan = get_option( self::LAST_SCAN_KEY, array() );
		return is_array( $scan ) ? $scan : array();
	}

	public function set_last_scan( array $scan ) {
		update_option( self::LAST_SCAN_KEY, $scan, false );
		$this->maybe_append_history( $scan );
	}

	/**
	 * @return array<int, array<string, mixed>>
	 */
	public function get_false_positives() {
		$list = get_option( self::FALSE_POSITIVE_KEY, array() );
		return is_array( $list ) ? $list : array();
	}

	public function add_false_positive( $fingerprint ) {
		if ( ! $this->plugin->has_feature( 'false_positive_mark' ) ) {
			return false;
		}
		$fingerprint = sanitize_text_field( $fingerprint );
		if ( '' === $fingerprint ) {
			return false;
		}
		$list = $this->get_false_positives();
		if ( ! in_array( $fingerprint, $list, true ) ) {
			$list[] = $fingerprint;
			update_option( self::FALSE_POSITIVE_KEY, $list, false );
		}
		return true;
	}

	public function is_false_positive( $fingerprint ) {
		return in_array( $fingerprint, $this->get_false_positives(), true );
	}

	public function remove_false_positive( $fingerprint ) {
		$fingerprint = sanitize_text_field( $fingerprint );
		$list        = array_values( array_diff( $this->get_false_positives(), array( $fingerprint ) ) );
		update_option( self::FALSE_POSITIVE_KEY, $list, false );
	}

	/**
	 * @return array{version:string,plugin:string,exported_at:string,fingerprints:array}
	 */
	public function export_false_positives_payload() {
		return array(
			'version'      => self::EXPORT_VERSION,
			'plugin'       => PCC_SLUG,
			'exported_at'  => gmdate( 'c' ),
			'fingerprints' => $this->get_false_positives(),
		);
	}

	public function export_false_positives_json() {
		return wp_json_encode( $this->export_false_positives_payload(), JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE );
	}

	/**
	 * @param string $json
	 * @return true|WP_Error
	 */
	public function import_false_positives_json( $json ) {
		if ( ! $this->plugin->has_feature( 'false_positive_mark' ) ) {
			return new WP_Error( 'forbidden', __( 'Volade account required.', 'php-compatibility-checker-by-volade' ) );
		}
		$data = json_decode( $json, true );
		if ( ! is_array( $data ) || empty( $data['fingerprints'] ) || ! is_array( $data['fingerprints'] ) ) {
			return new WP_Error( 'invalid', __( 'Invalid whitelist file.', 'php-compatibility-checker-by-volade' ) );
		}
		$list = array();
		foreach ( $data['fingerprints'] as $fp ) {
			$fp = sanitize_text_field( (string) $fp );
			if ( $fp ) {
				$list[] = $fp;
			}
		}
		update_option( self::FALSE_POSITIVE_KEY, array_values( array_unique( $list ) ), false );
		return true;
	}

	/**
	 * Compare deux entrées d'historique (régressions).
	 *
	 * @param string $id_a
	 * @param string $id_b
	 * @return array|WP_Error
	 */
	public function compare_history( $id_a, $id_b ) {
		if ( ! $this->plugin->has_feature( 'scan_history' ) ) {
			return new WP_Error( 'forbidden', __( 'Scan history requires V+ Premium.', 'php-compatibility-checker-by-volade' ) );
		}
		$history = get_option( self::SCAN_HISTORY_KEY, array() );
		$a = $b = null;
		foreach ( (array) $history as $row ) {
			if ( ( $row['id'] ?? '' ) === $id_a ) {
				$a = $row;
			}
			if ( ( $row['id'] ?? '' ) === $id_b ) {
				$b = $row;
			}
		}
		if ( ! $a || ! $b ) {
			return new WP_Error( 'not_found', __( 'Scan not found in history.', 'php-compatibility-checker-by-volade' ) );
		}

		$component_delta = array();
		$all_ids         = array_unique(
			array_merge(
				array_keys( (array) ( $a['by_component'] ?? array() ) ),
				array_keys( (array) ( $b['by_component'] ?? array() ) )
			)
		);
		foreach ( $all_ids as $cid ) {
			$ae = (int) ( $a['by_component'][ $cid ]['errors'] ?? 0 );
			$be = (int) ( $b['by_component'][ $cid ]['errors'] ?? 0 );
			$aw = (int) ( $a['by_component'][ $cid ]['warnings'] ?? 0 );
			$bw = (int) ( $b['by_component'][ $cid ]['warnings'] ?? 0 );
			if ( $ae === $be && $aw === $bw ) {
				continue;
			}
			$component_delta[] = array(
				'id'       => $cid,
				'label'    => $b['by_component'][ $cid ]['label'] ?? $a['by_component'][ $cid ]['label'] ?? $cid,
				'errors'   => $be - $ae,
				'warnings' => $bw - $aw,
			);
		}

		usort(
			$component_delta,
			function ( $x, $y ) {
				return abs( $y['errors'] ) <=> abs( $x['errors'] );
			}
		);

		return array(
			'older'             => $a,
			'newer'             => $b,
			'delta'             => array(
				'errors'   => (int) ( $b['errors'] ?? 0 ) - (int) ( $a['errors'] ?? 0 ),
				'warnings' => (int) ( $b['warnings'] ?? 0 ) - (int) ( $a['warnings'] ?? 0 ),
			),
			'component_delta'   => $component_delta,
		);
	}

	/**
	 * @return array<int, array<string, mixed>>
	 */
	public function get_scan_history() {
		if ( ! $this->plugin->has_feature( 'scan_history' ) ) {
			return array();
		}
		$history = get_option( self::SCAN_HISTORY_KEY, array() );
		if ( ! is_array( $history ) ) {
			return array();
		}
		$limit = $this->plugin->feature_limit( 'scan_history' );
		if ( $limit > 0 && count( $history ) > $limit ) {
			$history = array_slice( $history, -$limit );
		}
		return $history;
	}

	private function maybe_append_history( array $scan ) {
		if ( ! $this->plugin->has_feature( 'scan_history' ) || empty( $scan['completed_at'] ) ) {
			return;
		}
		$history   = get_option( self::SCAN_HISTORY_KEY, array() );
		$history   = is_array( $history ) ? $history : array();
		$history[] = array(
			'id'             => $scan['id'] ?? uniqid( 'pcc_', true ),
			'completed_at'   => $scan['completed_at'],
			'target_php'     => $scan['target_php'] ?? '',
			'verdict'        => $scan['verdict'] ?? 'unknown',
			'errors'         => (int) ( $scan['summary']['errors'] ?? 0 ),
			'warnings'       => (int) ( $scan['summary']['warnings'] ?? 0 ),
			'by_component'   => $scan['by_component'] ?? array(),
		);
		$limit = $this->plugin->feature_limit( 'scan_history' );
		if ( $limit > 0 && count( $history ) > $limit ) {
			$history = array_slice( $history, -$limit );
		}
		update_option( self::SCAN_HISTORY_KEY, $history, false );
	}

	/**
	 * @return array{version:string,plugin:string,exported_at:string,scan:array}
	 */
	public function export_scan_payload() {
		return array(
			'version'     => self::EXPORT_VERSION,
			'plugin'      => PCC_SLUG,
			'exported_at' => gmdate( 'c' ),
			'scan'        => $this->get_last_scan(),
		);
	}

	public function export_scan_json() {
		return wp_json_encode( $this->export_scan_payload(), JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE );
	}

	public function export_scan_csv() {
		if ( ! $this->plugin->has_feature( 'export_csv' ) ) {
			return '';
		}
		$scan     = $this->get_last_scan();
		$findings = $scan['findings'] ?? array();
		$lines    = array( 'severity,component,file,line,message' );
		foreach ( $findings as $row ) {
			$lines[] = sprintf(
				'%s,%s,%s,%d,%s',
				$this->csv_cell( $row['severity'] ?? '' ),
				$this->csv_cell( $row['component'] ?? '' ),
				$this->csv_cell( $row['file'] ?? '' ),
				(int) ( $row['line'] ?? 0 ),
				$this->csv_cell( $row['message'] ?? '' )
			);
		}
		return implode( "\n", $lines );
	}

	private function csv_cell( $value ) {
		$value = str_replace( array( '"', "\n", "\r" ), array( '""', ' ', ' ' ), (string) $value );
		return '"' . $value . '"';
	}

	/**
	 * Résumé badge pour un plugin (dernier scan).
	 *
	 * @param string $plugin_file Plugin basename path.
	 * @return string ready|warning|error|unknown
	 */
	public function get_plugin_badge_status( $plugin_file ) {
		$scan = $this->get_last_scan();
		if ( empty( $scan['by_component'] ) || ! is_array( $scan['by_component'] ) ) {
			return 'unknown';
		}
		$slug = dirname( $plugin_file );
		if ( '.' === $slug ) {
			$slug = basename( $plugin_file, '.php' );
		}
		$comp = $scan['by_component'][ 'plugin:' . $slug ] ?? null;
		if ( ! $comp ) {
			return 'unknown';
		}
		if ( ! empty( $comp['errors'] ) ) {
			return 'error';
		}
		if ( ! empty( $comp['warnings'] ) ) {
			return 'warning';
		}
		return 'ready';
	}

	/**
	 * @param string $stylesheet Theme slug.
	 * @return string ready|warning|error|unknown
	 */
	public function get_theme_badge_status( $stylesheet ) {
		$scan = $this->get_last_scan();
		if ( empty( $scan['by_component'] ) || ! is_array( $scan['by_component'] ) ) {
			return 'unknown';
		}
		$comp = $scan['by_component'][ 'theme:' . $stylesheet ] ?? null;
		if ( ! $comp ) {
			return 'unknown';
		}
		if ( ! empty( $comp['errors'] ) ) {
			return 'error';
		}
		if ( ! empty( $comp['warnings'] ) ) {
			return 'warning';
		}
		return 'ready';
	}
}
