<?php

declare(strict_types=1);

namespace Tests\Unit\Services;

use App\Contracts\Services\DateTimeServiceInterface;
use App\Services\DateTimeService;
use Carbon\Carbon;
use PHPUnit\Framework\TestCase;

final class DateTimeServiceTest extends TestCase
{
    private DateTimeServiceInterface $dateTimeService;

    protected function setUp(): void
    {
        parent::setUp();

        $this->dateTimeService = new DateTimeService;
    }

    protected function tearDown(): void
    {
        Carbon::setTestNow();

        parent::tearDown();
    }

    public function test_now_returns_carbon_instance_in_utc(): void
    {
        Carbon::setTestNow('2025-10-14 12:00:00');

        $now = $this->dateTimeService->now();

        $this->assertInstanceOf(Carbon::class, $now);
        $this->assertEquals('UTC', $now->timezoneName);
        $this->assertEquals('2025-10-14 12:00:00', $now->toDateTimeString());
    }

    public function test_parse_converts_datetime_to_utc(): void
    {
        $result = $this->dateTimeService->parse('2025-10-14 15:00:00', 'America/New_York');

        $this->assertEquals('UTC', $result->timezoneName);
        $this->assertEquals('2025-10-14 19:00:00', $result->toDateTimeString());
    }

    public function test_parse_defaults_to_utc_when_no_timezone_provided(): void
    {
        $result = $this->dateTimeService->parse('2025-10-14 12:00:00');

        $this->assertEquals('UTC', $result->timezoneName);
        $this->assertEquals('2025-10-14 12:00:00', $result->toDateTimeString());
    }

    public function test_to_user_timezone_converts_utc_to_user_timezone(): void
    {
        $utcTime = Carbon::parse('2025-10-14 19:00:00', 'UTC');

        $result = $this->dateTimeService->toUserTimezone($utcTime, 'America/New_York');

        $this->assertEquals('America/New_York', $result->timezoneName);
        $this->assertEquals('2025-10-14 15:00:00', $result->toDateTimeString());
    }

    public function test_to_utc_converts_user_timezone_to_utc(): void
    {
        $userTime = Carbon::parse('2025-10-14 15:00:00', 'America/New_York');

        $result = $this->dateTimeService->toUtc($userTime, 'America/New_York');

        $this->assertEquals('UTC', $result->timezoneName);
        $this->assertEquals('2025-10-14 19:00:00', $result->toDateTimeString());
    }

    public function test_format_for_user_formats_datetime_in_user_timezone(): void
    {
        $utcTime = Carbon::parse('2025-10-14 19:00:00', 'UTC');

        $result = $this->dateTimeService->formatForUser(
            $utcTime,
            'America/New_York',
            'Y-m-d H:i:s'
        );

        $this->assertEquals('2025-10-14 15:00:00', $result);
    }

    public function test_format_for_user_uses_default_format(): void
    {
        $utcTime = Carbon::parse('2025-10-14 19:00:00', 'UTC');

        $result = $this->dateTimeService->formatForUser(
            $utcTime,
            'America/New_York'
        );

        $this->assertEquals('2025-10-14 15:00:00', $result);
    }

    public function test_is_valid_timezone_returns_true_for_valid_timezone(): void
    {
        $this->assertTrue($this->dateTimeService->isValidTimezone('UTC'));
        $this->assertTrue($this->dateTimeService->isValidTimezone('America/New_York'));
        $this->assertTrue($this->dateTimeService->isValidTimezone('Europe/London'));
        $this->assertTrue($this->dateTimeService->isValidTimezone('Asia/Tokyo'));
    }

    public function test_is_valid_timezone_returns_false_for_invalid_timezone(): void
    {
        $this->assertFalse($this->dateTimeService->isValidTimezone('Invalid/Timezone'));
        $this->assertFalse($this->dateTimeService->isValidTimezone('Not_A_Real_Zone'));
        $this->assertFalse($this->dateTimeService->isValidTimezone(''));
    }

    public function test_get_timezones_returns_array_of_timezones(): void
    {
        $timezones = $this->dateTimeService->getTimezones();

        $this->assertIsArray($timezones);
        $this->assertNotEmpty($timezones);
        $this->assertArrayHasKey('UTC', $timezones);
        $this->assertArrayHasKey('America/New_York', $timezones);
        $this->assertEquals('America/New York', $timezones['America/New_York']);
    }

    public function test_timezone_conversions_are_consistent(): void
    {
        $originalUtc = Carbon::parse('2025-10-14 12:00:00', 'UTC');

        // Convert to user timezone and back
        $userTime = $this->dateTimeService->toUserTimezone($originalUtc, 'Europe/Paris');
        $backToUtc = $this->dateTimeService->toUtc($userTime, 'Europe/Paris');

        $this->assertEquals($originalUtc->toDateTimeString(), $backToUtc->toDateTimeString());
        $this->assertEquals('UTC', $backToUtc->timezoneName);
    }
}
