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

import ast
import re
from pathlib import Path

from app.models import foundation as models


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

FOUNDATION_MODELS = [
    models.SportsCenter,
    models.Branch,
    models.Sport,
    models.MembershipType,
    models.Membership,
    models.Coach,
    models.SportsRole,
    models.SportsPermission,
    models.Facility,
    models.Court,
    models.Room,
    models.LockerRoom,
    models.Locker,
    models.DeviceProvider,
    models.Device,
    models.AttendanceGateway,
    models.SportsConfiguration,
    models.SportsEvent,
    models.SportsSetting,
    models.SportsAuditLog,
]


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


def test_no_sport_specific_hardcoded_engines():
    forbidden = {"sport_engine", "hardcoded_sport_rules", "vendor_sdk"}
    for model in FOUNDATION_MODELS:
        columns = set(model.__table__.columns.keys())
        assert forbidden.isdisjoint(columns), model.__tablename__


def test_catalog_models_have_no_relationships():
    from app.models import catalog as catalog_models

    models = [
        catalog_models.SportCategory,
        catalog_models.AgeGroup,
        catalog_models.PricingModel,
        catalog_models.MembershipPackage,
        catalog_models.MembershipPlan,
        catalog_models.MembershipRule,
        catalog_models.RenewalPolicy,
        catalog_models.FreezingRule,
        catalog_models.ExpirationPolicy,
    ]
    assert len(models) == 9
    for model in models:
        assert hasattr(model, "tenant_id")
        assert not model.__mapper__.relationships


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

    assert "sports_center.membership_plans.manage" in ALL_PERMISSIONS
    assert "sports_center.pricing_models.view" in ALL_PERMISSIONS
    assert "sports_center.freezing_rules.manage" in ALL_PERMISSIONS
    assert "sports_center.audit.view" in ALL_PERMISSIONS
    assert "sports_center.view" in ALL_PERMISSIONS
    assert "sports_center.sports_centers.create" in ALL_PERMISSIONS
    assert "sports_center.memberships.create" 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_member_models_have_no_relationships():
    from app.models import members as member_models

    models = [
        member_models.Member,
        member_models.FamilyMember,
        member_models.EmergencyContact,
        member_models.MedicalInformation,
        member_models.MembershipCard,
        member_models.DigitalMembership,
        member_models.Waiver,
        member_models.MemberDocument,
    ]
    assert len(models) == 8
    for model in models:
        assert hasattr(model, "tenant_id")
        assert not model.__mapper__.relationships


def test_member_permissions_defined():
    from app.permissions.definitions import ALL_PERMISSIONS

    assert "sports_center.members.create" in ALL_PERMISSIONS
    assert "sports_center.members.manage" in ALL_PERMISSIONS
    assert "sports_center.family_members.manage" in ALL_PERMISSIONS
    assert "sports_center.medical.view" in ALL_PERMISSIONS
    assert "sports_center.membership_cards.manage" in ALL_PERMISSIONS
    assert "sports_center.waivers.manage" in ALL_PERMISSIONS
    assert "sports_center.memberships.assign" in ALL_PERMISSIONS
    assert "sports_center.memberships.status" in ALL_PERMISSIONS


def test_staff_permissions_defined():
    from app.permissions.definitions import ALL_PERMISSIONS

    assert "sports_center.staff_certificates.manage" in ALL_PERMISSIONS
    assert "sports_center.staff_assignments.manage" in ALL_PERMISSIONS


def test_booking_permissions_defined():
    from app.permissions.definitions import ALL_PERMISSIONS

    assert "sports_center.bookings.manage" in ALL_PERMISSIONS
    assert "sports_center.sessions.manage" in ALL_PERMISSIONS


def test_attendance_permissions_defined():
    from app.permissions.definitions import ALL_PERMISSIONS

    assert "sports_center.attendance.manage" in ALL_PERMISSIONS
    assert "sports_center.check_in.manage" in ALL_PERMISSIONS


def test_training_permissions_defined():
    from app.permissions.definitions import ALL_PERMISSIONS

    assert "sports_center.training_programs.manage" in ALL_PERMISSIONS
    assert "sports_center.workout_plans.manage" in ALL_PERMISSIONS


def test_competition_permissions_defined():
    from app.permissions.definitions import ALL_PERMISSIONS

    assert "sports_center.competitions.manage" in ALL_PERMISSIONS
    assert "sports_center.competition_matches.manage" in ALL_PERMISSIONS


def test_competition_events_defined():
    from app.events.types import SportsCenterEventType

    assert SportsCenterEventType.COMPETITION_CREATED.value == "sports_center.competition.created"
    assert SportsCenterEventType.COMPETITION_RESULT_RECORDED.value == "sports_center.competition.result.recorded"


def test_competition_models_have_no_relationships():
    from app.models import competitions as competition_models

    models = [
        competition_models.Competition,
        competition_models.CompetitionTeam,
        competition_models.CompetitionGroup,
        competition_models.CompetitionRegistration,
        competition_models.CompetitionMatch,
        competition_models.CompetitionResult,
        competition_models.CompetitionRanking,
        competition_models.CompetitionMedal,
        competition_models.CompetitionJudge,
        competition_models.CompetitionCertificate,
    ]
    for model in models:
        assert hasattr(model, "tenant_id")
        assert not model.__mapper__.relationships


def test_phase_events_defined():
    from app.events.types import SportsCenterEventType

    assert SportsCenterEventType.STAFF_CERTIFICATE_CREATED.value == "sports_center.staff_certificate.created"
    assert SportsCenterEventType.BOOKING_CREATED.value == "sports_center.booking.created"
    assert SportsCenterEventType.CHECK_IN_COMPLETED.value == "sports_center.check_in.completed"
    assert SportsCenterEventType.TRAINING_PROGRAM_CREATED.value == "sports_center.training_program.created"


def test_staff_models_have_no_relationships():
    from app.models import staff as staff_models

    models = [
        staff_models.StaffCertificate,
        staff_models.StaffSkill,
        staff_models.StaffWorkingHours,
        staff_models.StaffAvailability,
        staff_models.StaffAssignment,
    ]
    for model in models:
        assert hasattr(model, "tenant_id")
        assert not model.__mapper__.relationships


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

    assert SportsCenterEventType.MEMBER_CREATED.value == "sports_center.member.created"
    assert SportsCenterEventType.MEMBERSHIP_CREATED.value == "sports_center.membership.created"
    assert SportsCenterEventType.COACH_CREATED.value == "sports_center.coach.created"
    assert SportsCenterEventType.FACILITY_CREATED.value == "sports_center.facility.created"
    assert SportsCenterEventType.DEVICE_CONNECTED.value == "sports_center.device.connected"
    assert SportsCenterEventType.DEVICE_DISCONNECTED.value == "sports_center.device.disconnected"
    assert (
        SportsCenterEventType.ATTENDANCE_PROVIDER_CHANGED.value
        == "sports_center.attendance.provider.changed"
    )
    assert SportsCenterEventType.LOCKER_ASSIGNED.value == "sports_center.locker.assigned"
    assert SportsCenterEventType.LOCKER_RELEASED.value == "sports_center.locker.released"
    assert SportsCenterEventType.MEMBER_UPDATED.value == "sports_center.member.updated"
    assert SportsCenterEventType.MEMBERSHIP_ASSIGNED.value == "sports_center.membership.assigned"
    assert SportsCenterEventType.MEMBERSHIP_FROZEN.value == "sports_center.membership.frozen"
    assert SportsCenterEventType.WAIVER_SIGNED.value == "sports_center.waiver.signed"
    assert SportsCenterEventType.MEMBERSHIP_PLAN_CREATED.value == "sports_center.membership_plan.created"
    assert SportsCenterEventType.PRICING_MODEL_CREATED.value == "sports_center.pricing_model.created"


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

    assert AccountingProvider is not None
    assert CRMProvider is not None
    assert LoyaltyProvider is not None
    assert CommunicationProvider 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_connector_framework_exists():
    from app.connectors import (
        AttendanceDeviceConnector,
        BarcodeConnector,
        ConnectorCapability,
        ConnectorRegistry,
        DeviceConnector,
        DoorControllerConnector,
        FaceRecognitionConnector,
        FingerprintConnector,
        PaymentTerminalConnector,
        QRConnector,
        RFIDConnector,
        TurnstileConnector,
        get_connector_registry,
    )

    assert ConnectorCapability.QR.value == "qr"
    assert ConnectorCapability.RFID.value == "rfid"
    assert ConnectorCapability.FACE_RECOGNITION.value == "face_recognition"
    assert DeviceConnector is not None
    assert QRConnector is not None
    assert RFIDConnector is not None
    assert BarcodeConnector is not None
    assert FingerprintConnector is not None
    assert FaceRecognitionConnector is not None
    assert TurnstileConnector is not None
    assert DoorControllerConnector is not None
    assert PaymentTerminalConnector is not None
    assert AttendanceDeviceConnector is not None
    assert isinstance(get_connector_registry(), ConnectorRegistry)


def test_no_forbidden_service_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_api_ownership_is_sports_center_only():
    from app.api.v1 import api_router

    prefixes = {getattr(r, "path", "") for r in api_router.routes}
    joined = " ".join(sorted(prefixes))
    assert "/sports-centers" in joined or any("/sports-centers" in p for p in prefixes)
    assert "/memberships" in joined or any("/memberships" in p for p in prefixes)
    forbidden = ("accounting", "crm", "loyalty", "notification", "automation")
    for name in forbidden:
        assert not re.search(rf"(^|/){re.escape(name)}(/|$|\s)", joined), name


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/connectors",
        "app/policies",
        "app/specifications",
        "app/commands",
        "app/queries",
        "app/api/v1",
        "app/tests",
        "alembic/versions",
        "README.md",
    ]
    for rel in required:
        assert (root / rel).exists(), f"missing {rel}"
