"""Hospitality foundation repositories — Phase 12.0."""
from __future__ import annotations

from uuid import UUID

from sqlalchemy import select

from app.models.foundation import (
    Branch,
    BundleDefinition,
    DiningArea,
    DiningTable,
    FeatureToggle,
    HospitalityAuditLog,
    HospitalityConfiguration,
    HospitalityEvent,
    HospitalityPermission,
    HospitalityRole,
    HospitalitySetting,
    Menu,
    MenuCategory,
    MenuItem,
    TenantBundle,
    Venue,
)
from app.models.types import BundleKey, BundleStatus
from app.repositories.base import TenantBaseRepository


class VenueRepository(TenantBaseRepository[Venue]):
    model = Venue

    async def get_by_code(self, tenant_id: UUID, code: str) -> Venue | None:
        stmt = select(Venue).where(
            Venue.tenant_id == tenant_id,
            Venue.code == code,
            Venue.is_deleted.is_(False),
        )
        result = await self.session.execute(stmt)
        return result.scalar_one_or_none()


class BranchRepository(TenantBaseRepository[Branch]):
    model = Branch

    async def get_by_code(
        self, tenant_id: UUID, venue_id: UUID, code: str
    ) -> Branch | None:
        stmt = select(Branch).where(
            Branch.tenant_id == tenant_id,
            Branch.venue_id == venue_id,
            Branch.code == code,
            Branch.is_deleted.is_(False),
        )
        result = await self.session.execute(stmt)
        return result.scalar_one_or_none()


class DiningAreaRepository(TenantBaseRepository[DiningArea]):
    model = DiningArea

    async def get_by_code(
        self, tenant_id: UUID, venue_id: UUID, code: str
    ) -> DiningArea | None:
        stmt = select(DiningArea).where(
            DiningArea.tenant_id == tenant_id,
            DiningArea.venue_id == venue_id,
            DiningArea.code == code,
            DiningArea.is_deleted.is_(False),
        )
        result = await self.session.execute(stmt)
        return result.scalar_one_or_none()


class DiningTableRepository(TenantBaseRepository[DiningTable]):
    model = DiningTable

    async def get_by_code(
        self, tenant_id: UUID, venue_id: UUID, code: str
    ) -> DiningTable | None:
        stmt = select(DiningTable).where(
            DiningTable.tenant_id == tenant_id,
            DiningTable.venue_id == venue_id,
            DiningTable.code == code,
            DiningTable.is_deleted.is_(False),
        )
        result = await self.session.execute(stmt)
        return result.scalar_one_or_none()


class MenuRepository(TenantBaseRepository[Menu]):
    model = Menu

    async def get_by_code(
        self, tenant_id: UUID, venue_id: UUID, code: str
    ) -> Menu | None:
        stmt = select(Menu).where(
            Menu.tenant_id == tenant_id,
            Menu.venue_id == venue_id,
            Menu.code == code,
            Menu.is_deleted.is_(False),
        )
        result = await self.session.execute(stmt)
        return result.scalar_one_or_none()


class MenuCategoryRepository(TenantBaseRepository[MenuCategory]):
    model = MenuCategory

    async def get_by_code(
        self, tenant_id: UUID, menu_id: UUID, code: str
    ) -> MenuCategory | None:
        stmt = select(MenuCategory).where(
            MenuCategory.tenant_id == tenant_id,
            MenuCategory.menu_id == menu_id,
            MenuCategory.code == code,
            MenuCategory.is_deleted.is_(False),
        )
        result = await self.session.execute(stmt)
        return result.scalar_one_or_none()


class MenuItemRepository(TenantBaseRepository[MenuItem]):
    model = MenuItem

    async def get_by_code(
        self, tenant_id: UUID, menu_id: UUID, code: str
    ) -> MenuItem | None:
        stmt = select(MenuItem).where(
            MenuItem.tenant_id == tenant_id,
            MenuItem.menu_id == menu_id,
            MenuItem.code == code,
            MenuItem.is_deleted.is_(False),
        )
        result = await self.session.execute(stmt)
        return result.scalar_one_or_none()


class BundleDefinitionRepository(TenantBaseRepository[BundleDefinition]):
    model = BundleDefinition

    async def get_by_key(
        self, tenant_id: UUID, bundle_key: BundleKey
    ) -> BundleDefinition | None:
        stmt = select(BundleDefinition).where(
            BundleDefinition.tenant_id == tenant_id,
            BundleDefinition.bundle_key == bundle_key,
            BundleDefinition.is_deleted.is_(False),
        )
        result = await self.session.execute(stmt)
        return result.scalar_one_or_none()

    async def get_by_code(self, tenant_id: UUID, code: str) -> BundleDefinition | None:
        stmt = select(BundleDefinition).where(
            BundleDefinition.tenant_id == tenant_id,
            BundleDefinition.code == code,
            BundleDefinition.is_deleted.is_(False),
        )
        result = await self.session.execute(stmt)
        return result.scalar_one_or_none()


class TenantBundleRepository(TenantBaseRepository[TenantBundle]):
    model = TenantBundle

    async def get_active(
        self, tenant_id: UUID, bundle_key: BundleKey, venue_id: UUID | None = None
    ) -> TenantBundle | None:
        stmt = select(TenantBundle).where(
            TenantBundle.tenant_id == tenant_id,
            TenantBundle.bundle_key == bundle_key,
            TenantBundle.venue_id == venue_id,
            TenantBundle.status == BundleStatus.ACTIVE,
            TenantBundle.is_deleted.is_(False),
        )
        result = await self.session.execute(stmt)
        return result.scalar_one_or_none()

    async def list_active(self, tenant_id: UUID, venue_id: UUID | None = None):
        clauses = [
            TenantBundle.tenant_id == tenant_id,
            TenantBundle.status == BundleStatus.ACTIVE,
            TenantBundle.is_deleted.is_(False),
        ]
        if venue_id is None:
            clauses.append(TenantBundle.venue_id.is_(None))
        else:
            clauses.append(TenantBundle.venue_id == venue_id)
        stmt = select(TenantBundle).where(*clauses)
        result = await self.session.execute(stmt)
        return result.scalars().all()


class FeatureToggleRepository(TenantBaseRepository[FeatureToggle]):
    model = FeatureToggle

    async def get_by_key(
        self, tenant_id: UUID, feature_key: str, venue_id: UUID | None = None
    ) -> FeatureToggle | None:
        stmt = select(FeatureToggle).where(
            FeatureToggle.tenant_id == tenant_id,
            FeatureToggle.feature_key == feature_key,
            FeatureToggle.venue_id == venue_id,
            FeatureToggle.is_deleted.is_(False),
        )
        result = await self.session.execute(stmt)
        return result.scalar_one_or_none()

    async def list_enabled(self, tenant_id: UUID, venue_id: UUID | None = None):
        clauses = [
            FeatureToggle.tenant_id == tenant_id,
            FeatureToggle.enabled.is_(True),
            FeatureToggle.is_deleted.is_(False),
        ]
        if venue_id is None:
            clauses.append(FeatureToggle.venue_id.is_(None))
        else:
            clauses.append(FeatureToggle.venue_id == venue_id)
        stmt = select(FeatureToggle).where(*clauses)
        result = await self.session.execute(stmt)
        return result.scalars().all()


class HospitalityRoleRepository(TenantBaseRepository[HospitalityRole]):
    model = HospitalityRole

    async def get_by_code(self, tenant_id: UUID, code: str) -> HospitalityRole | None:
        stmt = select(HospitalityRole).where(
            HospitalityRole.tenant_id == tenant_id,
            HospitalityRole.code == code,
            HospitalityRole.is_deleted.is_(False),
        )
        result = await self.session.execute(stmt)
        return result.scalar_one_or_none()


class HospitalityPermissionRepository(TenantBaseRepository[HospitalityPermission]):
    model = HospitalityPermission

    async def get_by_code(
        self, tenant_id: UUID, code: str
    ) -> HospitalityPermission | None:
        stmt = select(HospitalityPermission).where(
            HospitalityPermission.tenant_id == tenant_id,
            HospitalityPermission.code == code,
            HospitalityPermission.is_deleted.is_(False),
        )
        result = await self.session.execute(stmt)
        return result.scalar_one_or_none()


class HospitalityConfigurationRepository(TenantBaseRepository[HospitalityConfiguration]):
    model = HospitalityConfiguration

    async def get_by_code(
        self, tenant_id: UUID, code: str
    ) -> HospitalityConfiguration | None:
        stmt = select(HospitalityConfiguration).where(
            HospitalityConfiguration.tenant_id == tenant_id,
            HospitalityConfiguration.code == code,
            HospitalityConfiguration.is_deleted.is_(False),
        )
        result = await self.session.execute(stmt)
        return result.scalar_one_or_none()


class HospitalityEventRepository(TenantBaseRepository[HospitalityEvent]):
    model = HospitalityEvent


class HospitalitySettingRepository(TenantBaseRepository[HospitalitySetting]):
    model = HospitalitySetting

    async def get_by_key(
        self, tenant_id: UUID, key: str, venue_id: UUID | None = None
    ) -> HospitalitySetting | None:
        stmt = select(HospitalitySetting).where(
            HospitalitySetting.tenant_id == tenant_id,
            HospitalitySetting.key == key,
            HospitalitySetting.venue_id == venue_id,
            HospitalitySetting.is_deleted.is_(False),
        )
        result = await self.session.execute(stmt)
        return result.scalar_one_or_none()


class HospitalityAuditLogRepository(TenantBaseRepository[HospitalityAuditLog]):
    model = HospitalityAuditLog

    async def list_for_entity(
        self, tenant_id: UUID, entity_type: str, entity_id: UUID, *, limit: int = 100
    ):
        stmt = (
            select(HospitalityAuditLog)
            .where(
                HospitalityAuditLog.tenant_id == tenant_id,
                HospitalityAuditLog.entity_type == entity_type,
                HospitalityAuditLog.entity_id == entity_id,
            )
            .limit(limit)
        )
        result = await self.session.execute(stmt)
        return result.scalars().all()
