"""Loyalty foundation repositories — Phase 7.0."""
from __future__ import annotations

from uuid import UUID

from sqlalchemy import select

from app.models.foundation import (
    Campaign,
    LoyaltyAuditLog,
    LoyaltyProgram,
    Member,
    MembershipLifecycleEvent,
    MembershipTier,
    PointAccount,
    Reward,
)
from app.repositories.base import TenantBaseRepository


class LoyaltyProgramRepository(TenantBaseRepository[LoyaltyProgram]):
    model = LoyaltyProgram

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


class MembershipTierRepository(TenantBaseRepository[MembershipTier]):
    model = MembershipTier

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

    async def list_by_program(
        self, tenant_id: UUID, program_id: UUID, *, offset: int = 0, limit: int = 50
    ):
        stmt = (
            select(MembershipTier)
            .where(
                MembershipTier.tenant_id == tenant_id,
                MembershipTier.program_id == program_id,
                MembershipTier.is_deleted.is_(False),
            )
            .order_by(MembershipTier.rank.asc(), MembershipTier.created_at.desc())
            .offset(offset)
            .limit(limit)
        )
        result = await self.session.execute(stmt)
        return result.scalars().all()


class MemberRepository(TenantBaseRepository[Member]):
    model = Member

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

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

    async def count_blocking_for_program(
        self, tenant_id: UUID, program_id: UUID, statuses: list
    ) -> int:
        from sqlalchemy import func

        stmt = (
            select(func.count())
            .select_from(Member)
            .where(
                Member.tenant_id == tenant_id,
                Member.program_id == program_id,
                Member.is_deleted.is_(False),
                Member.status.in_(statuses),
            )
        )
        result = await self.session.execute(stmt)
        return int(result.scalar_one())


class PointAccountRepository(TenantBaseRepository[PointAccount]):
    model = PointAccount

    async def get_by_member(
        self, tenant_id: UUID, member_id: UUID
    ) -> PointAccount | None:
        stmt = select(PointAccount).where(
            PointAccount.tenant_id == tenant_id,
            PointAccount.member_id == member_id,
            PointAccount.is_deleted.is_(False),
        )
        result = await self.session.execute(stmt)
        return result.scalar_one_or_none()

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


class RewardRepository(TenantBaseRepository[Reward]):
    model = Reward

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


class CampaignRepository(TenantBaseRepository[Campaign]):
    model = Campaign

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


class LoyaltyAuditLogRepository(TenantBaseRepository[LoyaltyAuditLog]):
    model = LoyaltyAuditLog

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


class MembershipLifecycleEventRepository(
    TenantBaseRepository[MembershipLifecycleEvent]
):
    model = MembershipLifecycleEvent

    async def list_for_member(
        self, tenant_id: UUID, member_id: UUID, *, limit: int = 100
    ):
        stmt = (
            select(MembershipLifecycleEvent)
            .where(
                MembershipLifecycleEvent.tenant_id == tenant_id,
                MembershipLifecycleEvent.member_id == member_id,
            )
            .order_by(MembershipLifecycleEvent.created_at.desc())
            .limit(limit)
        )
        result = await self.session.execute(stmt)
        return result.scalars().all()
