"""Real startup regressions for the v1.5 weekly-authority migration."""

from __future__ import annotations

from copy import deepcopy
import json
import os
from pathlib import Path
import sys
from typing import cast

import pytest
from pydantic import JsonValue, TypeAdapter
import yaml

sys.path.insert(0, str(Path(__file__).parents[1] / "dualcoach/profile"))

from checkin_cli.customer_coaching import load_customer_registry
from checkin_cli.customer_schedule import initialize_schedule_delivery_fence
from checkin_cli.store import CanonicalEventTransaction
from gateway.config import PlatformConfig
from gateway.platforms.telegram import TelegramAdapter
from gateway.platforms.nutrition_weekly_reminder_authority import (
    WeeklyReminderOwnerError,
)
from gateway.platforms.nutrition_weekly_reminder_bootstrap import (
    WeeklyReminderStartupAuthorityIncident,
    load_registered_weekly_reminder_customers,
)
from gateway.platforms.nutrition_weekly_reminder_owner_factory import (
    WeeklyReminderProductionInput,
    build_weekly_reminder_authority_owner,
)
from scripts.nutricoach_v140_authority_fixture import registry_payload
from scripts.nutricoach_v150_host_operations import apply_migrations, capacity_after
from scripts.nutricoach_v150_live_models import CANDIDATE_DIGEST
from scripts.nutricoach_v150_runtime_ops import run_installed_weekly_startup_smoke
from scripts.nutricoach_v150_sealed_target import HostPaths
from scripts.nutricoach_v150_weekly_authority import weekly_authority_path

_OBJECT = TypeAdapter(dict[str, JsonValue])


def _mapping(value: JsonValue | None) -> dict[str, JsonValue]:
    assert isinstance(value, dict)
    return value


def _document(path: Path) -> dict[str, JsonValue]:
    raw = cast(object, yaml.safe_load(path.read_text(encoding="utf-8")))
    return _OBJECT.validate_python(raw)


def _paths(root: Path) -> HostPaths:
    profile = root / "profile"
    registry = profile / "customers/registry.json"
    registry.parent.mkdir(parents=True)
    _ = registry.write_text(json.dumps(registry_payload()) + "\n", encoding="utf-8")
    registry.chmod(0o600)
    config = profile / "config.yaml"
    _ = config.write_text(
        "".join((
            "platforms:\n",
            "  telegram:\n",
            "    extra:\n",
            "      nutrition_coaching:\n",
            "        operator_review:\n",
            "          user_id: '100'\n",
            "          chat_id: '200'\n",
            "          topic_id: 59\n",
        )),
        encoding="utf-8",
    )
    runtime = load_customer_registry(registry, profile).customers[0]
    for directory in (
        runtime.customer_root,
        runtime.wizard_root,
        runtime.nutrition_plans_root,
    ):
        directory.mkdir(parents=True, mode=0o700)
    transaction = CanonicalEventTransaction.for_customer_runtime(runtime)
    for path in (transaction.events_path, transaction.sequence_path):
        path.touch(mode=0o600)
        path.chmod(0o600)
    _ = transaction.read_snapshot()
    data = profile / "data"
    data.mkdir(mode=0o700, exist_ok=True)
    ledger = data / "scheduled-deliveries.jsonl"
    ledger.touch(mode=0o600)
    ledger.chmod(0o600)
    (data / "customer-schedule-claims").mkdir(mode=0o700)
    _ = initialize_schedule_delivery_fence(profile)
    current = profile / ".strict-runtime/current/venv"
    return HostPaths(
        profile,
        registry,
        config,
        root / "gateway.service",
        root / "authority.conf",
        current,
        profile / ".strict-runtime/successor/venv",
        root / "execution",
        root / "ledger",
    )


def _platform_config(paths: HostPaths) -> PlatformConfig:
    document = _document(paths.config)
    platforms = _mapping(document.get("platforms"))
    telegram = _mapping(platforms.get("telegram"))
    return PlatformConfig(
        enabled=True,
        token="disposable",
        extra=_mapping(telegram.get("extra")),
    )


def _migrate(root: Path) -> HostPaths:
    paths = _paths(root)
    apply_migrations(paths, capacity_after(paths.registry))
    return paths


def _add_disabled_draft(paths: HostPaths) -> None:
    document = _OBJECT.validate_json(paths.registry.read_bytes())
    customers = document.get("customers")
    assert isinstance(customers, list)
    source = customers[0]
    assert isinstance(source, dict)
    draft = deepcopy(source)
    draft["customer_key"] = "draft_002"
    draft["display_name"] = "disabled draft"
    draft["enabled"] = False
    consent = _mapping(draft.get("ai_processing_consent"))
    consent["granted"] = False
    telegram = _mapping(draft.get("telegram"))
    telegram.update({"user_id": "301", "chat_id": "301", "topic_id": "42"})
    customers.append(draft)
    _ = paths.registry.write_text(json.dumps(document) + "\n", encoding="utf-8")
    paths.registry.chmod(0o600)


def test_migration_preserves_disabled_draft_and_authorizes_enabled_only(
    tmp_path: Path,
) -> None:
    paths = _paths(tmp_path)
    _add_disabled_draft(paths)

    apply_migrations(paths, capacity_after(paths.registry))

    adapter = TelegramAdapter(_platform_config(paths))
    try:
        assert tuple(
            customer.runtime.spec.customer_key
            for customer in adapter.weekly_reminder_customers
        ) == ("client_001",)
        registry = load_customer_registry(paths.registry, paths.profile)
        assert tuple(item.spec.enabled for item in registry.customers) == (
            True,
            False,
        )
        disabled = registry.customers[1]
        assert not disabled.spec.ai_processing_consent.granted
        assert not disabled.customer_root.exists()
    finally:
        adapter.close_weekly_reminder_capabilities()


def test_installed_startup_smoke_constructs_and_ticks_enabled_owner(
    tmp_path: Path,
) -> None:
    paths = _paths(tmp_path)
    _add_disabled_draft(paths)
    apply_migrations(paths, capacity_after(paths.registry))

    run_installed_weekly_startup_smoke(
        Path(sys.executable).resolve().parent.parent,
        paths.profile,
        paths.config,
        source_dependencies=True,
    )

    assert CANDIDATE_DIGEST in paths.config.read_text(encoding="utf-8")


def test_migration_builds_real_startup_authority_without_provider_calls(
    tmp_path: Path,
) -> None:
    paths = _migrate(tmp_path)

    adapter = TelegramAdapter(_platform_config(paths))
    try:
        assert tuple(
            customer.runtime.spec.customer_key
            for customer in adapter.weekly_reminder_customers
        ) == ("client_001",)
    finally:
        adapter.close_weekly_reminder_capabilities()

    config = paths.config.read_text(encoding="utf-8")
    assert "capacity: 5" in config
    assert "channel_inbox" not in config


def test_migration_rebinds_telegram_candidate_package_identity(
    tmp_path: Path,
) -> None:
    paths = _paths(tmp_path)
    document = _document(paths.config)
    platforms = _mapping(document.get("platforms"))
    telegram = _mapping(platforms.get("telegram"))
    extra = _mapping(telegram.get("extra"))
    extra["production_preflight"] = {"candidate_package_identity": "a" * 64}
    _ = paths.config.write_text(
        yaml.safe_dump(document, sort_keys=False), encoding="utf-8"
    )

    apply_migrations(paths, capacity_after(paths.registry))

    migrated = _document(paths.config)
    migrated_platforms = _mapping(migrated.get("platforms"))
    migrated_telegram = _mapping(migrated_platforms.get("telegram"))
    migrated_extra = _mapping(migrated_telegram.get("extra"))
    production = _mapping(migrated_extra.get("production_preflight"))
    assert production["candidate_package_identity"] == CANDIDATE_DIGEST


def test_successor_uses_new_weekly_authority_root(
    tmp_path: Path,
) -> None:
    # Given
    paths = _migrate(tmp_path)
    predecessor = paths.profile / "data/weekly-operations-authority"
    successor_candidate = "b" * 64

    # When
    successor = weekly_authority_path(paths, successor_candidate)

    # Then
    assert predecessor.is_dir()
    assert successor == (
        paths.profile / f"data/weekly-operations-authority-{successor_candidate[:16]}"
    )
    assert not successor.exists()


def test_migration_authority_denies_archived_or_tampered_registry(
    tmp_path: Path,
) -> None:
    paths = _migrate(tmp_path)
    archived = tmp_path / "archived-registry.json"
    _ = archived.write_bytes(paths.registry.read_bytes())
    archived.chmod(0o600)
    os.replace(archived, paths.registry)

    with pytest.raises(WeeklyReminderStartupAuthorityIncident):
        _ = load_registered_weekly_reminder_customers(_platform_config(paths))


@pytest.mark.parametrize("field", ["owner", "customer"])
def test_migration_authority_denies_owner_or_customer_rebinding(
    tmp_path: Path,
    field: str,
) -> None:
    paths = _migrate(tmp_path)
    document = _document(paths.config)
    platforms = _mapping(document.get("platforms"))
    telegram = _mapping(platforms.get("telegram"))
    extra = _mapping(telegram.get("extra"))
    nutrition = _mapping(extra.get("nutrition_coaching"))
    receipt = _mapping(nutrition.get("weekly_operations_authority"))
    if field == "owner":
        _mapping(receipt.get("owner"))["user_id"] = "not-the-owner"
    else:
        receipt["enabled_customer_keys"] = ["not-the-customer"]
    _ = paths.config.write_text(
        yaml.safe_dump(document, sort_keys=False), encoding="utf-8"
    )

    config = _platform_config(paths)
    if field == "customer":
        with pytest.raises(WeeklyReminderStartupAuthorityIncident):
            _ = load_registered_weekly_reminder_customers(config)
        return
    registry = load_customer_registry(paths.registry, paths.profile)
    with load_registered_weekly_reminder_customers(config) as owned:
        production = WeeklyReminderProductionInput(
            config.extra,
            CANDIDATE_DIGEST,
            registry,
            paths.registry,
            owned.customers,
        )
        with pytest.raises(WeeklyReminderOwnerError):
            _ = build_weekly_reminder_authority_owner(production)
