"""Disabled canonical registration for owner/customer-only Telegram bootstrap."""

from __future__ import annotations

import importlib
import os
from dataclasses import dataclass
from datetime import date, time
from pathlib import Path
from types import ModuleType
from typing import cast

from gateway.platforms.dualcoach_profile_package import (
    DualCoachProfilePackage,
    ProfilePackageResolutionError,
)
from gateway.platforms.telegram_customer_bootstrap import (
    BootstrapError,
    BootstrapState,
    Role,
    RoomBootstrapSession,
    RoomBootstrapStore,
)
from gateway.platforms.telegram_nutrition_addresses import (
    parse_customer_private_delivery_address,
)


class RegistrationConflict(BootstrapError):
    code = "CONFLICT"


@dataclass(frozen=True, slots=True)
class CustomerConsentRoute:
    user_id: str
    chat_id: str
    topic_id: str

    @property
    def key(self) -> tuple[str, str, str]:
        return self.user_id, self.chat_id, self.topic_id


@dataclass(frozen=True, slots=True)
class DisabledRegistrationReceipt:
    session: RoomBootstrapSession
    customer_key: str
    consent_route: CustomerConsentRoute
    reconciled: bool


class TelegramCustomerBootstrapRegistration:
    """Register one claimed customer DM as disabled; grant no activation."""

    def __init__(
        self,
        profile_root: Path | str,
        store: RoomBootstrapStore,
        *,
        package_root: Path | str | None = None,
    ) -> None:
        root = Path(profile_root)
        if root.is_symlink() or not root.exists() or not root.is_dir():
            raise BootstrapError("registration profile root is unavailable")
        if not isinstance(store, RoomBootstrapStore):
            raise TypeError("registration requires the customer bootstrap store")
        self.profile_root = root.resolve()
        configured_package = package_root or os.environ.get(
            "DUALCOACH_PROFILE_PACKAGE"
        )
        try:
            self.package = DualCoachProfilePackage.from_root(
                configured_package
                or self.profile_root / "workspace" / "checkin_cli"
            )
        except ProfilePackageResolutionError as exc:
            raise BootstrapError(str(exc)) from exc
        self.store = store

    def handoff_rehearsal_customer(
        self,
        session: RoomBootstrapSession,
    ) -> DisabledRegistrationReceipt:
        live = self._live_session(session)
        customer = live.role_claim(Role.CUSTOMER)
        draft = live.customer_draft
        if (
            live.state is not BootstrapState.REGISTERING
            or customer is None
            or len(live.role_claims) != 1
            or customer.user_id != customer.chat_id
            or customer.topic_id != "0"
            or customer.user_id != draft.customer_user_id
            or customer.user_id == live.owner_id
        ):
            raise RegistrationConflict(
                "customer registration authority is unavailable"
            )
        return self._handoff_private_customer(live)

    def _handoff_private_customer(
        self,
        session: RoomBootstrapSession,
    ) -> DisabledRegistrationReceipt:
        admin, coaching = self._profile_modules()
        registry_path = admin._resolve_registry_path(self.profile_root)
        with admin.profile_authority_lock(self.profile_root):
            live = self._live_session(session)
            if live.state is not BootstrapState.REGISTERING:
                raise RegistrationConflict("registration generation is stale")
            customer = live.role_claim(Role.CUSTOMER)
            if customer is None:
                raise RegistrationConflict("customer route is unavailable")
            payload = live.customer_draft.to_dict()
            payload.update(
                {
                    "user_id": customer.user_id,
                    "chat_id": customer.chat_id,
                    "topic_id": customer.topic_id,
                }
            )
            document = admin._read(registry_path)
            if self._owner_key(document.owner)[0] != live.owner_id:
                raise RegistrationConflict("canonical owner authority changed")
            existing = self._customer(document, live.customer_key)
            reconciled = existing is not None
            if existing is None:
                admin.register_customer(
                    registry_path,
                    self._customer_draft(admin, coaching, payload),
                )
            persisted_document = admin._read(registry_path)
            persisted = self._customer(persisted_document, live.customer_key)
            if (
                persisted is None
                or not self._is_exact_disabled_row(persisted, payload)
            ):
                raise RegistrationConflict("disabled registration mismatch")
            completed = self.store.transition(
                live.session_id,
                expected_generation=live.generation,
                target=BootstrapState.AWAITING_CONSENT,
            )
            completed = self.store.bind_private_owner(
                completed.session_id,
                owner_id=self._owner_key(persisted_document.owner)[0],
                expected_generation=completed.generation,
            )
            return DisabledRegistrationReceipt(
                session=completed,
                customer_key=completed.customer_key,
                consent_route=CustomerConsentRoute(
                    customer.user_id,
                    customer.chat_id,
                    customer.topic_id,
                ),
                reconciled=reconciled,
            )

    def _profile_modules(self) -> tuple[ModuleType, ModuleType]:
        try:
            admin = self.package.resolve(
                "checkin_cli.customer_admin",
                importer=importlib.import_module,
            )
            coaching = self.package.resolve(
                "checkin_cli.customer_coaching",
                importer=importlib.import_module,
            )
        except ProfilePackageResolutionError as exc:
            raise BootstrapError(str(exc)) from exc
        return admin, coaching

    def _live_session(
        self,
        supplied: RoomBootstrapSession,
    ) -> RoomBootstrapSession:
        try:
            live = self.store.get(supplied.session_id)
        except BootstrapError as exc:
            raise RegistrationConflict("bootstrap session is unavailable") from exc
        if live != supplied:
            raise RegistrationConflict("bootstrap session evidence is stale")
        return live

    @staticmethod
    def _customer(document: object, customer_key: str) -> object | None:
        return next(
            (
                item
                for item in getattr(document, "customers", ())
                if getattr(item, "customer_key", None) == customer_key
            ),
            None,
        )

    @staticmethod
    def _owner_key(owner: object) -> tuple[str, str, str]:
        return (
            str(getattr(owner, "user_id", "")),
            str(getattr(owner, "chat_id", "")),
            str(getattr(owner, "topic_id", "")),
        )

    @staticmethod
    def _customer_draft(
        admin: ModuleType,
        coaching: ModuleType,
        payload: dict[str, object],
    ) -> object:
        del coaching
        values = dict(payload)
        values.pop("customer_user_id", None)
        values["starts_on"] = date.fromisoformat(str(values["starts_on"]))
        values["daily_time"] = time.fromisoformat(str(values["daily_time"]))
        values["meals"] = tuple(cast(list[object], values["meals"]))
        for name in (
            "dietary_restrictions",
            "allergies",
            "food_preferences",
            "supplements",
        ):
            values[name] = tuple(cast(list[object], values[name]))
        customer = parse_customer_private_delivery_address(
            {
                "user_id": values["user_id"],
                "chat_id": values["chat_id"],
                "topic_id": values["topic_id"],
            }
        )
        values.update(
            {
                "user_id": str(customer.user_id),
                "chat_id": str(customer.chat_id),
                "topic_id": str(customer.topic_id),
            }
        )
        return admin.CustomerDraft(**values)

    @staticmethod
    def _is_exact_disabled_row(
        row: object,
        payload: dict[str, object],
    ) -> bool:
        telegram = getattr(row, "telegram", None)
        schedule = getattr(row, "schedule", None)
        profile = getattr(row, "profile", None)
        plan = getattr(row, "plan", None)
        weeks = tuple(getattr(plan, "weeks", ()))
        expected_meals = tuple(cast(list[object], payload["meals"]))
        expected_restrictions = tuple(
            cast(list[object], payload["dietary_restrictions"])
        )
        expected_allergies = tuple(cast(list[object], payload["allergies"]))
        expected_preferences = tuple(
            cast(list[object], payload["food_preferences"])
        )
        expected_supplements = tuple(cast(list[object], payload["supplements"]))
        return bool(
            getattr(row, "enabled", None) is False
            and getattr(row, "customer_key", None) == payload["customer_key"]
            and getattr(row, "display_name", None) == payload["display_name"]
            and telegram is not None
            and telegram.key
            == (payload["user_id"], payload["chat_id"], payload["topic_id"])
            and getattr(schedule, "daily_time", None)
            == time.fromisoformat(str(payload["daily_time"]))
            and getattr(schedule, "weekly_weekday", None)
            == payload["weekly_weekday"]
            and getattr(schedule, "monthly_day", None) == payload["monthly_day"]
            and getattr(plan, "starts_on", None)
            == date.fromisoformat(str(payload["starts_on"]))
            and len(weeks) == 12
            and all(
                week.week == index
                and week.calories_kcal == payload["calories_kcal"]
                and week.protein_g == payload["protein_g"]
                and tuple(week.meal_structure) == expected_meals
                and week.carbohydrate_g == payload["carbohydrate_g"]
                and week.fat_g == payload["fat_g"]
                and week.water_liters == payload["water_liters"]
                for index, week in enumerate(weeks, 1)
            )
            and getattr(profile, "primary_goal", None) == payload["primary_goal"]
            and tuple(getattr(profile, "dietary_restrictions", ()))
            == expected_restrictions
            and tuple(getattr(profile, "allergies", ())) == expected_allergies
            and tuple(getattr(profile, "food_preferences", ()))
            == expected_preferences
            and tuple(getattr(profile, "supplements", ()))
            == expected_supplements
        )


__all__ = [
    "CustomerConsentRoute",
    "DisabledRegistrationReceipt",
    "RegistrationConflict",
    "TelegramCustomerBootstrapRegistration",
]
