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

declare(strict_types=1);

namespace BookiFlex\Core\ORM\Typecast;

use BookiFlex\Core\Model\Penalty\PenaltyStatus;
use Brick\Money\Money;
use Cycle\ORM\Parser\CastableInterface;
use Cycle\ORM\Parser\UncastableInterface;
use DateTimeImmutable;
use DateTimeZone;

defined( 'ABSPATH' ) || exit;

/**
 * Typecast handler for Penalty entity.
 * Handles conversion between database representation and domain objects.
 */
class PenaltyTypecast implements CastableInterface, UncastableInterface
{
    /**
     * Define casting rules for the entity.
     */
    public function setRules(array $rules): array
    {
        return $rules;
    }

    /**
     * Cast data from database to entity format.
     * Converts database values to domain objects.
     *
     * @param array $data Raw database data
     * @return array Data with domain objects
     */
    public function cast(array $data): array
    {
        // Cast status to PenaltyStatus value object
        if (isset($data['status']) && is_string($data['status'])) {
            $data['status'] = new PenaltyStatus($data['status']);
        }

        // Cast amount and currency to Money object
        if (isset($data['amount'], $data['currency'])) {
            try {
                $data['amount'] = Money::ofMinor($data['amount'], $data['currency']);
            } catch (\Exception $e) {
                // If casting fails, keep original values for validation to handle
                $data['amount_error'] = $e->getMessage();
            }
        }

        // Cast applied_at to DateTimeImmutable
        if (isset($data['applied_at'])) {
            if (is_string($data['applied_at'])) {
                try {
                    $data['applied_at'] = new DateTimeImmutable($data['applied_at'], new DateTimeZone('UTC'));
                } catch (\Exception $e) {
                    $data['applied_at'] = null;
                }
            }
        }

        // Cast payment_reference - ensure it's null if empty
        if (isset($data['payment_reference']) && empty($data['payment_reference'])) {
            $data['payment_reference'] = null;
        }

        return $data;
    }

    /**
     * Uncast data from entity to database format.
     * Converts domain objects to database values.
     *
     * @param array $data Entity data with domain objects
     * @return array Data ready for database storage
     */
    public function uncast(array $data): array
    {
        // Uncast PenaltyStatus to string
        if (isset($data['status']) && $data['status'] instanceof PenaltyStatus) {
            $data['status'] = (string) $data['status'];
        }

        // Uncast Money object to amount and currency
        if (isset($data['amount']) && $data['amount'] instanceof Money) {
            $data['currency'] = $data['amount']->getCurrency()->getCurrencyCode();
            $data['amount'] = $data['amount']->getMinorAmount()->toInt();
        }

        // Uncast DateTimeImmutable to string
        if (isset($data['applied_at']) && $data['applied_at'] instanceof DateTimeImmutable) {
            $data['applied_at'] = $data['applied_at']
                ->setTimezone(new DateTimeZone('UTC'))
                ->format('Y-m-d H:i:s');
        }

        // Ensure payment_reference is null or string
        if (isset($data['payment_reference']) && empty($data['payment_reference'])) {
            $data['payment_reference'] = null;
        }

        return $data;
    }
}
