<?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\Model;

use BookiFlex\Core\Exception\RuntimeException;
use BookiFlex\Extensions\Cart\Contract\CartItemInterface;
use BookiFlex\Core\Service\StayCostCalculator\ValueObject\StayCostCalculatorResult;
use BookiFlex\Core\ValueObject\AccommodationSelection;
use BookiFlex\Core\ValueObject\GuestConfiguration;
use BookiFlex\Core\ValueObject\StayPeriod;
use DateTimeImmutable;

defined( 'ABSPATH' ) || exit;

class AccommodationCartItem implements CartItemInterface
{
    public const TYPE = 'accommodation';

    private array $validationErrors = [];
    private ?StayCostCalculatorResult $stayCostResult = null;

    public function __construct(
        private readonly StayPeriod $stayPeriod,
        private readonly AccommodationSelection $accommodationSelection,
        private readonly GuestConfiguration $guestConfiguration,
        private int $paymentTypeId,
        private string $paymentTypeName,
        private string $ratePlanName = '',
        private string $accommodationTypeName = '',
        private ?int $cancellationPolicyId = null,
        private string $cancellationPolicyName = '',
        private ?array $cancellationConsequences = null
    ) {
    }

    /**
     * Create from request data
     */
    public static function fromRequest(array $data): self
    {
        $checkIn = DateTimeImmutable::createFromFormat('Y-m-d', $data['checkInDate']);
        $checkOut = DateTimeImmutable::createFromFormat('Y-m-d', $data['checkOutDate']);

        return new self(
            new StayPeriod($checkIn, $checkOut),
            new AccommodationSelection(
                $data['accommodationTypeId'] ?? $data['accommodationType'],
                $data['ratePlanId'] ?? $data['ratePlan']
            ),
            new GuestConfiguration(
                $data['adults'],
                $data['children'] ?? []
            ),
            $data['paymentTypeId'] ?? 0,
            $data['paymentTypeName'] ?? '',
            $data['ratePlanName'] ?? '',
            $data['accommodationTypeName'] ?? '',
            $data['cancellationPolicyId'] ?? null,
            $data['cancellationPolicyName'] ?? '',
            $data['cancellationConsequences'] ?? null,
        );
    }

    public function getType(): string
    {
        return self::TYPE;
    }

    public function getHash(): string
    {
        $children = $this->guestConfiguration->getChildren();
        if (!empty($children)) {
            sort($children, SORT_NUMERIC);
        }

        return md5(
            $this->stayPeriod->getCheckInDate()->format('Y-m-d') .
            $this->stayPeriod->getCheckOutDate()->format('Y-m-d') .
            $this->accommodationSelection->getRatePlanId() .
            $this->accommodationSelection->getAccommodationTypeId() .
            $this->guestConfiguration->getAdults() .
            implode(',', $children)
        );
    }

    public function getName(): string
    {
        if ($this->accommodationTypeName && $this->ratePlanName) {
            return sprintf('%s (%s)', $this->accommodationTypeName, $this->ratePlanName);
        }

        return sprintf(
            'Accommodation #%d, Rate Plan #%d',
            $this->accommodationSelection->getAccommodationTypeId(),
            $this->accommodationSelection->getRatePlanId()
        );
    }

    public function calculateUnitPrice(): int
    {
        if (!$this->stayCostResult) {
            throw new RuntimeException('Summary price for cart item is not defined');
        }

        if ($this->stayCostResult->hasError()) {
//            var_dump($this->stayCostResult); die;
            throw new RuntimeException($this->stayCostResult->getErrorMessage(), 0, null, ['errorCode' => $this->stayCostResult->getErrorType()]);
        }

        return $this->stayCostResult->getFinalPrice()
            ->getMinorAmount()
            ->toInt();
    }

    public function getSummary(): array
    {
        if (!$this->stayCostResult) {
            throw new RuntimeException('Summary price for cart item is not defined');
        }

        return ['accommodation' => $this->stayCostResult->getCashier(), 'daily' => $this->stayCostResult->aggregateDailyCashiers()];
    }

    /**
     * Simple validation rules
     */
    public function validate(): bool
    {
        $this->validationErrors = [];

        // Check dates
        if ($this->stayPeriod->getCheckInDate() >= $this->stayPeriod->getCheckOutDate()) {
            $this->validationErrors['check_out_before_check_in'] = 'Check-out date must be after check-in date';
        }

        // Check if dates are in the past
        $today = new DateTimeImmutable('today');
        if ($this->stayPeriod->getCheckInDate() < $today) {
            $this->validationErrors['check_in_date_in_past'] = 'Check-in date cannot be in the past';
        }

        return empty($this->validationErrors);
    }

    /**
     * Get validation errors as associative array
     *
     * @return array<string, string> Associative array where key is error code and value is error message
     */
    public function getValidationErrors(): array
    {
        return $this->validationErrors;
    }

    public function toArray(): array
    {
        return [
            'type' => self::TYPE,
            'checkInDate' => $this->stayPeriod->getCheckInDate()->format('Y-m-d'),
            'checkOutDate' => $this->stayPeriod->getCheckOutDate()->format('Y-m-d'),
            'accommodationTypeId' => $this->accommodationSelection->getAccommodationTypeId(),
            'ratePlanId' => $this->accommodationSelection->getRatePlanId(),
            'adults' => $this->guestConfiguration->getAdults(),
            'children' => $this->guestConfiguration->getChildren(),
            'paymentTypeId' => $this->paymentTypeId,
            'paymentTypeName' => $this->paymentTypeName,
            'ratePlanName' => $this->ratePlanName,
            'accommodationTypeName' => $this->accommodationTypeName,
            'cancellationPolicyId' => $this->cancellationPolicyId,
            'cancellationPolicyName' => $this->cancellationPolicyName,
            'cancellationConsequences' => $this->cancellationConsequences,
        ];
    }

    public static function fromArray(array $data): self
    {
        return self::fromRequest($data);
    }

    // Getters for specific accommodation data
    public function getStayPeriod(): StayPeriod
    {
        return $this->stayPeriod;
    }

    public function getAccommodationSelection(): AccommodationSelection
    {
        return $this->accommodationSelection;
    }

    public function getGuestConfiguration(): GuestConfiguration
    {
        return $this->guestConfiguration;
    }

    public function getPaymentTypeId(): int
    {
        return $this->paymentTypeId;
    }

    public function setPaymentType(int $paymentTypeId, string $paymentTypeName): void
    {
        $this->paymentTypeId = $paymentTypeId;
        $this->paymentTypeName = $paymentTypeName;
    }

    public function getPaymentTypeName(): string
    {
        return $this->paymentTypeName;
    }

    public function setStayCostResult(StayCostCalculatorResult $result): void
    {
        $this->stayCostResult = $result;
    }

    public function getStayCostResult(): ?StayCostCalculatorResult
    {
        return $this->stayCostResult;
    }

    public function setNames(string $accommodationTypeName, string $ratePlanName): void
    {
        $this->accommodationTypeName = $accommodationTypeName;
        $this->ratePlanName = $ratePlanName;
    }

    public function getAccommodationTypeName(): string
    {
        return $this->accommodationTypeName;
    }

    public function getRatePlanName(): string
    {
        return $this->ratePlanName;
    }

    public function setCancellationPolicy(int $policyId, string $policyName, array $consequences): void
    {
        $this->cancellationPolicyId = $policyId;
        $this->cancellationPolicyName = $policyName;
        $this->cancellationConsequences = $consequences;
    }

    public function getCancellationPolicyId(): ?int
    {
        return $this->cancellationPolicyId;
    }

    public function getCancellationPolicyName(): string
    {
        return $this->cancellationPolicyName;
    }

    public function getCancellationConsequences(): array
    {
        return $this->cancellationConsequences;
    }
}
