<?php
/**
 * Auth Handler – centralises storage & retrieval of marketplace OAuth tokens / credentials.
 *
 * @package    MOMCS_Sync
 * @subpackage MOMCS_Sync\includes\common\handlers
 */

declare(strict_types=1);

namespace MOMCS_Sync\Includes\Common\Handlers;

use MOMCS_Sync\Includes\Common\Models\Multichannels_Model;
use MOMCS_Sync\Includes\Common\Models\Authentication_Details_Model;
use MOMCS_Sync\Includes\Common\Handlers\Crypto_Handler;
use MOMCS_Sync\Includes\Common\Constants\Plugin_Constants;

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

/**
 * Class Amazon_Auth_Handler
 *
 * Responsibilities:
 * 1. Persist refresh / access tokens for any marketplace inside the multichannels table.
 * 2. Provide read helpers so services can fetch stored creds without duplicating SQL.
 * 3. Maintain backward-compatibility by also mirroring the token to legacy `momcs_<marketplace>_sync_refresh_token` option.
 */
class Amazon_Auth_Handler {

	/**
	 * Multichannels model instance.
	 *
	 * @var Multichannels_Model
	 */
	private Multichannels_Model $multichannels_model;

	/**
	 * Authentication details model instance.
	 *
	 * @var Authentication_Details_Model
	 */
	private Authentication_Details_Model $auth_details_model;

	/**
	 * Constructor.
	 */
	public function __construct() {
		$this->multichannels_model = new Multichannels_Model();
		$this->auth_details_model  = new Authentication_Details_Model();
	}

	/**
	 * Persist a new marketplace authorisation payload.
	 *
	 * @param string $marketplace      Marketplace slug – e.g. 'amazon', 'ebay', 'walmart'.
	 * @param string $connection_name  Friendly name provided by user.
	 * @param string $region           Region / marketplace ID specified by the user.
	 * @param string $refresh_token    OAuth refresh token (or equivalent credential).
	 * @param string $seller_id        Seller ID.
	 *
	 * @return string The UUID of the newly created channel.
	 * @throws \RuntimeException On DB failure or connection limit exceeded.
	 */
	public function save_authorization( string $marketplace, string $connection_name, string $region, string $refresh_token, string $seller_id ): string {
		// Validate connection limits for Amazon before saving.
		if ( Plugin_Constants::AMAZON === strtolower( $marketplace ) ) {
			$limit_check = \MOMCS_Sync\Includes\Common\Utils\Connection_Limits::can_add_amazon_connection();

			if ( ! $limit_check['can_add'] ) {
				throw new \RuntimeException(
					esc_html( $limit_check['message'] )
				);
			}
		}

		$uuid = wp_generate_uuid4();

		// Save channel data (without authentication).
		$channel_data = array(
			'uuid'            => $uuid,
			'connection_name' => $connection_name,
			'marketplace'     => strtolower( $marketplace ),
			'region'          => $region,
			'seller_id'       => $seller_id,
			'status'          => 'active',
			'current_step'    => 2, // Post-auth step.
			'created_at'      => current_time( 'mysql' ),
			'updated_at'      => current_time( 'mysql' ),
		);

		$result = $this->multichannels_model->add_channel( $channel_data );
		if ( false === $result ) {
			throw new \RuntimeException( esc_html( Plugin_Constants::MESSAGE_FAILED_TO_PERSIST_CHANNEL_DATA ) );
		}

		// Save authentication data separately (encrypt at rest).
		$auth_data = array(
			'uuid'           => $uuid,
			'authentication' => Crypto_Handler::encrypt( $refresh_token ),
			'created_at'     => current_time( 'mysql' ),
			'updated_at'     => current_time( 'mysql' ),
		);

		$auth_result = $this->auth_details_model->add_auth_details( $auth_data );
		if ( false === $auth_result ) {
			// Cleanup channel data if auth save fails.
			$this->multichannels_model->delete_channel( $this->multichannels_model->get_channel_by_uuid( $uuid )['id'] );
			throw new \RuntimeException( esc_html( Plugin_Constants::MESSAGE_FAILED_TO_PERSIST_AUTHENTICATION_DATA ) );
		}

		// Fire action hook for Amazon channel authorization.
		if ( Plugin_Constants::AMAZON === strtolower( $marketplace ) ) {
			do_action(
				'momcs_channel_authorized',
				$uuid,
				array(
					'marketplace_id'  => $region,
					'connection_name' => $connection_name,
					'refresh_token'   => $refresh_token,
				)
			);
		}

		return $uuid;
	}

	/**
	 * Handle Amazon OAuth callback workflow: exchange auth code for tokens,
	 * persist channel/auth data, update seller id, and cache access token.
	 *
	 * @param string $connection_name Connection name captured before redirect.
	 * @param string $marketplace_id  Amazon marketplace/region identifier.
	 * @param string $seller_id       Amazon Selling Partner ID from callback.
	 * @param string $authorization_code Authorization code from callback.
	 *
	 * @throws \RuntimeException On any failure during processing.
	 * @return string The UUID of the newly created channel.
	 */
	public function process_amazon_oauth_callback( string $connection_name, string $marketplace_id, string $seller_id, string $authorization_code ): string {
		// Exchange authorization code for tokens via broker.
		$token_endpoint = rtrim( Plugin_Constants::BROKER_BASE_URL, '/' ) . Plugin_Constants::BROKER_TOKEN_PATH;
		$response       = wp_remote_post(
			$token_endpoint,
			array(
				'timeout' => 30,
				'headers' => array( 'Content-Type' => 'application/x-www-form-urlencoded' ),
				'body'    => array(
					'grant_type' => 'authorization_code',
					'code'       => $authorization_code,
				),
			)
		);

		if ( is_wp_error( $response ) ) {
			throw new \RuntimeException( esc_html( Plugin_Constants::MESSAGE_TOKEN_EXCHANGE_FAILED . $response->get_error_message() ) );
		}

		$code_status = wp_remote_retrieve_response_code( $response );
		$body_raw    = wp_remote_retrieve_body( $response );
		$payload     = json_decode( $body_raw, true );
		if ( $code_status < 200 || $code_status >= 300 || ! is_array( $payload ) ) {
			throw new \RuntimeException( esc_html( Plugin_Constants::MESSAGE_TOKEN_EXCHANGE_ERROR ) );
		}

		$access_token  = $payload['access_token'] ?? '';
		$refresh_token = $payload['refresh_token'] ?? '';
		$expires_in    = isset( $payload['expires_in'] ) ? (int) $payload['expires_in'] : 3600;
		if ( empty( $refresh_token ) ) {
			throw new \RuntimeException( esc_html( Plugin_Constants::MESSAGE_MISSING_REFRESH_TOKEN ) );
		}

		// Persist channel and auth details - returns the UUID of the newly created channel.
		$uuid = $this->save_authorization( Plugin_Constants::AMAZON, $connection_name, $marketplace_id, $refresh_token, $seller_id );

		// Cache access token encrypted for that channel UUID.
		if ( $uuid && $access_token ) {
			$enc = Crypto_Handler::encrypt( $access_token );
			set_transient( Plugin_Constants::TRANSIENT_ACCESS_TOKEN . $uuid, $enc, 3540 );
		}

		return $uuid;
	}

	/**
	 * Fetch authentication payload for a given channel UUID.
	 *
	 * @param string $uuid Channel UUID.
	 * @return array<string,mixed>|null Null if not found.
	 */
	public function get_authorization_by_uuid( string $uuid ): ?array {
		$channel = $this->multichannels_model->get_channel_by_uuid( $uuid );
		if ( ! $channel ) {
			return null;
		}

		// Get authentication details separately and merge.
		$auth_details = $this->auth_details_model->get_auth_by_uuid( $uuid );
		if ( $auth_details ) {
			$channel['authentication'] = $auth_details['authentication'];
		}

		return $channel;
	}
}
