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

declare(strict_types=1);

namespace BookiFlex\Extensions\Onboarding\ValueObject;

defined( 'ABSPATH' ) || exit;

/**
 * Weekly Pricing Value Object
 *
 * Stores pricing for each day of the week (Monday-Sunday) in cents.
 * Supports two formats:
 * - Simple: int value (same price for all occupancy variants)
 * - Occupancy-based: array with variant keys (__1, __2, etc.)
 *
 * @package BookiFlex\Extensions\Onboarding\ValueObject
 * @since   1.0.0
 */
class WeeklyPricing
{
    /**
     * Weekly Pricing constructor
     *
     * @param int|array $mon    Monday price(s) in cents
     * @param int|array $tue   Tuesday price(s) in cents
     * @param int|array $wed Wednesday price(s) in cents
     * @param int|array $thu  Thursday price(s) in cents
     * @param int|array $fri    Friday price(s) in cents
     * @param int|array $sat  Saturday price(s) in cents
     * @param int|array $sun    Sunday price(s) in cents
     *
     * @throws \InvalidArgumentException If validation fails
     */
    public function __construct(
        public readonly int|array $mon,
        public readonly int|array $tue,
        public readonly int|array $wed,
        public readonly int|array $thu,
        public readonly int|array $fri,
        public readonly int|array $sat,
        public readonly int|array $sun,
    ) {
        $this->validate();
    }

    /**
     * Validate all prices are non-negative
     *
     * @return void
     * @throws \InvalidArgumentException If any price is negative or invalid format
     */
    public function validate(): void
    {
        $days = ['mon' => $this->mon, 'tue' => $this->tue, 'wed' => $this->wed, 'thu' => $this->thu, 'fri' => $this->fri, 'sat' => $this->sat, 'sun' => $this->sun];

        foreach ($days as $dayName => $dayPrice) {
            if (is_int($dayPrice)) {
                // Simple format: single price
                if ($dayPrice < 0) {
                    throw new \InvalidArgumentException(
                        sprintf(
                            'Price for %s must be non-negative',
                            $dayName
                        )
                    );
                }
            } elseif (is_array($dayPrice)) {
                // Occupancy-based format: array of variant prices
                if (empty($dayPrice)) {
                    throw new \InvalidArgumentException(
                        sprintf(
                            'Price array for %s cannot be empty',
                            $dayName
                        )
                    );
                }

                foreach ($dayPrice as $variant => $value) {
                    if (!is_int($value) || $value < 0) {
                        throw new \InvalidArgumentException(
                            sprintf(
                                'Price for %1$s variant %2$s must be non-negative integer',
                                $dayName,
                                $variant
                            )
                        );
                    }
                }
            } else {
                throw new \InvalidArgumentException(
                    sprintf(
                        'Price for %s must be int or array',
                        $dayName
                    )
                );
            }
        }
    }

    /**
     * Convert to array representation
     *
     * @return array{mon: int, tue: int, wed: int, thu: int, fri: int, sat: int, sun: int}
     */
    public function toArray(): array
    {
        return [
            'mon' => $this->mon,
            'tue' => $this->tue,
            'wed' => $this->wed,
            'thu' => $this->thu,
            'fri' => $this->fri,
            'sat' => $this->sat,
            'sun' => $this->sun,
        ];
    }

    /**
     * Create from array
     *
     * @param  array<string, int> $data Array with day names as keys
     * @return self
     * @throws \InvalidArgumentException If required day is missing
     */
    public static function fromArray(array $data): self
    {
        return new self(
            mon: $data['mon'] ?? throw new \InvalidArgumentException('mon price required'),
            tue: $data['tue'] ?? throw new \InvalidArgumentException('tue price required'),
            wed: $data['wed'] ?? throw new \InvalidArgumentException('wed price required'),
            thu: $data['thu'] ?? throw new \InvalidArgumentException('thu price required'),
            fri: $data['fri'] ?? throw new \InvalidArgumentException('fri price required'),
            sat: $data['sat'] ?? throw new \InvalidArgumentException('sat price required'),
            sun: $data['sun'] ?? throw new \InvalidArgumentException('sun price required'),
        );
    }

    /**
     * Get price for a specific day
     *
     * Returns first available price for the day (for backward compatibility).
     * For occupancy-based pricing, returns the first variant's price.
     *
     * @param  string $day Day name (mon, tue, etc.)
     * @return int Price in cents
     * @throws \InvalidArgumentException If invalid day name
     */
    public function getPriceForDay(string $day): int
    {
        $dayPrice = match (strtolower($day)) {
            'mon' => $this->mon,
            'tue' => $this->tue,
            'wed' => $this->wed,
            'thu' => $this->thu,
            'fri' => $this->fri,
            'sat' => $this->sat,
            'sun' => $this->sun,
            default => throw new \InvalidArgumentException(
                sprintf(
                    'Invalid day name: %s',
                    $day
                )
            ),
        };

        // Return first available price
        return is_array($dayPrice) ? (int)current($dayPrice) : $dayPrice;
    }

    /**
     * Get price for a specific day and occupancy variant
     *
     * @param  string $day     Day name (mon, tue, etc.)
     * @param  string $variant Occupancy variant (__1, __2, etc.)
     * @return int Price in cents
     * @throws \InvalidArgumentException If invalid day name or variant not found
     */
    public function getPriceForDayAndVariant(string $day, string $variant): int
    {
        $dayPrice = match (strtolower($day)) {
            'mon' => $this->mon,
            'tue' => $this->tue,
            'wed' => $this->wed,
            'thu' => $this->thu,
            'fri' => $this->fri,
            'sat' => $this->sat,
            'sun' => $this->sun,
            default => throw new \InvalidArgumentException(
                sprintf(
                    'Invalid day name: %s',
                    $day
                )
            ),
        };

        // If occupancy-based pricing (array)
        if (is_array($dayPrice)) {
            if (!isset($dayPrice[$variant])) {
                throw new \InvalidArgumentException(
                    sprintf(
                        /* translators: %1$s: variant, %2$s: day name */
                        'Variant %1$s not found for %2$s',
                        $variant,
                        $day
                    )
                );
            }
            return $dayPrice[$variant];
        }

        // Simple format: same price for all variants
        return $dayPrice;
    }
}
