<?php
/**
 * 2026 Paysera
 *
 * NOTICE OF LICENSE
 *
 * This source file is subject to the Academic Free License (AFL 3.0)
 * that is bundled with this package in the file LICENSE.txt.
 * It is also available through the world-wide-web at this URL:
 * http://opensource.org/licenses/afl-3.0.php
 * If you did not receive a copy of the license and are unable to
 * obtain it through the world-wide-web, please send an email
 * to license@prestashop.com so we can send you a copy immediately.
 *
 * @author Paysera <plugins@paysera.com>
 * @copyright 2026 Paysera
 * @license http://opensource.org/licenses/afl-3.0.php Academic Free License (AFL 3.0)
 * International Registered Trademark & Property of Paysera
 */

declare(strict_types=1);

namespace Paysera\PrestashopPayments\Factory;

use Customer as PrestaShopCustomer;
use Evp\Component\Money\Money;
use Evp\Component\Money\MoneyException;
use Paysera\CheckoutSdk\Entity\PaymentStatus;
use Paysera\PrestashopPayments\Dto\Customer;
use Paysera\PrestashopPayments\Dto\Payer;
use Paysera\PrestashopPayments\Dto\PaymentDetails;
use Paysera\PrestashopPayments\Dto\PaymentListItem;
use Paysera\PrestashopPayments\Dto\PayseraPayment;
use Paysera\PrestashopPayments\Dto\PayseraPaymentInfo;
use Paysera\PrestashopPayments\Repository\PaymentStatusTransitionRepository;
use Paysera\PrestashopPayments\Repository\PayseraPaymentRepository;
use Paysera\PrestashopPayments\Service\PaymentMethodTitleResolver;

if (!defined('_PS_VERSION_')) {
    exit;
}

class PaymentDTOFactory
{
    public function __construct(
        private PayseraPaymentRepository $payseraPaymentRepository,
        private PaymentStatusTransitionRepository $statusTransitionRepository,
        private PaymentMethodTitleResolver $paymentMethodTitleResolver
    ) {
    }

    public function createListItemFromRow(array $row): PaymentListItem
    {
        return new PaymentListItem(
            (int) $row['id_order'],
            $row['reference'],
            $this->createMoney((float) $row['total_paid'], (int) $row['id_currency']),
            $this->resolveStatus($this->trimToNull($row['status'] ?? null)),
            $row['date_add'],
            $this->createCustomerFromRow($row),
            $this->resolveListItemPaymentMethod($row),
            (int) $row['id_cart'],
            $this->trimToNull($row['paysera_payment_id'] ?? null),
            $this->trimToNull($row['paysera_order_id'] ?? null)
        );
    }

    public function createDetailsFromOrder(\Order $order): PaymentDetails
    {
        $orderId = (int) $order->id;
        $payseraPayment = $this->payseraPaymentRepository->findByOrderId($orderId);
        $transitions = $this->statusTransitionRepository->findByOrderId($orderId);
        $customer = $this->createCustomer((int) $order->id_customer);

        return new PaymentDetails(
            $orderId,
            $order->reference,
            $this->createMoney((float) $order->total_paid, (int) $order->id_currency),
            $this->resolveStatus($payseraPayment?->getStatus()),
            $order->date_add,
            $order->date_upd,
            $customer,
            $this->resolveDetailsPaymentMethod($payseraPayment, $order),
            (int) $order->id_cart,
            $this->buildOrderAdminLink($orderId),
            $this->buildPayseraPaymentInfo($payseraPayment),
            $this->buildPayer($payseraPayment, $customer),
            $transitions
        );
    }

    /**
     * @param array<string, mixed> $row
     */
    private function resolveListItemPaymentMethod(array $row): string
    {
        $title = $this->trimToNull($row['paysera_payment_method_title'] ?? null);
        if ($title !== null) {
            return $title;
        }

        $key = $this->trimToNull($row['paysera_payment_method_key'] ?? null);
        if ($key !== null) {
            return $key;
        }

        return (string) ($row['payment'] ?? '');
    }

    private function resolveDetailsPaymentMethod(?PayseraPayment $payment, \Order $order): string
    {
        $key = $this->trimToNull($payment?->getPaymentMethod());

        if ($key === null) {
            return (string) $order->payment;
        }

        return $this->paymentMethodTitleResolver->resolve($key)->getTitle();
    }

    private function buildPayseraPaymentInfo(?PayseraPayment $payment): PayseraPaymentInfo
    {
        if ($payment === null) {
            return new PayseraPaymentInfo();
        }

        return new PayseraPaymentInfo(
            $this->trimToNull($payment->getPayseraPaymentId()),
            $this->trimToNull($payment->getPayseraOrderId()),
            $this->trimToNull($payment->getPaymentLink()),
            $this->moneyFromMinorUnits($payment->getOriginalAmount(), $payment->getOriginalCurrency()),
            $this->moneyFromMinorUnits($payment->getPaidAmount(), $payment->getPaidCurrency()),
            $this->calculateExchangeRate($payment)
        );
    }

    private function buildPayer(?PayseraPayment $payment, Customer $customer): Payer
    {
        return new Payer(
            $this->resolvePayerName($payment, $customer),
            $this->resolvePayerEmail($payment, $customer)
        );
    }

    private function createMoney(float $amount, int $currencyId): Money
    {
        $currency = new \Currency($currencyId);

        return new Money((string) $amount, $currency->iso_code);
    }

    private function createCustomer(int $customerId): Customer
    {
        $customer = new PrestaShopCustomer($customerId);

        return new Customer(
            $customer->email,
            (int) $customer->id,
            $this->trimToNull($customer->firstname),
            $this->trimToNull($customer->lastname)
        );
    }

    /**
     * @param array<string, mixed> $row
     */
    private function createCustomerFromRow(array $row): Customer
    {
        return new Customer(
            (string) ($row['customer_email'] ?? ''),
            (int) $row['id_customer'],
            $this->trimToNull($row['customer_firstname'] ?? null),
            $this->trimToNull($row['customer_lastname'] ?? null)
        );
    }

    private function moneyFromMinorUnits(?int $minorUnits, ?string $currency): ?Money
    {
        $normalizedCurrency = $this->normalizeCurrency($currency);

        if ($minorUnits === null || $normalizedCurrency === null) {
            return null;
        }

        try {
            return Money::createFromMinorUnits($minorUnits, $normalizedCurrency);
        } catch (MoneyException $e) {
            return null;
        }
    }

    private function calculateExchangeRate(?PayseraPayment $payment): ?string
    {
        if ($payment === null) {
            return null;
        }

        $originalCurrency = $this->normalizeCurrency($payment->getOriginalCurrency());
        $paidCurrency = $this->normalizeCurrency($payment->getPaidCurrency());

        if ($originalCurrency === null || $paidCurrency === null) {
            return null;
        }

        if ($originalCurrency === $paidCurrency) {
            return null;
        }

        $originalMoney = $this->moneyFromMinorUnits($payment->getOriginalAmount(), $originalCurrency);
        $paidMoney = $this->moneyFromMinorUnits($payment->getPaidAmount(), $paidCurrency);

        if ($originalMoney === null || $paidMoney === null) {
            return null;
        }

        if ($originalMoney->isZero()) {
            return null;
        }

        $rateMoney = $paidMoney->div($originalMoney->getAmount());

        return number_format((float) $rateMoney->getAmount(), 4, '.', '');
    }

    private function normalizeCurrency(?string $currency): ?string
    {
        $trimmed = $this->trimToNull($currency);

        return $trimmed === null ? null : strtoupper($trimmed);
    }

    private function resolvePayerName(?PayseraPayment $payment, Customer $customer): ?string
    {
        $payseraName = $this->trimToNull($payment?->getPayerName());

        if ($payseraName !== null) {
            return $payseraName;
        }

        $firstname = $customer->getFirstname();
        $lastname = $customer->getLastname();

        $fullName = trim(($firstname ?? '') . ' ' . ($lastname ?? ''));

        return $fullName === '' ? null : $fullName;
    }

    private function resolvePayerEmail(?PayseraPayment $payment, Customer $customer): ?string
    {
        $payseraEmail = $this->trimToNull($payment?->getPayerEmail());

        if ($payseraEmail !== null) {
            return $payseraEmail;
        }

        return $this->trimToNull($customer->getEmail());
    }

    private function resolveStatus(?string $status): string
    {
        return $status ?? PaymentStatus::INITIATED;
    }

    private function trimToNull(?string $value): ?string
    {
        if ($value === null) {
            return null;
        }

        $trimmed = trim($value);

        return $trimmed === '' ? null : $trimmed;
    }

    private function buildOrderAdminLink(int $orderId): string
    {
        $context = \Context::getContext();

        if ($context === null || $context->link === null) {
            return '';
        }

        return (string) $context->link->getAdminLink('AdminOrders', true, [], [
            'id_order' => $orderId,
            'vieworder' => 1,
        ]);
    }
}
