<?php
/**
 * Custom Authentication Strategy
 *
 * @category   WordPress_Plugin
 * @package    Common
 * @subpackage Strategies\Authentication
 * @author     miniOrange <info@miniorange.com>
 * @license    MIT/Expat
 * @link       https://plugins.miniorange.com/woocommerce-api-product-sync-with-woocommerce-rest-apis
 */

namespace MOWCPS\Common\Strategies\Authentication;

use WP_Error;
use MOWCPS\Common\Abstracts\Auth_Base_Strategy;
use MOWCPS\Common\Constants\Constants;
use MOWCPS\Common\Services\API\API_Service;
use MOWCPS\Common\Services\Logging\Supplier_Logger;

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

/**
 * Class CustomAuth_Strategy
 *
 * Handles custom authentication with flexible request/response configurations
 */
class Custom_Auth_Strategy extends Auth_Base_Strategy {
	/**
	 * Generate authorization headers
	 *
	 * @param int   $supplier_id Supplier ID.
	 * @param array $settings Auth settings.
	 * @return array|WP_Error Headers array or error
	 */
	public function generate_headers( $settings, $supplier_id ) {
		// Validate settings first.
		$validation = $this->validate_settings( $settings );
		if ( is_wp_error( $validation ) ) {
			return $validation;
		}

		$response_settings = $settings['response_settings'];

		$token = $this->get_custom_auth_token( $supplier_id, $settings );

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

		// For cookie-based auth, return cookies.
		if ( is_array( $token ) ) {
			$cookie_name = $response_settings['cookie_name'] ?? 'set-cookie';
			return array(
				$cookie_name => $token,
			);
		}

		// For token-based auth, add authorization header.
		$token_type  = $response_settings['token_type'] ?? Constants::DEFAULT_TOKEN_TYPE;
		$header_name = $response_settings['authorization_header_name'] ?? Constants::DEFAULT_AUTH_HEADER_NAME;

		return array(
			$header_name => $token_type . ' ' . $token,
		);
	}

	/**
	 * Validate auth settings
	 *
	 * @param array $settings Auth settings.
	 * @return bool|WP_Error True if valid, WP_Error if invalid
	 */
	public function validate_settings( $settings ) {
		$request_settings  = $settings['request_settings'] ?? array();
		$response_settings = $settings['response_settings'] ?? array();

		// Check required request settings.
		$url    = $request_settings['url'] ?? '';
		$method = $request_settings['method'] ?? '';

		if ( empty( $url ) ) {
			return new WP_Error(
				'invalid_custom_auth_config',
				'Invalid custom auth configuration: missing URL',
				array( 'status' => 400 )
			);
		}

		if ( empty( $method ) ) {
			return new WP_Error(
				'invalid_custom_auth_config',
				'Invalid custom auth configuration: missing method',
				array( 'status' => 400 )
			);
		}

		// Validate HTTP method.
		$valid_methods = array( Constants::HTTP_REQUEST_METHOD_GET, Constants::HTTP_REQUEST_METHOD_POST, Constants::HTTP_REQUEST_METHOD_PUT, Constants::HTTP_REQUEST_METHOD_DELETE );
		if ( ! in_array( strtoupper( $method ), $valid_methods, true ) ) {
			return new WP_Error(
				'invalid_custom_auth_method',
				'Invalid HTTP method: ' . $method,
				array( 'status' => 400 )
			);
		}

		// Check required response settings.
		$token_key   = $response_settings['token_key'] ?? Constants::DEFAULT_TOKEN_KEY;
		$cookie_name = $response_settings['cookie_name'] ?? 'set-cookie';
		if ( empty( $token_key ) && empty( $cookie_name ) ) {
			return new WP_Error(
				'invalid_custom_auth_response_config',
				'Invalid custom auth response configuration: missing token_key or cookie_name',
				array( 'status' => 400 )
			);
		}

		return true;
	}

	/**
	 * Get custom authentication token with caching
	 *
	 * @param int   $supplier_id Supplier ID.
	 * @param array $settings Custom auth settings.
	 * @return string|array|WP_Error Token or error
	 */
	private function get_custom_auth_token( $supplier_id, $settings ) {
		// Check for cached token first.
		$cached_token = $this->get_cached_token( $supplier_id );

		if ( $cached_token && empty( $settings['response_settings']['cookie_name'] ) ) {
			return $cached_token;
		}

		// No cached token, make a fresh request.
		return $this->refresh_custom_token( $supplier_id, $settings );
	}

	/**
	 * Refresh custom token
	 *
	 * @param int   $supplier_id Supplier ID.
	 * @param array $settings Custom auth settings.
	 * @return string|array|WP_Error Token or error
	 */
	private function refresh_custom_token( $supplier_id, $settings ) {
		$request_settings  = $settings['request_settings'] ?? array();
		$response_settings = $settings['response_settings'] ?? array();

		// Map UI field names to expected field names.
		$url     = $request_settings['url'];
		$method  = strtoupper( $request_settings['method'] );
		$headers = $request_settings['headers'] ?? array();
		$body    = null;

		// Add body if present.
		if ( ! empty( $request_settings['body'] ) && ! empty( $request_settings['body']['type'] ) && ! empty( $request_settings['body']['data'] ) ) {
			$body_config = $request_settings['body'];
			if ( 'json' === $body_config['type'] ) {
				$body                    = wp_json_encode( $body_config['data'], JSON_UNESCAPED_SLASHES );
				$headers['Content-Type'] = 'application/json';
			} elseif ( 'x-www-form-urlencoded' === $body_config['type'] ) {
				$body                    = http_build_query( $body_config['data'] );
				$headers['Content-Type'] = 'application/x-www-form-urlencoded';
			}
		}

		$api_request = array(
			'method'  => $method,
			'headers' => $headers,
			'body'    => $body,
		);

		// Make request using API service.
		$response = API_Service::make_api_call( $url, $api_request );

		if ( is_wp_error( $response ) ) {
			if ( 0 !== $supplier_id ) {
				Supplier_Logger::debug( $supplier_id, 'Custom authentication failed' );
				Supplier_Logger::debug( $supplier_id, 'Error: ' . $response->get_error_message() );
				Supplier_Logger::debug( $supplier_id, 'Error code: ' . $response->get_error_code() );
				Supplier_Logger::debug( $supplier_id, 'Check your custom authentication credentials and endpoint configuration' );
			}
			return $response;
		}

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

		// For cookie-based auth, return cookies.
		$cookie_name = $response_settings['cookie_name'] ?? '';

		if ( ! empty( $cookie_name ) ) {
			return $response['headers'][ $cookie_name ];
		}

		// Check for token-based auth.
		$token_key = $response_settings['token_key'] ?? '';
		if ( empty( $token_key ) || empty( $data[ $token_key ] ) ) {
			$error = new WP_Error(
				'custom_auth_token_error',
				'Failed to get custom auth token',
				array( 'status' => 500 )
			);
			if ( 0 !== $supplier_id ) {
				Supplier_Logger::debug( $supplier_id, 'Custom authentication response invalid or missing required token' );
				Supplier_Logger::debug( $supplier_id, 'Response body: ' . $body );
				Supplier_Logger::debug( $supplier_id, 'Expected token key: ' . $response_settings['token_key'] );
				Supplier_Logger::debug( $supplier_id, 'Verify your custom authentication response configuration and token key mapping' );
			}
			return $error;
		}

		$token      = $data[ $token_key ];
		$expires_in = $data[ $response_settings['expires_in_key'] ] ?? $response_settings['expires_in'] ?? Constants::DEFAULT_EXPIRES_IN;

		// Cache the token.
		if ( 0 !== $supplier_id ) {
			$this->set_cached_token( $supplier_id, $token, $expires_in );
		}

		return $token;
	}
}
