"""سرویس مدیریت پلن‌ها، قابلیت‌ها و اتصال آن‌ها."""
from __future__ import annotations

from typing import Sequence
from uuid import UUID

from sqlalchemy.ext.asyncio import AsyncSession

from app.models.plan import Feature, Plan, PlanFeature
from app.repositories.plan import (
    FeatureRepository,
    PlanFeatureRepository,
    PlanRepository,
)
from app.schemas.plan import FeatureCreate, PlanCreate, PlanFeatureCreate
from shared.exceptions import ConflictError, NotFoundError


class PlanService:
    def __init__(self, session: AsyncSession) -> None:
        self.session = session
        self.plan_repo = PlanRepository(session)
        self.feature_repo = FeatureRepository(session)
        self.plan_feature_repo = PlanFeatureRepository(session)

    # ---- Plans ----
    async def create_plan(self, data: PlanCreate) -> Plan:
        if await self.plan_repo.code_exists(data.code):
            raise ConflictError(f"کد پلن تکراری است: {data.code}", error_code="plan_code_taken")
        plan = Plan(
            code=data.code,
            name=data.name,
            description=data.description,
            is_active=data.is_active,
        )
        await self.plan_repo.add(plan)
        await self.session.commit()
        await self.session.refresh(plan)
        return plan

    async def list_plans(self, *, offset: int, limit: int) -> tuple[Sequence[Plan], int]:
        items = await self.plan_repo.list(offset=offset, limit=limit)
        total = await self.plan_repo.count()
        return items, total

    async def get_plan(self, plan_id: UUID) -> Plan:
        plan = await self.plan_repo.get(plan_id)
        if plan is None:
            raise NotFoundError(f"پلن یافت نشد: {plan_id}")
        return plan

    async def ensure_default_plan(
        self, *, code: str = "FREE", name: str = "رایگان"
    ) -> Plan:
        """پلن پیش‌فرض را برمی‌گرداند؛ در صورت نبود، آن را می‌سازد (idempotent).

        برای اطمینان از وجود پلن پیش‌فرض هنگام onboarding استفاده می‌شود؛ در
        کنار seed مربوط به migration (که در محیط production زودتر اجرا می‌شود).
        """
        plan = await self.plan_repo.get_by_code(code)
        if plan is not None:
            return plan
        plan = Plan(
            code=code,
            name=name,
            description="پلن پیش‌فرض تخصیص‌یافته در زمان onboarding",
            is_active=True,
        )
        await self.plan_repo.add(plan)
        await self.session.commit()
        await self.session.refresh(plan)
        return plan

    # ---- Features ----
    async def create_feature(self, data: FeatureCreate) -> Feature:
        if await self.feature_repo.key_exists(data.feature_key):
            raise ConflictError(
                f"feature_key تکراری است: {data.feature_key}",
                error_code="feature_key_taken",
            )
        feature = Feature(
            feature_key=data.feature_key,
            name=data.name,
            description=data.description,
            service_key=data.service_key,
            is_active=data.is_active,
        )
        await self.feature_repo.add(feature)
        await self.session.commit()
        await self.session.refresh(feature)
        return feature

    async def list_features(self, *, offset: int, limit: int) -> tuple[Sequence[Feature], int]:
        items = await self.feature_repo.list(offset=offset, limit=limit)
        total = await self.feature_repo.count()
        return items, total

    # ---- Plan <-> Feature ----
    async def attach_feature(self, plan_id: UUID, data: PlanFeatureCreate) -> PlanFeature:
        plan = await self.plan_repo.get(plan_id)
        if plan is None:
            raise NotFoundError(f"پلن یافت نشد: {plan_id}")
        feature = await self.feature_repo.get(data.feature_id)
        if feature is None:
            raise NotFoundError(f"قابلیت یافت نشد: {data.feature_id}")

        existing = await self.plan_feature_repo.get(plan_id, data.feature_id)
        if existing is not None:
            # به‌روزرسانی تنظیمات موجود به‌جای ایجاد رکورد تکراری.
            existing.limit_value = data.limit_value
            existing.limit_period = data.limit_period
            existing.is_enabled = data.is_enabled
            await self.session.commit()
            await self.session.refresh(existing)
            return existing

        plan_feature = PlanFeature(
            plan_id=plan_id,
            feature_id=data.feature_id,
            limit_value=data.limit_value,
            limit_period=data.limit_period,
            is_enabled=data.is_enabled,
        )
        await self.plan_feature_repo.add(plan_feature)
        await self.session.commit()
        await self.session.refresh(plan_feature)
        return plan_feature
