<?php

declare( strict_types=1 );

namespace Webequipe\SpinWinWheel;

// Exit if accessed directly.
if ( ! defined( 'ABSPATH' ) ) {
	exit;
}

/**
 * Mailchimp integration helper.
 *
 * Handles:
 * - Storing/retrieving Mailchimp integration settings.
 * - Testing API connectivity.
 * - Fetching audiences (lists).
 * - Subscribing/upserting contacts from spin submissions.
 */
class Mailchimp {

	/**
	 * Option name used to persist Mailchimp settings.
	 */
	const OPTION_KEY = 'wsww_mailchimp_settings';

	/**
	 * Prefix used to mark encrypted API keys in storage.
	 */
	const ENCRYPTED_PREFIX = 'wsww_enc:';

	/**
	 * Initialize hooks.
	 */
	public static function init(): void {
		// Admin‑only AJAX endpoints.
		if ( is_admin() ) {
			add_action( 'wp_ajax_wsww_mailchimp_connect', [ __CLASS__, 'ajax_connect' ] );
			add_action( 'wp_ajax_wsww_mailchimp_disconnect', [ __CLASS__, 'ajax_disconnect' ] );
		}
	}

	/**
	 * Get Mailchimp settings with sane defaults.
	 *
	 * @return array
	 */
	public static function get_settings(): array {
		$defaults = [
			'enabled'          => 0,
			'api_key'          => '',
			'list_id'          => '',
			'tag'              => 'SpinWheel',
			'consent_enabled'  => 0,
			'consent_text'     => __( 'I agree to receive promotional emails.', 'webequipe-spin-win-wheel' ),
			'last_status'      => '',
		];

		$stored = get_option( self::OPTION_KEY, [] );

		if ( ! is_array( $stored ) ) {
			$stored = [];
		}

		if ( isset( $stored['api_key'] ) && is_string( $stored['api_key'] ) ) {
			$stored['api_key'] = self::decrypt_api_key_from_storage( $stored['api_key'] );
		}

		return array_merge( $defaults, $stored );
	}

	/**
	 * Check if an API key value is already encrypted.
	 *
	 * @param string $value Raw stored value.
	 * @return bool
	 */
	public static function is_api_key_encrypted( string $value ): bool {
		return 0 === strpos( $value, self::ENCRYPTED_PREFIX );
	}

	/**
	 * Encrypt an API key before persisting it in options.
	 *
	 * @param string $api_key Plain API key.
	 * @return string Encrypted payload (or plain key if encryption unavailable).
	 */
	public static function encrypt_api_key_for_storage( string $api_key ): string {
		$api_key = trim( $api_key );
		if ( '' === $api_key ) {
			return '';
		}

		if ( self::is_api_key_encrypted( $api_key ) ) {
			return $api_key;
		}

		if ( ! function_exists( 'openssl_encrypt' ) || ! function_exists( 'openssl_random_pseudo_bytes' ) ) {
			return $api_key;
		}

		$key = self::get_encryption_key();
		if ( '' === $key ) {
			return $api_key;
		}

		$iv = openssl_random_pseudo_bytes( 16 );
		if ( false === $iv || 16 !== strlen( $iv ) ) {
			return $api_key;
		}

		$ciphertext = openssl_encrypt( $api_key, 'aes-256-cbc', $key, OPENSSL_RAW_DATA, $iv );
		if ( false === $ciphertext ) {
			return $api_key;
		}

		return self::ENCRYPTED_PREFIX . base64_encode( $iv . $ciphertext );
	}

	/**
	 * Decrypt an API key from storage for runtime usage.
	 *
	 * @param string $stored_value Stored API key value.
	 * @return string Plain API key.
	 */
	public static function decrypt_api_key_from_storage( string $stored_value ): string {
		$stored_value = trim( $stored_value );
		if ( '' === $stored_value ) {
			return '';
		}

		if ( ! self::is_api_key_encrypted( $stored_value ) ) {
			return $stored_value;
		}

		$encoded = substr( $stored_value, strlen( self::ENCRYPTED_PREFIX ) );
		if ( '' === $encoded ) {
			return '';
		}

		$payload = base64_decode( $encoded, true );
		if ( false === $payload || strlen( $payload ) <= 16 ) {
			return '';
		}

		if ( ! function_exists( 'openssl_decrypt' ) ) {
			return '';
		}

		$key = self::get_encryption_key();
		if ( '' === $key ) {
			return '';
		}

		$iv         = substr( $payload, 0, 16 );
		$ciphertext = substr( $payload, 16 );
		$plain      = openssl_decrypt( $ciphertext, 'aes-256-cbc', $key, OPENSSL_RAW_DATA, $iv );

		return false === $plain ? '' : $plain;
	}

	/**
	 * Build a site-specific encryption key.
	 *
	 * @return string
	 */
	protected static function get_encryption_key(): string {
		$source = wp_salt( 'auth' );
		if ( ! is_string( $source ) || '' === $source ) {
			return '';
		}

		return hash( 'sha256', $source, true );
	}

	/**
	 * Persist Mailchimp settings.
	 *
	 * @param array $settings Settings to store.
	 * @return void
	 */
	public static function update_settings( array $settings ): void {
		update_option( self::OPTION_KEY, $settings );
	}

	/**
	 * Derive Mailchimp API base URL from API key.
	 *
	 * @param string $api_key API key.
	 * @return string|\WP_Error
	 */
	public static function get_api_base_url( string $api_key ) {
		$api_key = trim( $api_key );
		if ( '' === $api_key || false === strpos( $api_key, '-' ) ) {
			return new \WP_Error(
				'wsww_mailchimp_invalid_key',
				__( 'Invalid Mailchimp API key format.', 'webequipe-spin-win-wheel' )
			);
		}

		list( , $dc ) = explode( '-', $api_key, 2 );
		$dc = trim( $dc );

		if ( '' === $dc ) {
			return new \WP_Error(
				'wsww_mailchimp_invalid_dc',
				__( 'Could not detect Mailchimp datacenter from API key.', 'webequipe-spin-win-wheel' )
			);
		}

		return sprintf( 'https://%s.api.mailchimp.com/3.0/', $dc );
	}

	/**
	 * Build HTTP request args for Mailchimp API.
	 *
	 * @param string $api_key API key.
	 * @param array  $extra   Extra args to merge.
	 * @return array
	 */
	protected static function build_request_args( string $api_key, array $extra = [] ): array {
		$base = [
			'timeout' => 10,
			'headers' => [
				'Authorization' => 'apikey ' . $api_key,
				'Content-Type'  => 'application/json',
			],
		];

		return array_replace_recursive( $base, $extra );
	}

	/**
	 * Test API key connectivity by hitting the /lists endpoint.
	 *
	 * @param string $api_key API key.
	 * @return array{
	 *   success:bool,
	 *   message:string,
	 *   lists:array<string, string>
	 * }
	 */
	public static function test_connection( string $api_key ): array {
		$result = [
			'success' => false,
			'message' => '',
			'lists'   => [],
		];

		$base = self::get_api_base_url( $api_key );
		if ( is_wp_error( $base ) ) {
			$result['message'] = $base->get_error_message();
			return $result;
		}

		$response = wp_remote_get(
			$base . 'lists',
			self::build_request_args( $api_key )
		);

		if ( is_wp_error( $response ) ) {
			$result['message'] = $response->get_error_message();
			return $result;
		}

		$code = wp_remote_retrieve_response_code( $response );
		$body = wp_remote_retrieve_body( $response );
		$data = json_decode( $body, true );

		if ( 200 !== $code || ! is_array( $data ) ) {
			$result['message'] = __( 'Mailchimp API request failed.', 'webequipe-spin-win-wheel' );
			return $result;
		}

		$lists = [];
		if ( isset( $data['lists'] ) && is_array( $data['lists'] ) ) {
			foreach ( $data['lists'] as $list ) {
				if ( empty( $list['id'] ) || empty( $list['name'] ) ) {
					continue;
				}
				$lists[ sanitize_text_field( (string) $list['id'] ) ] = sanitize_text_field( (string) $list['name'] );
			}
		}

		$result['success'] = true;
		$result['message'] = __( 'Mailchimp connected successfully.', 'webequipe-spin-win-wheel' );
		$result['lists']   = $lists;

		return $result;
	}

	/**
	 * AJAX handler for "Connect" button in admin.
	 *
	 * Verifies API key and returns available audiences.
	 *
	 * @return void
	 */
	public static function ajax_connect(): void {
		check_ajax_referer( 'wsww_mailchimp_settings', 'nonce' );

		if ( ! current_user_can( 'manage_options' ) ) {
			wp_send_json_error(
				[
					'message' => esc_html__( 'You do not have permission to perform this action.', 'webequipe-spin-win-wheel' ),
				]
			);
		}

		$api_key = isset( $_POST['api_key'] )
			? sanitize_text_field( wp_unslash( $_POST['api_key'] ) )
			: '';

		if ( '' === $api_key ) {
			wp_send_json_error(
				[
					'message' => esc_html__( 'Please enter your Mailchimp API key.', 'webequipe-spin-win-wheel' ),
				]
			);
		}

		$result = self::test_connection( $api_key );

		if ( ! $result['success'] ) {
			wp_send_json_error(
				[
					'message' => esc_html( $result['message'] ),
					'lists'   => $result['lists'],
				]
			);
		}

		wp_send_json_success(
			[
				'message' => esc_html( $result['message'] ),
				'lists'   => $result['lists'],
			]
		);
	}

	/**
	 * AJAX handler for "Disconnect" button in admin.
	 *
	 * Clears saved API key and selected audience immediately.
	 *
	 * @return void
	 */
	public static function ajax_disconnect(): void {
		check_ajax_referer( 'wsww_mailchimp_settings', 'nonce' );

		if ( ! current_user_can( 'manage_options' ) ) {
			wp_send_json_error(
				[
					'message' => esc_html__( 'You do not have permission to perform this action.', 'webequipe-spin-win-wheel' ),
				]
			);
		}

		$settings = self::get_settings();
		$settings['api_key']     = '';
		$settings['list_id']     = '';
		$settings['last_status'] = '';

		self::update_settings( $settings );

		wp_send_json_success(
			[
				'message' => esc_html__( 'Disconnected from Mailchimp.', 'webequipe-spin-win-wheel' ),
			]
		);
	}

	/**
	 * Subscribe (or update) a contact in Mailchimp.
	 *
	 * Uses PUT /lists/{list_id}/members/{subscriber_hash} for upsert behaviour.
	 *
	 * @param string $email  Email address.
	 * @param array  $args   {
	 *   @type string $name  Optional full name.
	 *   @type string $phone Optional phone.
	 * }
	 * @return void
	 */
	public static function subscribe( string $email, array $args = [] ): void {
		$email = sanitize_email( $email );
		if ( '' === $email || ! is_email( $email ) ) {
			return;
		}

		$settings = self::get_settings();

		if ( empty( $settings['enabled'] ) || empty( $settings['api_key'] ) || empty( $settings['list_id'] ) ) {
			return;
		}

		$base = self::get_api_base_url( $settings['api_key'] );
		if ( is_wp_error( $base ) ) {
			return;
		}

		$list_id = sanitize_text_field( (string) $settings['list_id'] );

		$subscriber_hash = md5( strtolower( $email ) );

		$merge_fields = [];

		if ( ! empty( $args['name'] ) ) {
			$merge_fields['FNAME'] = sanitize_text_field( (string) $args['name'] );
		}

		if ( ! empty( $args['phone'] ) ) {
			$merge_fields['PHONE'] = sanitize_text_field( (string) $args['phone'] );
		}

		$payload = [
			'email_address' => $email,
			'status_if_new' => 'subscribed',
			'status'        => 'subscribed',
		];

		if ( ! empty( $merge_fields ) ) {
			$payload['merge_fields'] = $merge_fields;
		}

		if ( ! empty( $settings['tag'] ) ) {
			$payload['tags'] = [ sanitize_text_field( (string) $settings['tag'] ) ];
		}

		$response = wp_remote_request(
			$base . 'lists/' . rawurlencode( $list_id ) . '/members/' . $subscriber_hash,
			self::build_request_args(
				$settings['api_key'],
				[
					'method' => 'PUT',
					'body'   => wp_json_encode( $payload ),
				]
			)
		);

		if ( is_wp_error( $response ) ) {
			return;
		}

		$code = wp_remote_retrieve_response_code( $response );

		if ( $code < 200 || $code > 299 ) {
			// Never break the main spin flow for marketing sync failures.
			return;
		}
	}

	/**
	 * Conditionally subscribe a contact for a spin submission.
	 *
	 * Reads current settings and honours consent configuration.
	 *
	 * @param array $data {
	 *   @type string $email      Email.
	 *   @type string $name       Name.
	 *   @type string $phone      Phone.
	 *   @type string $consent    Raw consent flag from form (e.g. "1").
	 * }
	 * @return void
	 */
	public static function maybe_subscribe_from_spin( array $data ): void {
		$settings = self::get_settings();

		if ( empty( $settings['enabled'] ) ) {
			return;
		}

		// If consent is required but not present, abort silently.
		if ( ! empty( $settings['consent_enabled'] ) ) {
			if ( empty( $data['consent'] ) ) {
				return;
			}
		}

		$email = isset( $data['email'] ) ? (string) $data['email'] : '';
		$name  = isset( $data['name'] ) ? (string) $data['name'] : '';
		$phone = isset( $data['phone'] ) ? (string) $data['phone'] : '';

		self::subscribe(
			$email,
			[
				'name'  => $name,
				'phone' => $phone,
			]
		);
	}
}

// Boot Mailchimp integration.
Mailchimp::init();

