"""Architecture tests — Hospitality module boundary enforcement."""
from __future__ import annotations

import ast
from pathlib import Path

from app.models import analytics as analytics_models
from app.models import catalog as catalog_models
from app.models import connectors as connectors_models
from app.models import foundation as models
from app.models import kitchen as kitchen_models
from app.models import pos_lite as pos_lite_models
from app.models import pos_pro as pos_pro_models
from app.models import qr as qr_models
from app.models import table_service as table_service_models


FORBIDDEN_IMPORT_PREFIXES = (
    "backend.services.accounting",
    "backend.services.crm",
    "backend.services.loyalty",
    "backend.services.communication",
    "backend.services.sports_center",
    "backend.services.automation",
    "backend.services.notification",
    "backend.services.file_storage",
    "backend.services.identity_access",
    "backend.core_service",
)

FOUNDATION_MODELS = [
    models.Venue,
    models.Branch,
    models.DiningArea,
    models.DiningTable,
    models.Menu,
    models.MenuCategory,
    models.MenuItem,
    models.BundleDefinition,
    models.TenantBundle,
    models.FeatureToggle,
    models.HospitalityRole,
    models.HospitalityPermission,
    models.HospitalityConfiguration,
    models.HospitalityEvent,
    models.HospitalitySetting,
    models.HospitalityAuditLog,
]

CATALOG_MODELS = [
    catalog_models.Allergen,
    catalog_models.ModifierGroup,
    catalog_models.ModifierOption,
    catalog_models.MenuItemModifier,
    catalog_models.MenuItemAllergen,
    catalog_models.AvailabilityWindow,
    catalog_models.MenuMediaRef,
    catalog_models.MenuLocalization,
]

QR_MODELS = [
    qr_models.QrCode,
    qr_models.QrMenuSession,
    qr_models.QrOrderingSession,
    qr_models.Cart,
    qr_models.CartLine,
]

TABLE_SERVICE_MODELS = [
    table_service_models.Reservation,
    table_service_models.TableAssignment,
    table_service_models.ServiceRequest,
    table_service_models.WaitlistEntry,
]

POS_LITE_MODELS = [
    pos_lite_models.PosRegister,
    pos_lite_models.PosShift,
    pos_lite_models.PosTicket,
    pos_lite_models.PosTicketLine,
]

POS_PRO_MODELS = [
    pos_pro_models.PosPayment,
    pos_pro_models.PosDiscount,
    pos_pro_models.PosTaxLine,
    pos_pro_models.PosFloorPlan,
    pos_pro_models.PosStation,
]

KITCHEN_MODELS = [
    kitchen_models.KitchenStation,
    kitchen_models.KitchenTicket,
    kitchen_models.KitchenTicketItem,
]

CONNECTORS_MODELS = [
    connectors_models.ConnectorRegistration,
    connectors_models.ConnectorDispatch,
]

ANALYTICS_MODELS = [
    analytics_models.AnalyticsReportDefinition,
    analytics_models.AnalyticsSnapshot,
]


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_foundation_aggregates_are_independent():
    assert len(FOUNDATION_MODELS) == 16
    for model in FOUNDATION_MODELS:
        assert hasattr(model, "tenant_id")
        assert hasattr(model, "id")
        assert not model.__mapper__.relationships


def test_catalog_aggregates_are_independent():
    assert len(CATALOG_MODELS) == 8
    for model in CATALOG_MODELS:
        assert hasattr(model, "tenant_id")
        assert hasattr(model, "id")
        assert not model.__mapper__.relationships


def test_qr_aggregates_are_independent():
    assert len(QR_MODELS) == 5
    for model in QR_MODELS:
        assert hasattr(model, "tenant_id")
        assert hasattr(model, "id")
        assert not model.__mapper__.relationships


def test_table_service_aggregates_are_independent():
    assert len(TABLE_SERVICE_MODELS) == 4
    for model in TABLE_SERVICE_MODELS:
        assert hasattr(model, "tenant_id")
        assert hasattr(model, "id")
        assert not model.__mapper__.relationships


def test_pos_lite_aggregates_are_independent():
    assert len(POS_LITE_MODELS) == 4
    for model in POS_LITE_MODELS:
        assert hasattr(model, "tenant_id")
        assert hasattr(model, "id")
        assert not model.__mapper__.relationships


def test_pos_pro_aggregates_are_independent():
    assert len(POS_PRO_MODELS) == 5
    for model in POS_PRO_MODELS:
        assert hasattr(model, "tenant_id")
        assert hasattr(model, "id")
        assert not model.__mapper__.relationships


def test_kitchen_aggregates_are_independent():
    assert len(KITCHEN_MODELS) == 3
    for model in KITCHEN_MODELS:
        assert hasattr(model, "tenant_id")
        assert hasattr(model, "id")
        assert not model.__mapper__.relationships


def test_connectors_aggregates_are_independent():
    assert len(CONNECTORS_MODELS) == 2
    for model in CONNECTORS_MODELS:
        assert hasattr(model, "tenant_id")
        assert hasattr(model, "id")
        assert not model.__mapper__.relationships


def test_analytics_aggregates_are_independent():
    assert len(ANALYTICS_MODELS) == 2
    for model in ANALYTICS_MODELS:
        assert hasattr(model, "tenant_id")
        assert hasattr(model, "id")
        assert not model.__mapper__.relationships


def test_no_format_specific_hardcoded_engines():
    forbidden = {"pos_engine", "kitchen_engine", "ordering_engine", "vendor_sdk"}
    for model in (
        FOUNDATION_MODELS
        + CATALOG_MODELS
        + QR_MODELS
        + TABLE_SERVICE_MODELS
        + POS_LITE_MODELS
        + POS_PRO_MODELS
        + KITCHEN_MODELS
        + CONNECTORS_MODELS
        + ANALYTICS_MODELS
    ):
        columns = set(model.__table__.columns.keys())
        assert forbidden.isdisjoint(columns), model.__tablename__


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

    assert "hospitality.view" in ALL_PERMISSIONS
    assert "hospitality.venues.create" in ALL_PERMISSIONS
    assert "hospitality.bundles.manage" in ALL_PERMISSIONS
    assert "hospitality.tenant_bundles.manage" in ALL_PERMISSIONS
    assert "hospitality.feature_toggles.manage" in ALL_PERMISSIONS
    assert "hospitality.menus.manage" in ALL_PERMISSIONS
    assert "hospitality.allergens.manage" in ALL_PERMISSIONS
    assert "hospitality.modifier_groups.manage" in ALL_PERMISSIONS
    assert "hospitality.availability_windows.manage" in ALL_PERMISSIONS
    assert "hospitality.menu_media.manage" in ALL_PERMISSIONS
    assert "hospitality.menu_localizations.manage" in ALL_PERMISSIONS
    assert "hospitality.qr_codes.manage" in ALL_PERMISSIONS
    assert "hospitality.qr_menu_sessions.manage" in ALL_PERMISSIONS
    assert "hospitality.qr_ordering_sessions.manage" in ALL_PERMISSIONS
    assert "hospitality.carts.manage" in ALL_PERMISSIONS
    assert "hospitality.reservations.manage" in ALL_PERMISSIONS
    assert "hospitality.table_assignments.manage" in ALL_PERMISSIONS
    assert "hospitality.service_requests.manage" in ALL_PERMISSIONS
    assert "hospitality.waitlist.manage" in ALL_PERMISSIONS
    assert "hospitality.pos_registers.manage" in ALL_PERMISSIONS
    assert "hospitality.pos_shifts.manage" in ALL_PERMISSIONS
    assert "hospitality.pos_tickets.manage" in ALL_PERMISSIONS
    assert "hospitality.pos_payments.manage" in ALL_PERMISSIONS
    assert "hospitality.pos_discounts.manage" in ALL_PERMISSIONS
    assert "hospitality.pos_floor_plans.manage" in ALL_PERMISSIONS
    assert "hospitality.kitchen_stations.manage" in ALL_PERMISSIONS
    assert "hospitality.kitchen_tickets.manage" in ALL_PERMISSIONS
    assert "hospitality.connector_registrations.manage" in ALL_PERMISSIONS
    assert "hospitality.connector_dispatches.manage" in ALL_PERMISSIONS
    assert "hospitality.analytics_reports.manage" in ALL_PERMISSIONS
    assert "hospitality.analytics_snapshots.manage" in ALL_PERMISSIONS
    assert "hospitality.digital_menu.view" in ALL_PERMISSIONS
    assert "hospitality.pos_lite.manage" in ALL_PERMISSIONS
    assert "hospitality.audit.view" 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 HospitalityEventType

    assert HospitalityEventType.VENUE_CREATED.value == "hospitality.venue.created"
    assert HospitalityEventType.MENU_CREATED.value == "hospitality.menu.created"
    assert (
        HospitalityEventType.TENANT_BUNDLE_ACTIVATED.value
        == "hospitality.tenant_bundle.activated"
    )
    assert (
        HospitalityEventType.FEATURE_TOGGLE_UPSERTED.value
        == "hospitality.feature_toggle.upserted"
    )
    assert HospitalityEventType.ALLERGEN_CREATED.value == "hospitality.allergen.created"
    assert (
        HospitalityEventType.MODIFIER_GROUP_CREATED.value
        == "hospitality.modifier_group.created"
    )
    assert (
        HospitalityEventType.MENU_LOCALIZATION_UPSERTED.value
        == "hospitality.menu_localization.upserted"
    )
    assert HospitalityEventType.QR_CODE_CREATED.value == "hospitality.qr_code.created"
    assert (
        HospitalityEventType.QR_ORDERING_SESSION_SUBMITTED.value
        == "hospitality.qr_ordering_session.submitted"
    )
    assert HospitalityEventType.CART_CREATED.value == "hospitality.cart.created"
    assert HospitalityEventType.CART_LINE_ADDED.value == "hospitality.cart_line.added"
    assert (
        HospitalityEventType.RESERVATION_CREATED.value == "hospitality.reservation.created"
    )
    assert (
        HospitalityEventType.RESERVATION_STATUS_CHANGED.value
        == "hospitality.reservation.status_changed"
    )
    assert (
        HospitalityEventType.TABLE_ASSIGNMENT_CREATED.value
        == "hospitality.table_assignment.created"
    )
    assert (
        HospitalityEventType.SERVICE_REQUEST_CREATED.value
        == "hospitality.service_request.created"
    )
    assert (
        HospitalityEventType.WAITLIST_ENTRY_CREATED.value
        == "hospitality.waitlist_entry.created"
    )
    assert HospitalityEventType.POS_REGISTER_CREATED.value == "hospitality.pos_register.created"
    assert HospitalityEventType.POS_SHIFT_OPENED.value == "hospitality.pos_shift.opened"
    assert HospitalityEventType.POS_TICKET_VOIDED.value == "hospitality.pos_ticket.voided"
    assert HospitalityEventType.POS_PAYMENT_RECORDED.value == "hospitality.pos_payment.recorded"
    assert HospitalityEventType.POS_STATION_CREATED.value == "hospitality.pos_station.created"
    assert HospitalityEventType.KITCHEN_TICKET_CREATED.value == "hospitality.kitchen_ticket.created"
    assert (
        HospitalityEventType.KITCHEN_TICKET_ITEM_ADDED.value
        == "hospitality.kitchen_ticket_item.added"
    )
    assert (
        HospitalityEventType.CONNECTOR_REGISTRATION_CREATED.value
        == "hospitality.connector_registration.created"
    )
    assert (
        HospitalityEventType.CONNECTOR_DISPATCH_CREATED.value
        == "hospitality.connector_dispatch.created"
    )
    assert (
        HospitalityEventType.ANALYTICS_SNAPSHOT_REFRESHED.value
        == "hospitality.analytics_snapshot.refreshed"
    )


def test_platform_provider_contracts_exist():
    from app.providers import (
        AIProvider,
        AccountingProvider,
        CRMProvider,
        CommunicationProvider,
        Customer360Provider,
        DeliveryProvider,
        IdentityProvider,
        LoyaltyProvider,
        NotificationProvider,
        StorageProvider,
        WebsiteBuilderProvider,
    )

    assert AccountingProvider is not None
    assert CRMProvider is not None
    assert LoyaltyProvider is not None
    assert CommunicationProvider is not None
    assert DeliveryProvider is not None
    assert WebsiteBuilderProvider is not None
    assert NotificationProvider is not None
    assert StorageProvider is not None
    assert AIProvider is not None
    assert IdentityProvider is not None
    assert Customer360Provider is not None


def test_architecture_folders_exist():
    root = Path(__file__).resolve().parents[1]
    for name in ("policies", "specifications", "commands", "queries", "providers"):
        assert (root / name).is_dir(), name


def test_no_forbidden_cross_service_imports():
    root = Path(__file__).resolve().parents[1]
    for path in root.rglob("*.py"):
        if "tests" in path.parts:
            continue
        tree = ast.parse(path.read_text(encoding="utf-8"))
        for node in ast.walk(tree):
            if isinstance(node, ast.Import):
                for alias in node.names:
                    for prefix in FORBIDDEN_IMPORT_PREFIXES:
                        assert not alias.name.startswith(prefix), path
            if isinstance(node, ast.ImportFrom) and node.module:
                for prefix in FORBIDDEN_IMPORT_PREFIXES:
                    assert not node.module.startswith(prefix), path


def test_api_ownership_no_foreign_domains():
    root = Path(__file__).resolve().parents[1] / "api" / "v1"
    text = "\n".join(p.read_text(encoding="utf-8") for p in root.glob("*.py"))
    for forbidden in ("/accounting", "/crm/", "/loyalty", "/sports-center"):
        assert forbidden not in text
