"""Architecture / boundary / documentation validation tests."""
from __future__ import annotations

import ast
from pathlib import Path

from app.models.foundation import (
    ContactSource,
    ManualContact,
    Message,
    MessageTemplate,
    OTPChallenge,
    ProviderConfig,
    QueueItem,
    SenderNumber,
)


FORBIDDEN_IMPORT_PREFIXES = (
    "backend.services.crm",
    "backend.services.accounting",
    "backend.services.loyalty",
    "backend.services.restaurant",
    "backend.services.marketplace",
    "backend.core_service",
    "app.services.crm",
    "app.models.crm",
)


def test_all_models_have_tenant_id():
    from app.core.database import Base
    import app.models  # noqa: F401

    skip = {"alembic_version"}
    for table in Base.metadata.tables.values():
        if table.name in skip:
            continue
        assert "tenant_id" in table.columns, f"{table.name} missing tenant_id"


def test_core_aggregates_independent():
    for model in (
        ProviderConfig,
        SenderNumber,
        MessageTemplate,
        ManualContact,
        ContactSource,
        Message,
        QueueItem,
        OTPChallenge,
    ):
        assert hasattr(model, "tenant_id")
        assert hasattr(model, "id")
        assert not model.__mapper__.relationships


def test_permissions_defined():
    from app.permissions.definitions import ALL_PERMISSIONS, PERMISSION_PREFIXES

    assert "communication.view" in ALL_PERMISSIONS
    assert "communication.providers.manage" in ALL_PERMISSIONS
    assert "communication.templates.approve" in ALL_PERMISSIONS
    assert "communication.messages.send" in ALL_PERMISSIONS
    assert "communication.otp.request" in ALL_PERMISSIONS
    assert "communication.queue.manage" in ALL_PERMISSIONS
    for prefix in PERMISSION_PREFIXES:
        assert any(p.startswith(prefix.rstrip(".")) or p.startswith(prefix) for p in ALL_PERMISSIONS)


def test_events_defined():
    from app.events.types import CommunicationEventType

    assert CommunicationEventType.MESSAGE_SENT.value == "communication.message.sent"
    assert CommunicationEventType.MESSAGE_DELIVERED.value == "communication.message.delivered"
    assert CommunicationEventType.PROVIDER_FAILOVER.value == "communication.provider.failover"
    assert CommunicationEventType.OTP_VERIFIED.value == "communication.otp.verified"
    assert CommunicationEventType.QUEUE_DEAD_LETTER.value == "communication.queue.dead_letter"


def test_sports_contact_source_type():
    from app.models.types import ContactSourceType

    assert ContactSourceType.SPORTS.value == "sports"


def test_permission_enforcement_module_exists():
    from app.api.permissions import require_permissions, user_has_permission

    assert callable(require_permissions)
    assert callable(user_has_permission)


def test_provider_framework_registered():
    from app.providers import list_registered_provider_kinds, supported_channels

    kinds = list_registered_provider_kinds()
    assert "mock" in kinds
    assert "payamak" in kinds
    channels = {c["channel"]: c for c in supported_channels()}
    assert channels["sms"]["status"] == "active"
    assert "email" in channels
    assert "whatsapp" in channels
    assert "telegram" in channels
    assert "rubika" in channels


def test_no_forbidden_business_module_imports():
    root = Path(__file__).resolve().parents[1]
    violations: list[str] = []
    for path in root.rglob("*.py"):
        if "tests" in path.parts:
            continue
        tree = ast.parse(path.read_text(encoding="utf-8"), filename=str(path))
        for node in ast.walk(tree):
            if isinstance(node, ast.Import):
                for alias in node.names:
                    for forbidden in FORBIDDEN_IMPORT_PREFIXES:
                        if alias.name.startswith(forbidden) or forbidden in alias.name:
                            violations.append(f"{path}: import {alias.name}")
            elif isinstance(node, ast.ImportFrom) and node.module:
                for forbidden in FORBIDDEN_IMPORT_PREFIXES:
                    if node.module.startswith(forbidden) or forbidden in node.module:
                        violations.append(f"{path}: from {node.module}")
    assert violations == []


def test_folder_structure():
    root = Path(__file__).resolve().parents[2]
    required = [
        "app/models",
        "app/repositories",
        "app/services",
        "app/validators",
        "app/schemas",
        "app/events",
        "app/permissions",
        "app/providers",
        "app/api/v1",
        "app/tests",
        "alembic/versions",
        "README.md",
    ]
    for rel in required:
        assert (root / rel).exists(), f"missing {rel}"


def test_phase_documentation_exists():
    docs = Path(__file__).resolve().parents[4] / "docs"
    # repo root = parents[4] from app/tests -> communication -> services -> backend -> root
    # Path: .../communication/app/tests -> parents[0]=tests, [1]=app, [2]=communication, [3]=services, [4]=backend, [5]=root
    docs = Path(__file__).resolve().parents[5] / "docs"
    assert (docs / "communication-phase-8.md").exists()
    progress = (docs / "progress.md").read_text(encoding="utf-8")
    assert "Phase 8" in progress
    registry = (docs / "module-registry.md").read_text(encoding="utf-8")
    assert "communication" in registry.lower()
