<?php
/**
 * BookiFlex Cart Extension
 *
 * @package    BookiFlex
 * @subpackage Cart
 * @author     BookiFlex
 * @copyright  Copyright (c) 2025 BookiFlex
 * @license    GPL-2.0-or-later
 *
 * @link       https://bookiflex.com
 */

declare(strict_types=1);

namespace BookiFlex\Extensions\Cart\Api\Transformer;

use BookiFlex\Core\Model\PaymentType\PaymentType;
use BookiFlex\Core\Service\Aggregator\SummaryAggregator;
use BookiFlex\Extensions\Cart\Model\AccommodationCartItem;
use BookiFlex\Extensions\Cart\Model\Cart;
use BookiFlex\Extensions\Cart\Model\ExtraCartItem;
use BookiFlex\Extensions\Cart\Service\PaymentTypeService;

defined( 'ABSPATH' ) || exit;

/**
 * Transform cart data for API responses
 */
class CartTransformer
{
    public function __construct(
        private readonly PaymentTypeService $paymentTypeService,
    ) {
    }

    /**
     * Transform cart to API response format
     */
    public function transform(Cart $cart, array $totals = []): array
    {
        $dailyBreakdownByHash = $this->extractDailyBreakdownByHash($totals);

        return [
            ...$this->transformCart($cart),
            'items' => $this->transformItems($cart, $dailyBreakdownByHash),
            'totals' => $this->transformTotals($cart, $totals),
            'customer' => $this->transformCustomer($cart),
            'payment' => $this->buildPaymentInfo($cart),
        ];
    }

    /**
     * Index per-item daily placement breakdown by cart item hash.
     * The totals payload is shaped as $totals[$type][] = ['hash' => ..., 'dailyBreakdown' => [...], ...].
     *
     * @return array<string, array<int, array<string, mixed>>>
     */
    private function extractDailyBreakdownByHash(array $totals): array
    {
        $byHash = [];
        foreach ($totals as $byType) {
            if (!is_array($byType)) {
                continue;
            }
            foreach ($byType as $summary) {
                if (!is_array($summary)) {
                    continue;
                }
                $hash      = $summary['hash']           ?? null;
                $breakdown = $summary['dailyBreakdown'] ?? null;
                if ($hash !== null && is_array($breakdown) && $breakdown !== []) {
                    $byHash[$hash] = $breakdown;
                }
            }
        }
        return $byHash;
    }

    protected function transformCart(Cart $cart): array
    {
        return [
            'sessionId' => $cart->getSessionId(),
            'currency' => $cart->getCurrency(),
            'status' => $cart->getStatus(),
            'checkInTime' => $cart->getCheckInTime(),
            'checkOutTime' => $cart->getCheckOutTime(),
            'promoCode' => $cart->getPromoCode(),
            'specialRequest' => $cart->getSpecialRequest(),
            'arrivalTime' => $cart->getArrivalTime(),
            'metadata' => [
                'itemsCount' => count($cart->getItems()),
                'totalQuantity' => $cart->getTotalQuantity(),
                'isEmpty' => $cart->isEmpty(),
                'createdAt' => $cart->getCreatedAt()->format('c'),
                'updatedAt' => $cart->getUpdatedAt()?->format('c'),
            ],
        ];
    }

    protected function transformTotals(Cart $cart, array $totals): array
    {
        $aggregator = new SummaryAggregator($cart->getCurrency());

        if (!$cart->isEmpty()) {
            foreach ($totals as $type => $items) {
                foreach ($items as $summary) {
                    if ($type === 'extra') {
                        $aggregator->addSummary(
                            $summary['accommodation']['cashier'],
                            'extra',
                            'item',
                            $summary['accommodation']['context'],
                        );
                    } else {
                        // Add the main accommodation summary
                        $aggregator->addSummary(
                            $summary['accommodation']['cashier'],
                            'cart',
                            'total',
                            $summary['accommodation']['context'],
                        );

                        foreach ($summary['daily'] as $date => $cashier) {
                            $aggregator->addSummary(
                                $cashier,
                                'accommodation',
                                'daily',
                                [
                                    'date' => $date,
                                    'id' => $summary['accommodation']['context']['id']
                                ]
                            );
                        }
                    }
                }
            }
        }

        $aggregated = $aggregator->aggregate();
        return $aggregated->toArray();
    }

    /**
     * Transform customer data
     */
    protected function transformCustomer(Cart $cart): ?array
    {
        $customer = $cart->getCustomer();
        if (!$customer) {
            return null;
        }

        return [
            'firstName' => $customer->getFirstName(),
            'lastName' => $customer->getLastName(),
            'email' => $customer->getEmail(),
            'phone' => $customer->getPhone(),
        ];
    }

    /**
     * Transform cart items
     *
     * @param array<string, array<int, array<string, mixed>>> $dailyBreakdownByHash
     */
    protected function transformItems(Cart $cart, array $dailyBreakdownByHash = []): array
    {
        $availablePaymentTypes = $this->paymentTypeService->getAvailablePaymentTypes($cart);
        $transformed = [];

        foreach ($cart->getItems() as $item) {
            $baseData = [
                'type' => $item->getType(),
                'hash' => $item->getHash(),
                'quantity' => $item->getQuantity(),
                'unitPrice'  => $item->getUnitPrice(),
                'totalPrice' => $item->getTotalPrice(),
            ];

            $domainItem = $item->toDomainObject();
            // Add type-specific data
            if ($domainItem instanceof AccommodationCartItem) {
                $dailyBreakdown = $dailyBreakdownByHash[$item->getHash()] ?? [];
                $transformed[] = array_merge(
                    $baseData,
                    $this->transformAccommodationItem($domainItem, $cart, $availablePaymentTypes, $dailyBreakdown)
                );
            } elseif ($domainItem instanceof ExtraCartItem) {
                $transformed[] = array_merge($baseData, $this->transformExtraItem($domainItem));
            } else {
                $transformed[] = $baseData;
            }
        }

        return $transformed;
    }

    /**
     * Transform extra cart item. Cart-level base data (cart-owned fields).
     * Pro override (ExtrasCartTransformer) extends this with domain details
     * such as description / charging rate / thumbnail.
     */
    protected function transformExtraItem(ExtraCartItem $item): array
    {
        return [
            'name' => $item->getName(),
            'details' => [
                'extra' => [
                    'extraId'    => $item->getExtraId(),
                    'parentHash' => $item->getParentHash(),
                    'included'   => $item->isIncluded(),
                ],
            ],
        ];
    }

    /**
     * Transform accommodation item
     *
     * @param array<int, array<string, mixed>> $dailyBreakdown
     */
    protected function transformAccommodationItem(
        AccommodationCartItem $item,
        Cart $cart,
        array $availablePaymentTypes,
        array $dailyBreakdown = []
    ): array {
        $details = [
            'accommodation' => [
                'checkInDate' => $item->getStayPeriod()->getCheckInDate()->format('Y-m-d'),
                'checkOutDate' => $item->getStayPeriod()->getCheckOutDate()->format('Y-m-d'),
                'lengthOfStay' => $item->getStayPeriod()->getLengthOfStay(),
                'accommodationType' => [
                    'name' =>  $item->getAccommodationTypeName(),
                ],
                'ratePlan' => [
                    'name' => $item->getRatePlanName(),
                ],
                'cancellationPolicy' => [
                    'name'         => $item->getCancellationPolicyName(),
                    'rules' => $item->getCancellationConsequences(),
                ],
                'paymentType' => [
                    'id'   => $item->getPaymentTypeId(),
                    'name' => $item->getPaymentTypeName(),
                ],
                'adults' => $item->getGuestConfiguration()->getAdults(),
                'children' => $item->getGuestConfiguration()->getChildren(),
                'totalGuests' => $item->getGuestConfiguration()->getTotalGuests(),
                'dailyBreakdown' => $dailyBreakdown,
            ],
            'availablePaymentTypes' => $this->transformPaymentTypes($availablePaymentTypes[$item->getHash()] ?? []),
        ];

        return [
            'name' => $item->getName(),
            'details' => $this->decorateAccommodationDetails($item, $cart, $details),
        ];
    }

    /**
     * Hook for extensions to enrich accommodation details with extra fields
     * (e.g. Pro adds availableExtras). Default returns details unchanged so
     * Cart stays independent of external domains.
     */
    protected function decorateAccommodationDetails(
        AccommodationCartItem $item,
        Cart $cart,
        array $details
    ): array {
        return $details;
    }

    protected function transformPaymentTypes(array $paymentTypes): array
    {
        return array_map(function (PaymentType $paymentType) {
            return [
                'id'          => $paymentType->getId(),
                'name'        => $paymentType->getName(),
                'description' => $paymentType->getDescription(),
            ];
        }, $paymentTypes);
    }

    protected function buildPaymentInfo(Cart $cart): array
    {
        $prepayment = $this->paymentTypeService->calculateTotalPrepayment($cart)->getMinorAmount()->toInt();
        return [
            'prepayment' => $prepayment,
            'onArrival' => $cart->getTotalPrice() - abs($prepayment),
        ];
    }
}
