<?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\Service\CashierFactory;
use BookiFlex\Extensions\Cart\Contract\CartItemInterface;
use Brick\Money\Money;

defined( 'ABSPATH' ) || exit;

class ExtraCartItem implements CartItemInterface
{
    public const TYPE = 'extra';

    private array $validationErrors = [];

    public function __construct(
        private readonly int $extraId,
        private readonly string $parentHash,
        private string $name,
        private readonly int $quantity,
        private readonly int $guests,
        private readonly int $nights,
        private readonly string $currency,
        private ?int $unitPrice = null,
        private readonly bool $included = false,
    ) {
    }

    public static function fromRequest(array $data): self
    {
        return new self(
            extraId: (int) $data['extraId'],
            parentHash: (string) $data['parentHash'],
            name: (string) ($data['name'] ?? ''),
            quantity: (int) ($data['quantity'] ?? 1),
            guests: (int) ($data['guests'] ?? 1),
            nights: (int) ($data['nights'] ?? 1),
            currency: (string) $data['currency'],
            unitPrice: isset($data['unitPrice']) ? (int) $data['unitPrice'] : null,
            included: (bool) ($data['included'] ?? false),
        );
    }

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

    public function getHash(): string
    {
        return md5($this->extraId . '|' . $this->parentHash . '|' . ($this->included ? '1' : '0'));
    }

    public function getName(): string
    {
        return $this->name !== '' ? $this->name : sprintf('Extra #%d', $this->extraId);
    }

    public function calculateUnitPrice(): int
    {
        return $this->unitPrice ?? 0;
    }

    /**
     * Compatible with CartService::calculateTotal which destructures
     * ['accommodation' => Cashier, 'daily' => array]. Daily breakdown is irrelevant
     * for extras and stays empty.
     */
    public function getSummary(): array
    {
        $price = Money::ofMinor($this->unitPrice ?? 0, $this->currency);

        return [
            'accommodation' => CashierFactory::make($price, []),
            'daily'         => [],
        ];
    }

    public function validate(): bool
    {
        $this->validationErrors = [];

        if ($this->extraId <= 0) {
            $this->validationErrors['invalid_extra_id'] = 'Invalid extra service id';
        }
        if ($this->parentHash === '') {
            $this->validationErrors['parent_required'] = 'Parent accommodation hash is required';
        }
        if ($this->quantity < 1) {
            $this->validationErrors['quantity_invalid'] = 'Quantity must be at least 1';
        }

        return empty($this->validationErrors);
    }

    public function getValidationErrors(): array
    {
        return $this->validationErrors;
    }

    public function toArray(): array
    {
        return [
            'type'       => self::TYPE,
            'extraId'    => $this->extraId,
            'parentHash' => $this->parentHash,
            'name'       => $this->name,
            'quantity'   => $this->quantity,
            'guests'     => $this->guests,
            'nights'     => $this->nights,
            'currency'   => $this->currency,
            'unitPrice'  => $this->unitPrice,
            'included'   => $this->included,
        ];
    }

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

    public function getExtraId(): int
    {
        return $this->extraId;
    }

    public function getParentHash(): string
    {
        return $this->parentHash;
    }

    public function getQuantity(): int
    {
        return $this->quantity;
    }

    public function getGuests(): int
    {
        return $this->guests;
    }

    public function getNights(): int
    {
        return $this->nights;
    }

    public function getCurrency(): string
    {
        return $this->currency;
    }

    public function getUnitPrice(): ?int
    {
        return $this->unitPrice;
    }

    public function isIncluded(): bool
    {
        return $this->included;
    }

    public function setUnitPrice(int $unitPrice): void
    {
        $this->unitPrice = $unitPrice;
    }

    public function setName(string $name): void
    {
        $this->name = $name;
    }
}
