<?php

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

/**
 * Live editor checks and per-content evidence references.
 */
final class ClaimPrism_Editor {
	const EVIDENCE_META = '_claimprism_evidence';

	/** @var ClaimPrism_Scanner */
	private $scanner;

	public function __construct( ClaimPrism_Scanner $scanner ) {
		$this->scanner = $scanner;

		add_action( 'rest_api_init', array( $this, 'register_rest_routes' ) );
		add_action( 'add_meta_boxes', array( $this, 'add_meta_boxes' ) );
		add_action( 'save_post', array( $this, 'save_evidence' ), 30, 2 );
		add_action( 'admin_enqueue_scripts', array( $this, 'enqueue_editor_assets' ) );
	}

	public function register_rest_routes() {
		register_rest_route(
			'claimprism/v1',
			'/scan',
			array(
				'methods'             => WP_REST_Server::CREATABLE,
				'callback'            => array( $this, 'rest_scan' ),
				'permission_callback' => array( $this, 'can_scan' ),
				'args'                => array(
					'text' => array(
						'type'     => 'string',
						'required' => true,
					),
				),
			)
		);
	}

	public function can_scan() {
		return current_user_can( 'edit_posts' ) || current_user_can( 'edit_products' );
	}

	public function rest_scan( WP_REST_Request $request ) {
		$text     = (string) $request->get_param( 'text' );
		if ( strlen( $text ) > 1000000 ) {
			return new WP_Error( 'claimprism_text_too_large', __( 'Content is too large for a live check.', 'claimprism' ), array( 'status' => 413 ) );
		}
		$findings = $this->scanner->scan_text( $text );
		$response = array();

		foreach ( $findings as $finding ) {
			$response[] = array(
				'claim_key'      => $finding['claim_key'],
				'rule_id'        => $finding['rule_id'],
				'rule_label'     => $finding['rule_label'],
				'severity'       => $finding['severity'],
				'matched_phrase' => $finding['matched_phrase'],
				'context'        => $finding['context'],
				'description'    => $finding['description'],
				'source_url'     => $finding['source_url'],
			);
		}

		return rest_ensure_response(
			array(
				'findings' => $response,
				'total'    => count( $response ),
			)
		);
	}

	public function add_meta_boxes() {
		foreach ( ClaimPrism_Scanner::supported_post_types() as $post_type ) {
			add_meta_box(
				'claimprism-live',
				__( 'ClaimPrism — Live Check', 'claimprism' ),
				array( $this, 'render_live_box' ),
				$post_type,
				'side',
				'high'
			);

			add_meta_box(
				'claimprism-evidence',
				__( 'ClaimPrism — Evidence', 'claimprism' ),
				array( $this, 'render_evidence_box' ),
				$post_type,
				'normal',
				'default'
			);
		}
	}

	public function render_live_box() {
		?>
		<div id="claimprism-live-panel" class="cg-live-panel">
			<p class="cg-live-status" aria-live="polite"><?php esc_html_e( 'Waiting for content…', 'claimprism' ); ?></p>
			<div class="cg-live-summary" hidden></div>
			<div class="cg-live-findings"></div>
			<button type="button" class="button cg-check-now"><?php esc_html_e( 'Check now', 'claimprism' ); ?></button>
			<p class="description"><?php esc_html_e( 'Automated risk signals only. Human review remains necessary.', 'claimprism' ); ?></p>
		</div>
		<?php
	}

	public function render_evidence_box( $post ) {
		$entries = self::get_evidence( $post->ID );
		$claims  = $this->scanner->get_findings_for_post( $post->ID );
		wp_nonce_field( 'claimprism_save_evidence', 'claimprism_evidence_nonce' );
		?>
		<p><?php esc_html_e( 'Record the evidence or certification associated with environmental claims on this content. ClaimPrism stores references locally and does not treat an entry as proof of compliance.', 'claimprism' ); ?></p>
		<div id="claimprism-evidence-list">
			<?php
			if ( empty( $entries ) ) {
				$this->render_evidence_row( array(), 0, $claims );
			} else {
				foreach ( $entries as $index => $entry ) {
					$this->render_evidence_row( $entry, $index, $claims );
				}
			}
			?>
		</div>
		<button type="button" class="button cg-add-evidence"><?php esc_html_e( 'Add evidence', 'claimprism' ); ?></button>
		<script type="text/html" id="tmpl-claimprism-evidence-row">
			<?php $this->render_evidence_row( array(), '{{INDEX}}', $claims ); ?>
		</script>
		<?php
	}

	private function render_evidence_row( $entry, $index, $claims ) {
		$entry = wp_parse_args(
			$entry,
			array(
				'claim_key' => '',
				'label'     => '',
				'issuer'    => '',
				'url'       => '',
				'expires'   => '',
				'notes'     => '',
			)
		);
		$name = 'claimprism_evidence[' . $index . ']';
		?>
		<div class="cg-evidence-row">
			<label class="cg-evidence-claim"><?php esc_html_e( 'Linked claim', 'claimprism' ); ?>
				<select name="<?php echo esc_attr( $name ); ?>[claim_key]">
					<option value=""><?php esc_html_e( 'General evidence — not linked to one finding', 'claimprism' ); ?></option>
					<?php
					$known_keys = array();
					foreach ( $claims as $claim ) {
						if ( isset( $known_keys[ $claim['claim_key'] ] ) ) {
							continue;
						}
						$known_keys[ $claim['claim_key'] ] = true;
						$option_label = sprintf( '%s — %s', $claim['matched_phrase'], $claim['rule_label'] );
						?>
						<option value="<?php echo esc_attr( $claim['claim_key'] ); ?>" <?php selected( $entry['claim_key'], $claim['claim_key'] ); ?>><?php echo esc_html( $option_label ); ?></option>
						<?php
					}
					if ( $entry['claim_key'] && ! isset( $known_keys[ $entry['claim_key'] ] ) ) :
						?>
						<option value="<?php echo esc_attr( $entry['claim_key'] ); ?>" selected><?php esc_html_e( 'Previously linked claim — no longer detected', 'claimprism' ); ?></option>
					<?php endif; ?>
				</select>
			</label>
			<div class="cg-evidence-grid">
				<label><?php esc_html_e( 'Evidence / certificate', 'claimprism' ); ?><input type="text" name="<?php echo esc_attr( $name ); ?>[label]" value="<?php echo esc_attr( $entry['label'] ); ?>" placeholder="EU Ecolabel"></label>
				<label><?php esc_html_e( 'Issuer', 'claimprism' ); ?><input type="text" name="<?php echo esc_attr( $name ); ?>[issuer]" value="<?php echo esc_attr( $entry['issuer'] ); ?>"></label>
				<label><?php esc_html_e( 'Reference URL', 'claimprism' ); ?><input type="url" name="<?php echo esc_attr( $name ); ?>[url]" value="<?php echo esc_attr( $entry['url'] ); ?>"></label>
				<label><?php esc_html_e( 'Expiry', 'claimprism' ); ?><input type="date" name="<?php echo esc_attr( $name ); ?>[expires]" value="<?php echo esc_attr( $entry['expires'] ); ?>"></label>
			</div>
			<label class="cg-evidence-notes"><?php esc_html_e( 'Notes', 'claimprism' ); ?><textarea name="<?php echo esc_attr( $name ); ?>[notes]" rows="2"><?php echo esc_textarea( $entry['notes'] ); ?></textarea></label>
			<button type="button" class="button-link-delete cg-remove-evidence"><?php esc_html_e( 'Remove', 'claimprism' ); ?></button>
		</div>
		<?php
	}

	public function save_evidence( $post_id, $post ) {
		if ( wp_is_post_revision( $post_id ) || wp_is_post_autosave( $post_id ) ) {
			return;
		}

		if ( ! in_array( $post->post_type, ClaimPrism_Scanner::supported_post_types(), true ) ) {
			return;
		}

		if ( ! isset( $_POST['claimprism_evidence_nonce'] ) || ! wp_verify_nonce( sanitize_text_field( wp_unslash( $_POST['claimprism_evidence_nonce'] ) ), 'claimprism_save_evidence' ) ) {
			return;
		}

		if ( ! current_user_can( 'edit_post', $post_id ) ) {
			return;
		}

		// phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized -- Each submitted evidence field is sanitized by type below after unslashing.
		$raw_entries = isset( $_POST['claimprism_evidence'] ) && is_array( $_POST['claimprism_evidence'] ) ? wp_unslash( $_POST['claimprism_evidence'] ) : array();
		$entries     = array();
		$allowed_claim_keys = array();
		foreach ( $this->scanner->get_findings_for_post( $post_id ) as $finding ) {
			$allowed_claim_keys[ $finding['claim_key'] ] = true;
		}
		foreach ( self::get_evidence( $post_id ) as $existing_entry ) {
			if ( ! empty( $existing_entry['claim_key'] ) ) {
				$allowed_claim_keys[ $existing_entry['claim_key'] ] = true;
			}
		}

		foreach ( $raw_entries as $raw ) {
			if ( ! is_array( $raw ) ) {
				continue;
			}

			$claim_key = isset( $raw['claim_key'] ) ? sanitize_text_field( $raw['claim_key'] ) : '';
			$entry = array(
				'claim_key' => preg_match( '/^[a-f0-9]{64}$/', $claim_key ) && isset( $allowed_claim_keys[ $claim_key ] ) ? $claim_key : '',
				'label'     => isset( $raw['label'] ) ? sanitize_text_field( $raw['label'] ) : '',
				'issuer'    => isset( $raw['issuer'] ) ? sanitize_text_field( $raw['issuer'] ) : '',
				'url'       => isset( $raw['url'] ) ? esc_url_raw( $raw['url'] ) : '',
				'expires'   => isset( $raw['expires'] ) && self::valid_date( $raw['expires'] ) ? $raw['expires'] : '',
				'notes'     => isset( $raw['notes'] ) ? sanitize_textarea_field( $raw['notes'] ) : '',
			);

			if ( array_filter( array( $entry['label'], $entry['issuer'], $entry['url'], $entry['expires'], $entry['notes'] ) ) ) {
				$entries[] = $entry;
			}
		}

		if ( empty( $entries ) ) {
			delete_post_meta( $post_id, self::EVIDENCE_META );
			do_action( 'claimprism_evidence_saved', $post_id, array() );
			return;
		}

		$entries = apply_filters( 'claimprism_evidence_entries', $entries, $post_id );
		update_post_meta( $post_id, self::EVIDENCE_META, $entries );
		do_action( 'claimprism_evidence_saved', $post_id, $entries );
	}

	public static function get_evidence( $post_id ) {
		$entries = get_post_meta( (int) $post_id, self::EVIDENCE_META, true );
		return is_array( $entries ) ? $entries : array();
	}

	public static function get_evidence_alerts( $days = 30 ) {
		global $wpdb;
		$days      = max( 0, min( 365, (int) $days ) );
		$today     = current_time( 'Y-m-d' );
		$threshold = gmdate( 'Y-m-d', strtotime( $today . ' +' . $days . ' days' ) );
		// phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery,WordPress.DB.DirectDatabaseQuery.NoCaching -- Expiry reporting intentionally scans current evidence metadata; caching here risks stale dashboard alerts.
		$rows      = $wpdb->get_results(
			$wpdb->prepare( "SELECT post_id, meta_value FROM {$wpdb->postmeta} WHERE meta_key = %s", self::EVIDENCE_META ),
			ARRAY_A
		);
		$alerts = array();

		foreach ( $rows as $row ) {
			if ( ! get_post_status( (int) $row['post_id'] ) ) {
				continue;
			}

			$entries = maybe_unserialize( $row['meta_value'] );
			if ( ! is_array( $entries ) ) {
				continue;
			}

			foreach ( $entries as $entry ) {
				if ( empty( $entry['expires'] ) || ! self::valid_date( $entry['expires'] ) || $entry['expires'] > $threshold ) {
					continue;
				}

				$alerts[] = array(
					'post_id'   => (int) $row['post_id'],
					'label'     => ! empty( $entry['label'] ) ? $entry['label'] : __( 'Evidence', 'claimprism' ),
					'expires'   => $entry['expires'],
					'status'    => $entry['expires'] < $today ? 'expired' : 'expiring',
					'claim_key' => ! empty( $entry['claim_key'] ) ? $entry['claim_key'] : '',
				);
			}
		}

		usort(
			$alerts,
			function ( $a, $b ) {
				if ( $a['status'] !== $b['status'] ) {
					return 'expired' === $a['status'] ? -1 : 1;
				}
				return strcmp( $a['expires'], $b['expires'] );
			}
		);

		return apply_filters( 'claimprism_evidence_alerts', $alerts, $days );
	}

	private static function valid_date( $date ) {
		if ( ! is_string( $date ) || ! preg_match( '/^\d{4}-\d{2}-\d{2}$/', $date ) ) {
			return false;
		}

		$parsed = DateTimeImmutable::createFromFormat( '!Y-m-d', $date );
		return $parsed && $parsed->format( 'Y-m-d' ) === $date;
	}

	public function enqueue_editor_assets( $hook ) {
		if ( ! in_array( $hook, array( 'post.php', 'post-new.php' ), true ) ) {
			return;
		}

		$screen = get_current_screen();
		if ( ! $screen || ! in_array( $screen->post_type, ClaimPrism_Scanner::supported_post_types(), true ) ) {
			return;
		}

		wp_enqueue_style( 'claimprism-editor', CLAIMPRISM_URL . 'assets/editor.css', array(), CLAIMPRISM_VERSION );
		wp_enqueue_script( 'claimprism-editor', CLAIMPRISM_URL . 'assets/editor.js', array( 'wp-api-fetch', 'wp-data', 'wp-dom-ready' ), CLAIMPRISM_VERSION, true );
		wp_localize_script(
			'claimprism-editor',
			'ClaimPrism',
			array(
				'endpoint' => '/claimprism/v1/scan',
				'nonce'    => wp_create_nonce( 'wp_rest' ),
				'strings'  => array(
					'checking' => __( 'Checking…', 'claimprism' ),
					'clear'    => __( 'No current risk signals found.', 'claimprism' ),
					'found'    => __( 'risk signals found', 'claimprism' ),
					'error'    => __( 'Live check could not run. Saved-content scanning remains active.', 'claimprism' ),
				),
			)
		);
	}
}
