"""Phase 6.2 sales engine repositories."""
from __future__ import annotations

from uuid import UUID

from sqlalchemy import select

from app.models.sales_engine import (
    OpportunityCompetitor,
    OpportunityContact,
    OpportunityProduct,
    OpportunityWinLoss,
    SalesAudit,
    SalesForecast,
    SalesGoal,
    SalesPlaybook,
    SalesPlaybookAssignment,
    SalesPlaybookStep,
    SalesProcess,
    SalesStageHistory,
    SalesTarget,
    WinLossCategory,
    WinLossReason,
)
from app.models.types import OpportunityStatus
from app.repositories.base import TenantBaseRepository
from app.models.foundation import Opportunity, Pipeline, PipelineStage


class OpportunityRepository(TenantBaseRepository[Opportunity]):
    model = Opportunity

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

    async def list_open_in_period(
        self,
        tenant_id: UUID,
        *,
        period_start,
        period_end,
        pipeline_id: UUID | None = None,
    ):
        clauses = [
            Opportunity.tenant_id == tenant_id,
            Opportunity.is_deleted.is_(False),
            Opportunity.status == OpportunityStatus.OPEN,
        ]
        if pipeline_id is not None:
            clauses.append(Opportunity.pipeline_id == pipeline_id)
        stmt = select(Opportunity).where(*clauses)
        result = await self.session.execute(stmt)
        rows = list(result.scalars().all())
        # Filter by expected_close_date when present
        filtered = []
        for row in rows:
            if row.expected_close_date is None:
                filtered.append(row)
            elif period_start <= row.expected_close_date <= period_end:
                filtered.append(row)
        return filtered


class PipelineRepository(TenantBaseRepository[Pipeline]):
    model = Pipeline

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


class PipelineStageRepository(TenantBaseRepository[PipelineStage]):
    model = PipelineStage

    async def list_by_pipeline(
        self, tenant_id: UUID, pipeline_id: UUID, *, offset: int = 0, limit: int = 100
    ):
        stmt = (
            select(PipelineStage)
            .where(
                PipelineStage.tenant_id == tenant_id,
                PipelineStage.pipeline_id == pipeline_id,
            )
            .order_by(PipelineStage.position.asc())
            .offset(offset)
            .limit(limit)
        )
        result = await self.session.execute(stmt)
        return result.scalars().all()


class SalesProcessRepository(TenantBaseRepository[SalesProcess]):
    model = SalesProcess


class SalesStageHistoryRepository(TenantBaseRepository[SalesStageHistory]):
    model = SalesStageHistory

    async def list_by_opportunity(self, tenant_id: UUID, opportunity_id: UUID):
        stmt = (
            select(SalesStageHistory)
            .where(
                SalesStageHistory.tenant_id == tenant_id,
                SalesStageHistory.opportunity_id == opportunity_id,
            )
            .order_by(SalesStageHistory.changed_at.desc())
        )
        result = await self.session.execute(stmt)
        return result.scalars().all()

    async def latest_for_opportunity(
        self, tenant_id: UUID, opportunity_id: UUID
    ) -> SalesStageHistory | None:
        rows = await self.list_by_opportunity(tenant_id, opportunity_id)
        return rows[0] if rows else None


class OpportunityProductRepository(TenantBaseRepository[OpportunityProduct]):
    model = OpportunityProduct

    async def list_by_opportunity(self, tenant_id: UUID, opportunity_id: UUID):
        stmt = select(OpportunityProduct).where(
            OpportunityProduct.tenant_id == tenant_id,
            OpportunityProduct.opportunity_id == opportunity_id,
        )
        result = await self.session.execute(stmt)
        return result.scalars().all()


class OpportunityContactRepository(TenantBaseRepository[OpportunityContact]):
    model = OpportunityContact

    async def list_by_opportunity(self, tenant_id: UUID, opportunity_id: UUID):
        stmt = select(OpportunityContact).where(
            OpportunityContact.tenant_id == tenant_id,
            OpportunityContact.opportunity_id == opportunity_id,
        )
        result = await self.session.execute(stmt)
        return result.scalars().all()


class OpportunityCompetitorRepository(TenantBaseRepository[OpportunityCompetitor]):
    model = OpportunityCompetitor

    async def list_by_opportunity(self, tenant_id: UUID, opportunity_id: UUID):
        stmt = select(OpportunityCompetitor).where(
            OpportunityCompetitor.tenant_id == tenant_id,
            OpportunityCompetitor.opportunity_id == opportunity_id,
        )
        result = await self.session.execute(stmt)
        return result.scalars().all()


class SalesPlaybookRepository(TenantBaseRepository[SalesPlaybook]):
    model = SalesPlaybook

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


class SalesPlaybookStepRepository(TenantBaseRepository[SalesPlaybookStep]):
    model = SalesPlaybookStep

    async def list_by_playbook(self, tenant_id: UUID, playbook_id: UUID):
        stmt = (
            select(SalesPlaybookStep)
            .where(
                SalesPlaybookStep.tenant_id == tenant_id,
                SalesPlaybookStep.playbook_id == playbook_id,
            )
            .order_by(SalesPlaybookStep.position.asc())
        )
        result = await self.session.execute(stmt)
        return result.scalars().all()


class SalesPlaybookAssignmentRepository(TenantBaseRepository[SalesPlaybookAssignment]):
    model = SalesPlaybookAssignment

    async def list_by_opportunity(self, tenant_id: UUID, opportunity_id: UUID):
        stmt = select(SalesPlaybookAssignment).where(
            SalesPlaybookAssignment.tenant_id == tenant_id,
            SalesPlaybookAssignment.opportunity_id == opportunity_id,
        )
        result = await self.session.execute(stmt)
        return result.scalars().all()


class WinLossCategoryRepository(TenantBaseRepository[WinLossCategory]):
    model = WinLossCategory

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


class WinLossReasonRepository(TenantBaseRepository[WinLossReason]):
    model = WinLossReason

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


class OpportunityWinLossRepository(TenantBaseRepository[OpportunityWinLoss]):
    model = OpportunityWinLoss

    async def get_by_opportunity(
        self, tenant_id: UUID, opportunity_id: UUID
    ) -> OpportunityWinLoss | None:
        stmt = select(OpportunityWinLoss).where(
            OpportunityWinLoss.tenant_id == tenant_id,
            OpportunityWinLoss.opportunity_id == opportunity_id,
        )
        result = await self.session.execute(stmt)
        return result.scalar_one_or_none()


class SalesForecastRepository(TenantBaseRepository[SalesForecast]):
    model = SalesForecast


class SalesGoalRepository(TenantBaseRepository[SalesGoal]):
    model = SalesGoal


class SalesTargetRepository(TenantBaseRepository[SalesTarget]):
    model = SalesTarget


class SalesAuditRepository(TenantBaseRepository[SalesAudit]):
    model = SalesAudit

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