<?php

declare(strict_types=1);

namespace App\Framework\Core\ValueObjects;

use DateTimeZone;
use InvalidArgumentException;

/**
 * Timezone value object
 */
final readonly class Timezone
{
    public function __construct(
        public string $value
    ) {
        $this->validate();
    }

    /**
     * Create from timezone string
     */
    public static function fromString(string $timezone): self
    {
        return new self($timezone);
    }

    /**
     * Create UTC timezone
     */
    public static function utc(): self
    {
        return new self('UTC');
    }

    /**
     * Create timezone for Europe/Berlin
     */
    public static function berlin(): self
    {
        return new self('Europe/Berlin');
    }

    /**
     * Create timezone for America/New_York
     */
    public static function newYork(): self
    {
        return new self('America/New_York');
    }

    /**
     * Create timezone for Asia/Tokyo
     */
    public static function tokyo(): self
    {
        return new self('Asia/Tokyo');
    }

    /**
     * Get DateTimeZone object
     */
    public function toDateTimeZone(): DateTimeZone
    {
        return new DateTimeZone($this->value);
    }

    /**
     * Get timezone offset in seconds from UTC
     */
    public function getOffsetFromUtc(): int
    {
        $timezone = $this->toDateTimeZone();
        $now = new \DateTime('now', $timezone);

        return $timezone->getOffset($now);
    }

    /**
     * Get timezone offset as string (+/-HH:MM)
     */
    public function getOffsetString(): string
    {
        $offset = $this->getOffsetFromUtc();
        $hours = intval($offset / 3600);
        $minutes = abs(intval(($offset % 3600) / 60));

        return sprintf('%+03d:%02d', $hours, $minutes);
    }

    /**
     * Check if timezone is UTC
     */
    public function isUtc(): bool
    {
        return $this->value === 'UTC';
    }

    /**
     * Check if timezone uses daylight saving time
     */
    public function usesDaylightSaving(): bool
    {
        $timezone = $this->toDateTimeZone();
        $transitions = $timezone->getTransitions();

        // If there are transitions, it likely uses DST
        return count($transitions) > 1;
    }

    /**
     * Get timezone name
     */
    public function getName(): string
    {
        return $this->value;
    }

    /**
     * Get timezone abbreviation
     */
    public function getAbbreviation(): string
    {
        $timezone = $this->toDateTimeZone();
        $now = new \DateTime('now', $timezone);

        return $now->format('T');
    }

    /**
     * Get continent
     */
    public function getContinent(): ?string
    {
        $parts = explode('/', $this->value);

        return $parts[0] ?? null;
    }

    /**
     * Get city/region
     */
    public function getCity(): ?string
    {
        $parts = explode('/', $this->value);

        return $parts[1] ?? null;
    }

    /**
     * Check if this is a valid timezone for geographic region
     */
    public function isValidForCountry(CountryCode $countryCode): bool
    {
        $countryTimezones = [
            'DE' => ['Europe/Berlin'],
            'US' => [
                'America/New_York', 'America/Chicago', 'America/Denver',
                'America/Los_Angeles', 'America/Anchorage', 'Pacific/Honolulu',
            ],
            'GB' => ['Europe/London'],
            'FR' => ['Europe/Paris'],
            'JP' => ['Asia/Tokyo'],
            'CN' => ['Asia/Shanghai'],
            'IN' => ['Asia/Kolkata'],
            'RU' => [
                'Europe/Moscow', 'Asia/Yekaterinburg', 'Asia/Novosibirsk',
                'Asia/Krasnoyarsk', 'Asia/Irkutsk', 'Asia/Yakutsk',
                'Asia/Vladivostok', 'Asia/Magadan', 'Asia/Kamchatka',
            ],
            'AU' => [
                'Australia/Sydney', 'Australia/Melbourne', 'Australia/Brisbane',
                'Australia/Perth', 'Australia/Adelaide', 'Australia/Darwin',
            ],
            'BR' => [
                'America/Sao_Paulo', 'America/Manaus', 'America/Fortaleza',
                'America/Recife', 'America/Bahia',
            ],
            'CA' => [
                'America/Toronto', 'America/Vancouver', 'America/Montreal',
                'America/Calgary', 'America/Edmonton', 'America/Winnipeg',
            ],
        ];

        $validTimezones = $countryTimezones[$countryCode->value] ?? [];

        return in_array($this->value, $validTimezones, true);
    }

    /**
     * Convert to array
     */
    public function toArray(): array
    {
        return [
            'timezone' => $this->value,
            'offset' => $this->getOffsetString(),
            'offset_seconds' => $this->getOffsetFromUtc(),
            'abbreviation' => $this->getAbbreviation(),
            'continent' => $this->getContinent(),
            'city' => $this->getCity(),
            'is_utc' => $this->isUtc(),
            'uses_daylight_saving' => $this->usesDaylightSaving(),
        ];
    }

    /**
     * String representation
     */
    public function toString(): string
    {
        return $this->value;
    }

    /**
     * Validate timezone
     */
    private function validate(): void
    {
        try {
            new DateTimeZone($this->value);
        } catch (\Exception $e) {
            throw new InvalidArgumentException("Invalid timezone: {$this->value}");
        }
    }

    public function __toString(): string
    {
        return $this->value;
    }
}
