<?php
/**
 * Plugin Bootstrap
 *
 * @package MBMaxCf7Relay
 */

namespace MBMaxCf7Relay\Includes;

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

/**
 * Основной класс плагина.
 */
class Plugin {

	/**
	 * Ключ опций.
	 */
	const OPTION_KEY = 'mb_max_cf7_relay_options';

	/**
	 * Базовый URL портала. Все запросы идут только через него.
	 */
	const PORTAL_BASE_URL = 'https://cacr.ru';

	/**
	 * Опции по умолчанию.
	 */
	const DEFAULT_OPTIONS = array(
		'integration_key'   => '',
		'license_ttl'       => 300,
		'debug_mode'        => 0,
	);

	/**
	 * Экземпляр.
	 *
	 * @var Plugin|null
	 */
	private static $instance = null;

	/**
	 * Сервис проверки лицензии.
	 *
	 * @var Services\LicenseService
	 */
	private $license_service;

	/**
	 * Сервис отправки лидов.
	 *
	 * @var Services\LeadForwarder
	 */
	private $lead_forwarder;

	/**
	 * Получить экземпляр.
	 *
	 * @return Plugin
	 */
	public static function get_instance() {
		if ( null === self::$instance ) {
			self::$instance = new self();
		}

		return self::$instance;
	}

	/**
	 * Инициализация хуков.
	 *
	 * @return void
	 */
	public function init() {
		$this->license_service = new Services\LicenseService();
		$this->lead_forwarder  = new Services\LeadForwarder( $this->license_service );

		$settings_page = new Admin\SettingsPage( $this->license_service );
		$settings_page->register_hooks();

		add_action( 'wpcf7_mail_sent', array( $this, 'handle_cf7_submission' ), 20, 1 );
		add_action( 'wpcf7_mail_failed', array( $this, 'handle_cf7_submission' ), 20, 1 );
		add_action( 'wp_mail_succeeded', array( $this, 'handle_mail_submission' ), 20, 1 );
		add_action( 'rest_api_init', array( $this, 'register_rest_routes' ) );
		add_action( 'wp_ajax_mb_max_cf7_onboarding_state', array( $this, 'ajax_onboarding_state' ) );
		add_action( 'wp_ajax_mb_max_cf7_onboarding_bind_code', array( $this, 'ajax_onboarding_bind_code' ) );
		add_action( 'wp_ajax_mb_max_cf7_onboarding_verify', array( $this, 'ajax_onboarding_verify' ) );
		add_filter( 'plugin_action_links_' . plugin_basename( MB_MAX_CF7_RELAY_FILE ), array( $this, 'add_action_links' ) );
	}

	/**
	 * Получить актуальные настройки плагина.
	 *
	 * @return array
	 */
	public static function get_options() {
		$stored = get_option( self::OPTION_KEY, array() );
		if ( ! is_array( $stored ) ) {
			$stored = array();
		}

		$options = wp_parse_args( $stored, self::DEFAULT_OPTIONS );
		$options['integration_key']   = trim( (string) $options['integration_key'] );
		$options['license_ttl']       = max( 60, (int) $options['license_ttl'] );
		$options['debug_mode']        = empty( $options['debug_mode'] ) ? 0 : 1;

		return $options;
	}

	/**
	 * Сохранить настройки плагина.
	 *
	 * @param array $options Настройки.
	 * @return void
	 */
	public static function save_options( $options ) {
		$normalized = wp_parse_args( $options, self::DEFAULT_OPTIONS );
		$normalized['integration_key'] = sanitize_text_field( (string) $normalized['integration_key'] );
		$normalized['license_ttl'] = max( 60, (int) $normalized['license_ttl'] );
		$normalized['debug_mode'] = empty( $normalized['debug_mode'] ) ? 0 : 1;

		update_option( self::OPTION_KEY, $normalized );
	}

	/**
	 * Получить URL портала.
	 *
	 * @return string
	 */
	public static function get_portal_base_url() {
		return self::PORTAL_BASE_URL;
	}

	/**
	 * Отправить JSON POST-запрос в CACR с fallback на IPv4 при сетевых сбоях.
	 *
	 * @param string $url URL запроса.
	 * @param array  $payload Payload запроса.
	 * @param int    $timeout Таймаут в секундах.
	 * @return array|\WP_Error
	 */
	public static function post_portal_json( $url, $payload, $timeout = 15 ) {
		$args = array(
			'timeout' => max( 1, (int) $timeout ),
			'headers' => array( 'Content-Type' => 'application/json' ),
			'body'    => wp_json_encode( $payload ),
		);

		$response = wp_remote_post( $url, $args );
		if ( ! self::should_retry_portal_request_over_ipv4( $url, $response ) ) {
			return $response;
		}

		$force_ipv4 = static function ( $handle, $parsed_args, $request_url ) {
			if ( ! self::is_portal_url( $request_url ) ) {
				return;
			}

			curl_setopt( $handle, CURLOPT_IPRESOLVE, CURL_IPRESOLVE_V4 );
		};

		add_action( 'http_api_curl', $force_ipv4, 10, 3 );

		try {
			return wp_remote_post( $url, $args );
		} finally {
			remove_action( 'http_api_curl', $force_ipv4, 10 );
		}
	}

	/**
	 * Нужно ли повторить запрос к CACR через IPv4.
	 *
	 * @param string          $url URL запроса.
	 * @param array|\WP_Error $response Ответ WordPress HTTP API.
	 * @return bool
	 */
	private static function should_retry_portal_request_over_ipv4( $url, $response ) {
		if ( ! self::can_force_portal_ipv4( $url ) || ! is_wp_error( $response ) ) {
			return false;
		}

		$message = strtolower( $response->get_error_message() );
		$markers = array(
			'curl error 7',
			'curl error 28',
			'failed to connect',
			'couldn\'t connect',
			'could not connect',
			'connection timed out',
			'operation timed out',
			'network is unreachable',
		);

		foreach ( $markers as $marker ) {
			if ( false !== strpos( $message, $marker ) ) {
				return true;
			}
		}

		return false;
	}

	/**
	 * Можно ли принудительно использовать IPv4 для CACR-запроса.
	 *
	 * @param string $url URL запроса.
	 * @return bool
	 */
	private static function can_force_portal_ipv4( $url ) {
		return self::is_portal_url( $url )
			&& function_exists( 'curl_setopt' )
			&& defined( 'CURLOPT_IPRESOLVE' )
			&& defined( 'CURL_IPRESOLVE_V4' );
	}

	/**
	 * Проверить, относится ли URL к порталу CACR.
	 *
	 * @param string $url URL запроса.
	 * @return bool
	 */
	private static function is_portal_url( $url ) {
		$host        = (string) wp_parse_url( $url, PHP_URL_HOST );
		$portal_host = (string) wp_parse_url( self::get_portal_base_url(), PHP_URL_HOST );

		return '' !== $host && '' !== $portal_host && strtolower( $host ) === strtolower( $portal_host );
	}

	/**
	 * Обработчик отправки формы Contact Form 7.
	 *
	 * @param \WPCF7_ContactForm $contact_form Форма.
	 * @return void
	 */
	public function handle_cf7_submission( $contact_form ) {
		$this->lead_forwarder->forward_from_cf7( $contact_form );
	}

	/**
	 * Обработчик универсальной отправки из форм, которые используют wp_mail.
	 *
	 * @param array $mail_data Данные успешно отправленного письма.
	 * @return void
	 */
	public function handle_mail_submission( $mail_data ) {
		$this->lead_forwarder->forward_from_wp_mail( $mail_data );
	}

	/**
	 * Регистрация REST маршрутов плагина.
	 *
	 * @return void
	 */
	public function register_rest_routes() {
		register_rest_route(
			'mb/v1',
			'/integration/ping',
			array(
				'methods'             => 'POST',
				'callback'            => array( $this, 'handle_integration_ping' ),
				'permission_callback' => '__return_true',
			)
		);
	}

	/**
	 * Ответ на ping из кабинета CACR.
	 *
	 * @param \WP_REST_Request $request Запрос.
	 * @return \WP_REST_Response
	 */
	public function handle_integration_ping( $request ) {
		$params  = $request->get_json_params();
		$params  = is_array( $params ) ? $params : $request->get_params();

		$incoming_token = isset( $params['site_token'] ) ? sanitize_text_field( (string) $params['site_token'] ) : '';
		$incoming_bot   = isset( $params['bot_key'] ) ? sanitize_key( (string) $params['bot_key'] ) : '';

		if ( 'tg_cf7' === $incoming_bot && class_exists( '\\MBTgCf7Relay\\Includes\\Plugin' ) ) {
			return \MBTgCf7Relay\Includes\Plugin::get_instance()->handle_integration_ping( $request );
		}

		$options = self::get_options();

		if ( '' !== $incoming_bot && 'max_cf7' !== $incoming_bot ) {
			return new \WP_REST_Response(
				array(
					'success' => false,
					'code'    => 'invalid_bot_key',
					'message' => 'Поддерживается только bot_key=max_cf7.',
				),
				400
			);
		}

		if ( '' === $options['integration_key'] ) {
			return new \WP_REST_Response(
				array(
					'success' => false,
					'code'    => 'integration_key_missing_local',
					'message' => 'На сайте не задан ключ интеграции из кабинета CACR.',
				),
				412
			);
		}

		if ( '' === $incoming_token ) {
			return new \WP_REST_Response(
				array(
					'success' => false,
					'code'    => 'site_token_missing',
					'message' => 'В ping-запросе отсутствует site_token.',
				),
				400
			);
		}

		if ( ! hash_equals( $options['integration_key'], $incoming_token ) ) {
			return new \WP_REST_Response(
				array(
					'success' => false,
					'code'    => 'invalid_token',
					'message' => 'Ключ интеграции не совпадает.',
				),
				401
			);
		}

		$license = $this->license_service->check_license( true );

		return new \WP_REST_Response(
			array(
				'success' => true,
				'data'    => array(
					'plugin'         => 'cacr-relay-cf7-max',
					'version'        => MB_MAX_CF7_RELAY_VERSION,
					'site_url'       => home_url( '/' ),
					'license_active' => ! empty( $license['active'] ),
				),
			),
			200
		);
	}

	/**
	 * Ссылка "Настройки" в списке плагинов.
	 *
	 * @param array $links Ссылки.
	 * @return array
	 */
	public function add_action_links( $links ) {
		$settings_link = '<a href="' . esc_url( admin_url( 'options-general.php?page=cacr-relay-cf7-max' ) ) . '">Настройки</a>';
		array_unshift( $links, $settings_link );
		return $links;
	}

	/**
	 * AJAX: получить состояние onboarding.
	 *
	 * @return void
	 */
	public function ajax_onboarding_state() {
		$this->assert_onboarding_ajax_permissions();
		if ( ! check_ajax_referer( 'mb_max_cf7_onboarding', 'nonce', false ) ) {
			wp_send_json_error(
				array(
					'code'    => 'invalid_nonce',
					'message' => 'Сессия устарела. Обновите страницу и повторите.',
				),
				403
			);
		}

		$payload = $this->build_portal_payload_base();
		if ( is_wp_error( $payload ) ) {
			wp_send_json_error(
				array(
					'code'    => sanitize_key( (string) $payload->get_error_code() ),
					'message' => $payload->get_error_message(),
				),
				400
			);
		}

		$result = $this->proxy_portal_request( '/max-cf7/setup-state', $payload );
		if ( is_wp_error( $result ) ) {
			wp_send_json_error(
				array(
					'code'    => sanitize_key( (string) $result->get_error_code() ),
					'message' => $result->get_error_message(),
					'data'    => $result->get_error_data(),
				),
				400
			);
		}

		wp_send_json_success( $result );
	}

	/**
	 * AJAX: получить bind-команду для выбранного режима.
	 *
	 * @return void
	 */
	public function ajax_onboarding_bind_code() {
		$this->assert_onboarding_ajax_permissions();
		if ( ! check_ajax_referer( 'mb_max_cf7_onboarding', 'nonce', false ) ) {
			wp_send_json_error(
				array(
					'code'    => 'invalid_nonce',
					'message' => 'Сессия устарела. Обновите страницу и повторите.',
				),
				403
			);
		}

		$payload = $this->build_portal_payload_base();
		if ( is_wp_error( $payload ) ) {
			wp_send_json_error(
				array(
					'code'    => sanitize_key( (string) $payload->get_error_code() ),
					'message' => $payload->get_error_message(),
				),
				400
			);
		}

		$recipient_type = isset( $_POST['recipient_type'] ) ? sanitize_key( wp_unslash( (string) $_POST['recipient_type'] ) ) : '';
		if ( ! in_array( $recipient_type, array( 'direct', 'group' ), true ) ) {
			$recipient_type = 'direct';
		}
		$payload['recipient_type'] = $recipient_type;

		$result = $this->proxy_portal_request( '/max-cf7/setup-bind-code', $payload );
		if ( is_wp_error( $result ) ) {
			wp_send_json_error(
				array(
					'code'    => sanitize_key( (string) $result->get_error_code() ),
					'message' => $result->get_error_message(),
					'data'    => $result->get_error_data(),
				),
				400
			);
		}

		wp_send_json_success( $result );
	}

	/**
	 * AJAX: проверить привязку (форсирует polling updates на портале).
	 *
	 * @return void
	 */
	public function ajax_onboarding_verify() {
		$this->assert_onboarding_ajax_permissions();
		if ( ! check_ajax_referer( 'mb_max_cf7_onboarding', 'nonce', false ) ) {
			wp_send_json_error(
				array(
					'code'    => 'invalid_nonce',
					'message' => 'Сессия устарела. Обновите страницу и повторите.',
				),
				403
			);
		}

		$payload = $this->build_portal_payload_base();
		if ( is_wp_error( $payload ) ) {
			wp_send_json_error(
				array(
					'code'    => sanitize_key( (string) $payload->get_error_code() ),
					'message' => $payload->get_error_message(),
				),
				400
			);
		}

		$result = $this->proxy_portal_request( '/max-cf7/setup-verify', $payload );
		if ( is_wp_error( $result ) ) {
			wp_send_json_error(
				array(
					'code'    => sanitize_key( (string) $result->get_error_code() ),
					'message' => $result->get_error_message(),
					'data'    => $result->get_error_data(),
				),
				400
			);
		}

		wp_send_json_success( $result );
	}

	/**
	 * Проверить доступ к onboarding AJAX.
	 *
	 * @return void
	 */
	private function assert_onboarding_ajax_permissions() {
		if ( ! current_user_can( 'manage_options' ) ) {
			wp_send_json_error(
				array(
					'code'    => 'forbidden',
					'message' => 'Недостаточно прав.',
				),
				403
			);
		}
	}

	/**
	 * Базовый payload для запросов в портал CACR.
	 *
	 * @return array|\WP_Error
	 */
	private function build_portal_payload_base() {
		$options = self::get_options();
		if ( '' === $options['integration_key'] ) {
			return new \WP_Error( 'integration_key_missing', 'Сначала укажите и сохраните ключ интеграции.' );
		}

		return array(
			'integration_key' => (string) $options['integration_key'],
			'site_url'        => home_url( '/' ),
			'site_domain'     => (string) wp_parse_url( home_url( '/' ), PHP_URL_HOST ),
		);
	}

	/**
	 * Проксировать запрос в портал CACR.
	 *
	 * @param string $route Маршрут API в namespace `mb/v1`.
	 * @param array  $payload Payload запроса.
	 * @return array|\WP_Error
	 */
	private function proxy_portal_request( $route, $payload ) {
		$url = untrailingslashit( self::get_portal_base_url() ) . '/wp-json/mb/v1' . $route;
		$response = self::post_portal_json( $url, $payload, 20 );

		if ( is_wp_error( $response ) ) {
			return new \WP_Error( 'portal_request_failed', 'Не удалось выполнить запрос к CACR: ' . $response->get_error_message() );
		}

		$code = (int) wp_remote_retrieve_response_code( $response );
		$raw  = wp_remote_retrieve_body( $response );
		$data = json_decode( (string) $raw, true );

		if ( $code < 200 || $code >= 300 || ! is_array( $data ) || empty( $data['success'] ) ) {
			$error_code = is_array( $data ) && ! empty( $data['code'] ) ? sanitize_key( (string) $data['code'] ) : 'portal_error';
			$message    = is_array( $data ) && ! empty( $data['message'] ) ? sanitize_text_field( (string) $data['message'] ) : 'Портал CACR вернул ошибку.';
			$error_data = is_array( $data ) && isset( $data['data'] ) ? $data['data'] : array( 'http' => $code );
			return new \WP_Error( $error_code, $message, $error_data );
		}

		return isset( $data['data'] ) && is_array( $data['data'] ) ? $data['data'] : array();
	}

}
