<?php

namespace SerpIQ\Api\Controllers;

use SerpIQ\Gsc\OAuth;
use SerpIQ\Gsc\Backfill;
use SerpIQ\Helpers\Options;

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

final class Status_Controller {
	private static ?Status_Controller $instance = null;

	public static function instance(): Status_Controller {
		if ( null === self::$instance ) {
			self::$instance = new self();
		}
		return self::$instance;
	}

	public function register_routes(): void {
		register_rest_route(
			'serpiq/v1',
			'/status',
			array(
				'methods'             => 'GET',
				'callback'            => array( $this, 'get_status' ),
				'permission_callback' => array( \SerpIQ\Api\Rest_Permission::class, 'admin_rest_request' ),
			)
		);

		register_rest_route(
			'serpiq/v1',
			'/health-check',
			array(
				'methods'             => 'GET',
				'callback'            => array( $this, 'get_health_check' ),
				'permission_callback' => array( \SerpIQ\Api\Rest_Permission::class, 'admin_rest_request' ),
			)
		);
	}

	public function get_status( \WP_REST_Request $request ): \WP_REST_Response {
		$connected = (bool) get_option( Options::google_token_encrypted_key(), '' );
		$has_credentials = (bool) get_option( Options::google_client_id_key(), '' ) && (bool) get_option( Options::google_client_secret_key(), '' );

		$status = array(
			'pluginVersion' => SERPIQ_VERSION,
			'dbVersion'     => (string) get_option( Options::db_version_key(), '0' ),
			'phpVersion'    => PHP_VERSION,
			'wpVersion'     => get_bloginfo( 'version' ),
			'siteUrl'       => home_url( '/' ),
			'deps'          => array(
				'googleClientAvailable' => OAuth::instance()->is_library_available(),
			),
			'oauth'         => array(
				'connected' => $connected,
				'hasCredentials' => $has_credentials,
			),
			'gsc'           => array(
				'property' => (string) get_option( Options::google_property_key(), '' ),
			),
			'sync'          => array(
				'lastSyncAt' => (string) get_option( Options::last_sync_at_key(), '' ),
				'cronNext'   => (string) wp_next_scheduled( 'serpiq_daily_sync' ),
				'locked'     => (bool) get_transient( 'serpiq_sync_lock' ),
			),
		);

		return new \WP_REST_Response( $status, 200 );
	}

	/**
	 * Manual health-check endpoint for support / diagnostics.
	 *
	 * Returns a snapshot of backfill state, last health check, cron info,
	 * and a very small slice of the sync log. No query strings, URLs, or
	 * user-identifiable data are exposed.
	 */
	public function get_health_check( \WP_REST_Request $request ): \WP_REST_Response {
		global $wpdb;

		$backfill_state = Backfill::instance()->get_state();
		$health         = null;

		// Always run a fresh health check when this endpoint is called so support
		// sees up-to-date information.
		try {
			$health = Backfill::instance()->run_health_check();
		} catch ( \Throwable $e ) {
			$health = array(
				'error'      => true,
				'error_type' => 'health_check_exception',
				'message'    => $e->getMessage(),
			);
		}

		$log_table = $wpdb->prefix . 'serpiq_sync_log';
		$logs      = array();
		$error_summary = array(
			'count_recent_errors' => 0,
			'last_error'          => null,
			'by_type'             => array(),
		);
		if ( $wpdb->get_var( $wpdb->prepare( 'SHOW TABLES LIKE %s', $log_table ) ) ) { // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared
			$rows = $wpdb->get_results(
				$wpdb->prepare(
					'SELECT created_at, job_type, sync_start_date, sync_end_date, rows_imported, status, duration_ms FROM %i ORDER BY created_at DESC LIMIT 10',
					$log_table
				),
				ARRAY_A
			);
			if ( is_array( $rows ) ) {
				$logs = $rows;
			}

			// Last errors (synthetic): count + last error by job_type (no full messages).
			$err_rows = $wpdb->get_results(
				$wpdb->prepare(
					"SELECT created_at, job_type, status, error_message FROM %i WHERE status = 'error' ORDER BY created_at DESC LIMIT 10",
					$log_table
				),
				ARRAY_A
			);
			if ( is_array( $err_rows ) && ! empty( $err_rows ) ) {
				$error_summary['count_recent_errors'] = count( $err_rows );
				$first = $err_rows[0];
				$error_summary['last_error'] = array(
					'at'      => (string) ( $first['created_at'] ?? '' ),
					'jobType' => (string) ( $first['job_type'] ?? '' ),
					'msg'     => $first['error_message'] ? wp_trim_words( preg_replace( '/\s+/', ' ', (string) $first['error_message'] ), 20, '…' ) : '',
				);
				$by = array();
				foreach ( $err_rows as $r ) {
					$jt = (string) ( $r['job_type'] ?? '' );
					if ( '' === $jt ) {
						$jt = 'unknown';
					}
					$by[ $jt ] = isset( $by[ $jt ] ) ? ( (int) $by[ $jt ] + 1 ) : 1;
				}
				$error_summary['by_type'] = $by;
			}
		}

		// Cron details.
		$backfill_next = wp_next_scheduled( 'serpiq_backfill_step' );
		$daily_next    = wp_next_scheduled( 'serpiq_daily_sync' );
		$now           = time();
		$cron_late_backfill = ( $backfill_next && (int) $backfill_next < ( $now - 60 ) );
		$cron_late_daily    = ( $daily_next && (int) $daily_next < ( $now - 60 ) );

		// Import coverage/progress from data (anti 0%).
		$coverage = Backfill::instance()->get_current_import_coverage();
		$covered_days  = isset( $coverage['coveredDays'] ) ? (int) $coverage['coveredDays'] : 0;
		$covered_total = isset( $coverage['totalDays'] ) ? (int) $coverage['totalDays'] : 0;

		$response = array(
			'siteUrl'       => home_url( '/' ),
			'pluginVersion' => SERPIQ_VERSION,
			'wpVersion'     => get_bloginfo( 'version' ),
			'phpVersion'    => PHP_VERSION,
			'cron'          => array(
				'now' => gmdate( 'c' ),
				'backfill_step' => array(
					'hook'      => 'serpiq_backfill_step',
					'scheduled' => (bool) $backfill_next,
					'next_run'  => $backfill_next ? gmdate( 'c', (int) $backfill_next ) : null,
					'cron_late' => (bool) $cron_late_backfill,
				),
				'daily_sync' => array(
					'hook'      => 'serpiq_daily_sync',
					'scheduled' => (bool) $daily_next,
					'next_run'  => $daily_next ? gmdate( 'c', (int) $daily_next ) : null,
					'cron_late' => (bool) $cron_late_daily,
				),
			),
			'backfill'      => array(
				'status'        => (string) ( $backfill_state['status'] ?? 'idle' ),
				'target_start'  => (string) ( $backfill_state['target_start_date'] ?? '' ),
				'target_end'    => (string) ( $backfill_state['target_end_date'] ?? '' ),
				'imported_days' => (int) ( $backfill_state['imported_days'] ?? 0 ),
				'total_days'    => (int) ( $backfill_state['total_days'] ?? 0 ),
				'from_data'     => array(
					'covered_days'  => $covered_days,
					'covered_total' => $covered_total,
					'earliest'      => isset( $coverage['earliest'] ) ? $coverage['earliest'] : null,
					'latest'        => isset( $coverage['latest'] ) ? $coverage['latest'] : null,
				),
				'created_at'    => (string) ( $backfill_state['created_at'] ?? '' ),
				'last_step_at'  => (string) ( $backfill_state['last_step_at'] ?? '' ),
				'completed_at'  => (string) ( $backfill_state['completed_at'] ?? '' ),
				'error_message' => (string) ( $backfill_state['error_message'] ?? '' ),
				'last_batch'    => array(
					'start'       => (string) ( $backfill_state['last_batch_start'] ?? '' ),
					'end'         => (string) ( $backfill_state['last_batch_end'] ?? '' ),
					'rows'        => (int) ( $backfill_state['last_batch_rows'] ?? 0 ),
					'duration_ms' => (int) ( $backfill_state['last_batch_duration'] ?? 0 ),
				),
				'features'      => array(
					'fast_start' => isset( $backfill_state['fast_start'] ) ? $backfill_state['fast_start'] : null,
					'auto_tuning'=> isset( $backfill_state['auto_tuning'] ) ? $backfill_state['auto_tuning'] : null,
					'safe_mode'  => isset( $backfill_state['safe_mode'] ) ? $backfill_state['safe_mode'] : null,
				),
				'last_health_check' => $health,
			),
			'errors'        => $error_summary,
			'logs'          => $logs,
		);

		return new \WP_REST_Response( $response, 200 );
	}
}

