<?php

namespace Easymailing\App\Core\Provider\Woocommerce\Batch;

use Easymailing\App\Api\Client;
use Easymailing\App\Core\Log\LogManager;
use Easymailing\App\Core\Provider\Woocommerce\Model\BatchOperationIdentifier;
use Easymailing\App\Core\Provider\Woocommerce\Model\LogType;
use Easymailing\App\Core\Provider\Woocommerce\Model\SkipReason;
use Easymailing\App\Core\Provider\Woocommerce\Model\SynchronizationStatus;
use Easymailing\App\Core\Provider\Woocommerce\Service\MetadataHelper;
use Easymailing\App\Core\Provider\Woocommerce\Service\PhoneCountryResolver;
use Easymailing\App\Repository\ConfigRepository;
use Easymailing\App\Utils\Api;
use Easymailing\App\Utils\CustomerPayload;

class CustomerBatchOperations
{
	private $configRepository;
	private $logManager;

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

    public function getCustomerBatchOperations(): array
    {
	    $batchOperations = [];

        try {
			$this->logManager->createLog(LogType::BATCH, "Start CustomerBatchOperations");
	        $config = $this->configRepository->getSynchronizationConfig();
            $shopId = $config->getShopId();
            $onlyCustomers = $config->getOnlyCustomers();

            if ($onlyCustomers == 1) {
                $args = array(
                    'role'    => 'customer',
                    'number'  => -1
                );
            } else {
                $args = array(
                    'number'  => -1

                );
            }

            $wpUsers = get_users($args);

            if (count($wpUsers) == 0) {
	            $this->logManager->createLog(LogType::BATCH, "End CustomerBatchOperations");
                return $batchOperations;
            }

            $resolvedEmails = $this->resolveEffectiveEmails($wpUsers);
            $retryOnly = (bool) get_option(MetadataHelper::getRetryOnlyMode());

            foreach ($wpUsers as $user) {
                $resolution = $resolvedEmails[$user->ID] ?? ['email' => '', 'reason' => SkipReason::EMPTY_EMAIL];

                if ($resolution['reason'] !== null) {
                    $this->markAsSkipped($user, $resolution['reason']);
                    continue;
                }

                // En modo retry_only solo reencolamos los que estén en error. La
                // resolución de emails se ejecuta sobre todos los users para mantener
                // las reservas de emails hechas por users ya sincronizados.
                if ($retryOnly) {
                    $status = get_user_meta($user->ID, MetadataHelper::getSynchronizationStatus(), true);
                    if ($status !== SynchronizationStatus::ERROR) {
                        continue;
                    }
                }

                $emCustomerId = get_user_meta($user->ID, MetadataHelper::getResourceId(), true);
                if ($emCustomerId) {
                    $line = $this->updateCustomerLine($user, $shopId, $resolution['email']);
                } else {
                    $line = $this->createCustomerLine($user, $shopId, $resolution['email']);
                }
	            $batchOperations[] = $line;
            }
	        $this->logManager->createLog(LogType::BATCH, "End CustomerBatchOperations");
            return $batchOperations;
        } catch (\Exception $e) {
			$this->logManager->createLog(LogType::BATCH, sprintf("Error in CustomerBatchOperations: %s", $e->getMessage()));
        }

		$this->logManager->createLog(LogType::BATCH, "End CustomerBatchOperations");
	    return $batchOperations;
    }

    private function createCustomerLine(\WP_User $user, string $shopId, string $email): array
    {
        $emCustomerId = wp_generate_uuid4();
        update_user_meta($user->ID, MetadataHelper::getResourceId(), $emCustomerId);
        $url = "/stores" . "/" . $shopId . "/customers?disableDoubleOptIn=true&disableWelcomeEmail=true&disableSmsDoubleOptIn=true";

	    $body = $this->getUserInfo($user, $email);
	    $body['resource_id'] = $emCustomerId;

	    return [
			"method" => "POST",
			"path" => $url,
			"body" => json_encode($body),
			"external_identifier" => BatchOperationIdentifier::buildIdentifier(BatchOperationIdentifier::CUSTOMER, $user->ID)
        ];
    }

    private function updateCustomerLine(\WP_User $user, string $shopId, string $email): array
    {
        $emCustomerId = get_user_meta($user->ID, MetadataHelper::getResourceId(), true);
        $url = "/stores" . "/" . $shopId . "/customers" . "/" . $emCustomerId . '?disableDoubleOptIn=true&disableWelcomeEmail=true&disableSmsDoubleOptIn=true';
        $body = $this->getUserInfo($user, $email);

        return [
			"method" => "PUT",
			"path" => $url,
			"body" => json_encode($body),
			"external_identifier" => BatchOperationIdentifier::buildIdentifier(BatchOperationIdentifier::CUSTOMER, $user->ID)
        ];
    }

	private function getUserInfo(\WP_User $user, string $email): array
	{
		$country = PhoneCountryResolver::fromUser($user->ID);

		return CustomerPayload::body([
			'email'     => $email,
			'firstname' => !empty($billingFirstName = get_user_meta($user->ID, 'billing_first_name', true)) ? $billingFirstName : ($user->first_name ?: ''),
			'lastname'  => !empty($billingLastName = get_user_meta($user->ID, 'billing_last_name', true)) ? $billingLastName : ($user->last_name ?: ''),
			'company'   => get_user_meta($user->ID, 'billing_company', true) ?: '',
			'phone'     => get_user_meta($user->ID, 'billing_phone', true),
			'country'   => $country,
		], null, $country);
	}

	/**
	 * Decide el email a enviar para cada user garantizando unicidad.
	 *
	 * Cascada: primary (según isUseAccountEmail) → fallback → skip.
	 * Cuando dos users competirían por el mismo email (billing compartido, o user_email
	 * de uno que coincide con billing_email de otro, etc.), el segundo cae a su fallback.
	 * Si ni primary ni fallback son únicos, se marca EMAIL_COLLISION.
	 *
	 * @param array<\WP_User> $users
	 * @return array<int,array{email:string,reason:?string}>
	 */
	private function resolveEffectiveEmails(array $users): array
	{
		$useAccountEmail = $this->configRepository->getSynchronizationConfig()->isUseAccountEmail();

		$candidates = [];
		foreach ($users as $user) {
			$billingEmail = strtolower(trim((string) get_user_meta($user->ID, 'billing_email', true)));
			$userEmail = strtolower(trim((string) ($user->user_email ?: '')));

			if ($useAccountEmail) {
				$primary  = $userEmail ?: $billingEmail;
				$fallback = $billingEmail;
			} else {
				$primary  = $billingEmail ?: $userEmail;
				$fallback = $userEmail;
			}

			$candidates[$user->ID] = [
				'user_email' => $userEmail,
				'primary'    => $primary,
				'fallback'   => $fallback,
			];
		}

		$used = [];
		$result = [];

		// Primera pasada: los "dueños" técnicos del email (user_email == primary) tienen
		// prioridad. Son usuarios que en wp_users son la UNIQUE key de ese email, así que
		// no podemos reubicarlos a ningún fallback distinto sin perder su identidad.
		foreach ($candidates as $userId => $cand) {
			if ($cand['user_email'] !== '' && $cand['primary'] === $cand['user_email']) {
				if (!isset($used[$cand['primary']])) {
					$used[$cand['primary']] = $userId;
					$result[$userId] = ['email' => $cand['primary'], 'reason' => null];
				}
			}
		}

		// Segunda pasada: el resto de users cae en cascada primary → fallback → skip,
		// respetando lo que los dueños ya reservaron.
		foreach ($candidates as $userId => $cand) {
			if (isset($result[$userId])) {
				continue;
			}

			$primary  = $cand['primary'];
			$fallback = $cand['fallback'];

			if ($primary === '' && $fallback === '') {
				$result[$userId] = ['email' => '', 'reason' => SkipReason::EMPTY_EMAIL];
				continue;
			}

			$chosen = null;
			if ($primary !== '' && !isset($used[$primary])) {
				$chosen = $primary;
			} elseif ($fallback !== '' && $fallback !== $primary && !isset($used[$fallback])) {
				$chosen = $fallback;
			}

			if ($chosen === null) {
				$result[$userId] = ['email' => '', 'reason' => SkipReason::EMAIL_COLLISION];
				continue;
			}

			$used[$chosen] = $userId;
			$result[$userId] = ['email' => $chosen, 'reason' => null];
		}

		return $result;
	}

	private function markAsSkipped(\WP_User $user, string $reason): void
	{
		update_user_meta($user->ID, MetadataHelper::getSynchronizationStatus(), SynchronizationStatus::SKIPPED);
		update_user_meta($user->ID, MetadataHelper::getSkipReason(), $reason);
		delete_user_meta($user->ID, MetadataHelper::getResourceId());
		delete_user_meta($user->ID, MetadataHelper::isResourceCreated());

		$this->logManager->createLog(
			LogType::WARNING,
			sprintf("User %d skipped: %s", $user->ID, $reason)
		);
	}
}
