from __future__ import annotations

from typing import Any

from app.domain.keys import generate_experiment_key, generate_variant_key
from app.domain.mappers import to_experiment
from app.repositories.directus.experiments import ExperimentRepository
from app.repositories.directus.variants import VariantRepository
from app.schemas.admin import ExperimentCreate, ExperimentRead, ExperimentUpdate, experiment_to_read_model


class ExperimentService:
    """Application service for experiment use-cases.

    This is where we will keep:
    - validation
    - permission checks
    - state transitions
    - orchestration before writing to Directus
    """

    def __init__(
        self,
        repository: ExperimentRepository | None = None,
        variant_repository: VariantRepository | None = None,
    ) -> None:
        self.repository = repository or ExperimentRepository()
        self.variant_repository = variant_repository or VariantRepository()

    async def list_experiments(self, access_token: str | None = None) -> list[ExperimentRead]:
        items = await self.repository.list(access_token=access_token)
        return [experiment_to_read_model(to_experiment(item)) for item in items]

    async def get_experiment(
        self,
        experiment_id: str,
        access_token: str | None = None,
    ) -> ExperimentRead | None:
        item = await self.repository.get(experiment_id, access_token=access_token)
        if not item:
            return None
        return experiment_to_read_model(to_experiment(item))

    async def create_experiment(
        self,
        payload: ExperimentCreate,
        access_token: str | None = None,
    ) -> ExperimentRead:
        data: dict[str, Any] = payload.model_dump(exclude_none=True)
        data["experiment_key"] = generate_experiment_key()
        item = await self.repository.create(data, access_token=access_token)
        experiment = to_experiment(item)

        # Auto-create 原始版本 (control) variant
        await self.variant_repository.create(
            {
                "experiment_id": experiment.id,
                "variant_key": generate_variant_key(),
                "name": "原始版本",
                "traffic_weight": 50,
            },
            access_token=access_token,
        )

        return experiment_to_read_model(experiment)

    async def update_experiment(
        self,
        experiment_id: str,
        payload: ExperimentUpdate,
        access_token: str | None = None,
    ) -> ExperimentRead | None:
        data: dict[str, Any] = payload.model_dump(exclude_unset=True)
        item = await self.repository.update(experiment_id, data, access_token=access_token)
        if not item:
            return None
        return experiment_to_read_model(to_experiment(item))
