from datetime import datetime
from zoneinfo import ZoneInfo

import pytest
from freezegun import freeze_time
from pytest_mock import MockFixture
from src.__types.index import Client
from src.domain.entities.client_api_key import ClientApiKey, ClientApiKeyAlgorithm, ClientApiKeyStatus
from src.domain.services.client_api_key_service import ClientApiKeyService

STAGE = "test"


class TestClientApiKeyService:
    @pytest.fixture(autouse=True)
    def mock_settings(self, mocker: MockFixture):
        class DummySettings:
            stage = STAGE

        mocker.patch("src.domain.services.client_api_key_service.settings", DummySettings())

    @pytest.fixture
    def service(self) -> ClientApiKeyService:
        return ClientApiKeyService()

    class TestIssue:
        def assert_common_entity_fields(self, entity: ClientApiKey, client: Client):
            assert entity.client_id == client.id
            assert entity.algorithm == ClientApiKeyAlgorithm.SHA256
            assert entity.status == ClientApiKeyStatus.ACTIVE
            assert entity.rotated_at is None

        @freeze_time("2024-01-01 12:00:00", tz_offset=-9)  # Asia/Tokyo
        def test_issue_with_duration(self, service: ClientApiKeyService, mock_client: Client):
            """APIキー発行時に有効期限が正しく設定されることを確認する"""
            result = service.issue(mock_client, 7)
            expected_expires_at = datetime(2024, 1, 8, 12, 0, tzinfo=ZoneInfo("Asia/Tokyo"))

            self.assert_common_entity_fields(result.entity, mock_client)
            assert result.entity.expires_at == expected_expires_at

        @freeze_time("2024-01-01 12:00:00", tz_offset=-9)  # Asia/Tokyo
        def test_issue_without_duration(self, service: ClientApiKeyService, mock_client: Client):
            """duration_days が None の場合、APIキー発行時に有効期限が None に設定されることを確認する"""
            result = service.issue(mock_client, None)

            self.assert_common_entity_fields(result.entity, mock_client)
            assert result.entity.expires_at is None

        def test_plain_key_format(self, service: ClientApiKeyService, mock_client: Client):
            """発行されるAPIキーのフォーマットが正しいことを確認する"""
            result = service.issue(mock_client, 7)
            parts = result.plain_key.get_secret_value().split(".")

            assert parts[0] == STAGE  # stage
            assert parts[1] == mock_client.name  # client name
            assert parts[2] == mock_client.stage  # client stage
            assert len(parts[3]) > 0  # key id
            assert len(parts[4]) > 0  # raw secret
