<?php

namespace Easymailing\App\Api;



use Easymailing\App\Api\Exception\AuthenticationException;
use Easymailing\App\Api\Exception\NotFoundException;
use Easymailing\App\Api\Exception\RequestException;
use Easymailing\App\Api\Exception\RequestLimitException;
use Easymailing\App\Api\Exception\ServerException;
use Easymailing\App\Api\Exception\MethodNotAllowed;
use Easymailing\App\Api\Exception\ValidationException;
use Easymailing\App\Api\Model\Response;
use Easymailing\App\Core\Log\LogManager;
use Easymailing\App\Core\ParameterBag;
use Easymailing\App\Core\Provider\Woocommerce\Model\LogType;
use Easymailing\App\Model\Audience;
use Easymailing\App\Model\BatchOperation;
use Easymailing\App\Model\Campaign;
use Easymailing\App\Model\Customer;
use Easymailing\App\Model\Order;
use Easymailing\App\Model\Product;
use Easymailing\App\Repository\ConfigRepository;


class Client
{

	private $parameterBag;
	private $configRepository;
	private $hydrator;
	private $logManager;

	public function __construct(ParameterBag $parameterBag, ConfigRepository $configRepository, Hydrator $hydrator, LogManager $logManager)
	{
		$this->parameterBag = $parameterBag;
		$this->configRepository = $configRepository;
		$this->hydrator = $hydrator;
		$this->logManager = $logManager;
	}

	public function checkAuth($apiKey)
	{
		$wpResponse = wp_remote_get($this->parameterBag->get('api_base_uri'), [
			'headers' => [
				'X-Auth-Token' => $apiKey
			]
		]);

		return $this->buildResponse($wpResponse, 'GET', $this->parameterBag->get('api_base_uri'), $this->parameterBag->get('api_base_uri'), [], false);
	}

	public function getMySuscription()
	{
		return $this->hydrator->hydrate($this->doRequest('GET', '/my_suscription')->getBody());
	}

	public function getAudiences()
	{
		$data = $this->doRequest('GET', '/audiences')->getBody();
		return $this->hydrator->hydrate($data);
	}

	public function getAudienceById($iri): Audience
	{
		$data = $this->doRequest('GET', $iri)->getBody();
		return $this->hydrator->hydrate($data);
	}

	public function getAudienceArrayById($iri): array
	{
		return $this->doRequest('GET', $iri)->getBody();
	}

	public function getForms($audience, $query)
	{
		return $this->hydrator->hydrate($this->doRequest('GET', $audience . '/suscription_forms', $query)->getBody());
	}


	public function getFormById($iri)
	{
		return $this->hydrator->hydrate($this->doRequest('GET', $iri)->getBody());
	}

	public function createMember($audienceIri, $data, $disableDoubleOptIn = false, $disableSmsDoubleOptIn = false, $disableWelcomeEmail = false)
	{
		$query = $this->buildBooleanQuery([
			'disableDoubleOptIn' => $disableDoubleOptIn,
			'disableSmsDoubleOptIn' => $disableSmsDoubleOptIn,
			'disableWelcomeEmail' => $disableWelcomeEmail,
		]);
		$data = $this->doRequest('POST', $audienceIri . '/members', $query, $data)->getBody();
		return $this->hydrator->hydrate($data);
	}

	public function getCustomers($iri): array
	{
		 return $this->hydrator->hydrate($this->doRequest('GET', $iri)->getBody());
	}

	public function getCustomer($iri)
	{
		$data = $this->doRequest('GET', $iri)->getBody();
		return $this->hydrator->hydrate($data);
	}

	public function findCustomerByEmail(string $url, string $email): ?Customer
	{
		return $this->findFirstCustomer($url, ['email' => $email]);
	}

	public function findCustomerByPhone(string $url, string $phone): ?Customer
	{
		return $this->findFirstCustomer($url, ['phone' => $phone]);
	}

	public function findCustomerByIdentity(string $url, ?string $email = null, ?string $phone = null): ?Customer
	{
		if (!empty($email)) {
			$customer = $this->findCustomerByEmail($url, $email);
			if ($customer) {
				return $customer;
			}
		}

		if (!empty($phone)) {
			return $this->findCustomerByPhone($url, $phone);
		}

		return null;
	}

	private function findFirstCustomer(string $url, array $query): ?Customer
	{
		$data = $this->doRequest('GET', $url, $query)->getBody();
		$elements = $this->hydrator->hydrate($data);

		if(count($elements) > 0){
			return $elements[0];
		}

		return null;
	}

	public function createCustomer($customerIri, $data, array $query = [])
	{
		$data = $this->doRequest('POST', $customerIri, $this->buildQuery($query), $data)->getBody();
		return $this->hydrator->hydrate($data);
	}

	public function updateCustomer($customerIri, $data, array $query = [])
	{
		$data = $this->doRequest('PUT', $customerIri, $this->buildQuery($query), $data)->getBody();
		return $this->hydrator->hydrate($data);
	}

	public function getProducts($productsIri)
	{
		$data = $this->doRequest('GET', $productsIri)->getBody();
		return $this->hydrator->hydrate($data);
	}
	public function getProduct($productIri)
	{
		$data = $this->doRequest('GET', $productIri)->getBody();
		return $this->hydrator->hydrate($data);
	}

	public function createProduct($productIri, $data): Product
	{
		$data = $this->doRequest('POST', $productIri, null, $data)->getBody();
		return $this->hydrator->hydrate($data);
	}

	public function updateProduct($productIri, $data)
	{
		$data = $this->doRequest('PUT', $productIri, null, $data)->getBody();
		return $this->hydrator->hydrate($data);
	}

	public function deleteProduct($productIri): void
	{
		$this->doRequest('DELETE', $productIri, null)->getBody();
	}

	public function deleteUser($userIri): void
	{
		$this->doRequest('DELETE', $userIri, null)->getBody();
	}

	public function getVariations($variationsIri)
	{
		$data = $this->doRequest('GET', $variationsIri)->getBody();
		return $this->hydrator->hydrate($data);
	}

	public function createVariation($variationIri, $data)
	{
		$data = $this->doRequest('POST', $variationIri, null, $data)->getBody();
		return $this->hydrator->hydrate($data);
	}

	public function updateVariation($variationIri, $data)
	{
		$data = $this->doRequest('PUT', $variationIri, null, $data)->getBody();
		return $this->hydrator->hydrate($data);
	}

	public function getCategories($categoriesIri)
	{
		$data = $this->doRequest('GET', $categoriesIri)->getBody();
		return $this->hydrator->hydrate($data);
	}
	public function getCategory($categoryIri)
	{
		$data = $this->doRequest('GET', $categoryIri)->getBody();
		return $this->hydrator->hydrate($data);
	}

	public function createCategory($categoryIri, $data)
	{
		$data = $this->doRequest('POST', $categoryIri, null, $data)->getBody();
		return $this->hydrator->hydrate($data);
	}

	public function updateCategory($categoryIri, $data)
	{
		$data = $this->doRequest('PUT', $categoryIri, null, $data)->getBody();
		return $this->hydrator->hydrate($data);
	}


	public function getStores()
	{
		return $this->hydrator->hydrate($this->doRequest('GET', "/stores")->getBody());
	}


	public function getStore($endpoint)
	{
		$data = $this->doRequest('GET', $endpoint)->getBody();
		return $this->hydrator->hydrate($data);
	}

	public function createStore($data)
	{
		return $this->hydrator->hydrate($this->doRequest('POST', "/stores", null, $data)->getBody());
	}

	public function updateStore($iri, $data)
	{
		return $this->hydrator->hydrate($this->doRequest('PUT', $iri, null, $data)->getBody());
	}
	public function deleteStore($resourceId)
	{
		$this->doRequest('DELETE', "/stores" . "/" . $resourceId)->getBody();
	}

	public function getOrders($iri)
	{
		$data = $this->doRequest('GET', $iri)->getBody();
		return $this->hydrator->hydrate($data);
	}

	public function getOrder($iri): Order
	{
		$data = $this->doRequest('GET', $iri)->getBody();
		return $this->hydrator->hydrate($data);
	}

	public function getCampaignByHash($iri): ?Campaign
	{
		$data = $this->doRequest('GET', $iri)->getBody();
		$campaigns =  $this->hydrator->hydrate($data);
		if(count($campaigns) > 0){
			return $campaigns[0];
		}

		return null;

	}


	public function createOrder($orderUrl, $data): Order
	{
		$data = $this->doRequest('POST', $orderUrl, null, $data)->getBody();
		return $this->hydrator->hydrate($data);
	}
	public function deleteOrder($orderUrl)
	{
		$this->doRequest('DELETE', $orderUrl, null)->getBody();
	}

	public function updateOrder($url, $data): Order
	{
		$data = $this->doRequest('PUT', $url, null, $data)->getBody();
		return $this->hydrator->hydrate($data);
	}

	public function handleOrder($url, $data)
	{
		$data = $this->doRequest('PUT', $url, null, $data)->getBody();
		return $this->hydrator->hydrate($data);
	}


	public function createBatchOperation($data): BatchOperation
	{
		$data = $this->doRequest('POST', "/batch_operations", null, $data)->getBody();
		return $this->hydrator->hydrate($data);
	}

	public function getBatchOperations()
	{
		$data = $this->doRequest('GET', "/batch_operations")->getBody();
		return $this->hydrator->hydrate($data);
	}

	public function getBatchOperation($url): ?BatchOperation
	{
		$data = $this->doRequest('GET', $url)->getBody();
		return $this->hydrator->hydrate($data);
	}

	public function deleteBatchOperation($url): void
	{
		$this->doRequest('DELETE', $url)->getBody();
	}

	public function regenerateBatchOperationsResponseBodyUrl($url)
	{
		$data = $this->doRequest('PUT', $url)->getBody();
		return $this->hydrator->hydrate($data);
	}

	public function getListFields($iri)
	{
		$data = $this->doRequest('GET', $iri)->getBody();
		return $this->hydrator->hydrate($data);
	}


	/**
	 * @param $method
	 * @param $endPoint
	 * @param $query
	 * @param $body
	 *
	 * @return Response
	 * @throws \Exception
	 */
	protected function doRequest($method, $endPoint, $query = null, $body = null): Response
	{
		$url = $this->parameterBag->get('api_base_uri') . $endPoint;
		$query = $this->buildQuery($query);

		// Agregar query a la URL
		if ($query) {
			$url .= "?" . http_build_query($query);
		}

		// Convertir body en JSON, si existe
		if ($body !== null) {
			$body = json_encode($body);
		}

		// Realizar la solicitud HTTP
		$wpResponse = wp_remote_get($url, [
			'timeout' => 10,
			'method'  => $method,
			'headers' => [
				'Content-Type' => 'application/ld+json',
				'X-Auth-Token' => $this->configRepository->getConfig()->getApiKey(),
			],
			'body'    => $body,
			'query'   => $query,
		]);

		// Mover el registro de logs y manejo de errores al método centralizado
		return $this->buildResponse($wpResponse, $method, $endPoint, $url, $body);
	}

	private function buildQuery($query): array
	{
		if (!$query || !is_array($query)) {
			return [];
		}

		$query = array_filter($query, function ($value) {
			return $value !== null && $value !== '';
		});

		return array_map(function ($value) {
			if ($value === true) {
				return 'true';
			}
			if ($value === false) {
				return 'false';
			}
			return $value;
		}, $query);
	}

	private function buildBooleanQuery(array $query): array
	{
		return array_filter($query, function ($value) {
			return $value === true;
		});
	}


	/**
	 * @param $wpResponse
	 * @param $method
	 * @param $endPoint
	 * @param $url
	 * @param $body
	 * @param bool $log
	 *
	 * @return Response
	 * @throws \Exception
	 */
	private function buildResponse($wpResponse, $method, $endPoint, $url, $body = null, bool $log = true): Response
	{
		$statusCode = (int) wp_remote_retrieve_response_code($wpResponse); // Código HTTP
		$responseBody = wp_remote_retrieve_body($wpResponse); // Cuerpo de la respuesta

		$decodedBody = $responseBody ? json_decode($responseBody, true) : null;
		$status = $statusCode;

		if ($statusCode >= 200 && $statusCode < 300) {
			if($log){
				$this->logManager->createRequestLog(
					LogType::REQUEST,
					sprintf('Successful %s request to %s', $method, $endPoint),
					$url,
					(string) $status,
					$body,
					$responseBody
				);
			}


			return Response::init($statusCode, $decodedBody);
		}

		// Manejo de errores para respuestas con error
		$errorMessage = $decodedBody['hydra:title'] ?? $decodedBody['title'] ?? 'Unexpected error occurred.';
		$errorDetail = $decodedBody['hydra:description'] ?? $decodedBody['detail'] ?? 'No additional details provided.';
		$errorType = $decodedBody['type'] ?? 'unknown_error';

		if($log){
			$this->logManager->createRequestLog(
				LogType::REQUEST,
				sprintf(
					'Error in %s request to %s: [%s] %s - %s',
					$method,
					$endPoint,
					$errorType,
					$errorMessage,
					$errorDetail
				),
				$url,
				(string) $status,
				$body,
				$responseBody
			);
		}

		throw $this->getException($decodedBody, $statusCode);
	}

	private function getException($body, $status): \Exception
	{
		$errorTitle = null;

		if (is_array($body)) {
			if (array_key_exists('title', $body)) {
				$errorTitle = $body['title'];
				if (array_key_exists('detail', $body)) {
					$errorTitle .=  ": " . $body['detail'];
				}
			}

			if (array_key_exists('hydra:title', $body)) {
				$errorTitle = $body['hydra:title'];
				if (array_key_exists('hydra:description', $body)) {
					$errorTitle .=  ": " . $body['hydra:description'];
				}
			}

			if (array_key_exists('message', $body)) {
				$errorTitle = $body['message'];
			}
		}

		if (!$errorTitle) {
			$errorTitle = 'Unexpected error';
		}

		if ($status === 401) {
			$e = new AuthenticationException($errorTitle ?? 'Authentication exception', $status);
		} elseif ($status === 404) {
			$e = new NotFoundException($errorTitle ?? 'Not found', $status);
		} elseif ($status === 405) {
			$e = new MethodNotAllowed($errorTitle ?? 'Method not allowed', $status);
		} elseif ($status === 429) {
			$e = new RequestLimitException($errorTitle ?? 'Request limit exceeded', $status);
		} elseif ($status === 422) {
			$e = new ValidationException($errorTitle ?? 'Validation errors', $status, null, $body['violations']);
		} elseif ($status >= 400 && $status < 500) {
			$e = new RequestException($errorTitle, $status);
		} elseif ($status >= 500 && $status < 600) {
			$e = new ServerException($errorTitle, $status);
		} else {
			$e = new ServerException('Unexpected HTTP Status Code: ' . $status, $status);
		}
		return $e;
	}
}
