# ================================================================
# services/procurement/app/repositories/procurement_plan_repository.py
# ================================================================
"""
Procurement Plan Repository - Database operations for procurement plans and requirements
"""

import uuid
from datetime import datetime, date
from typing import List, Optional, Dict, Any
from sqlalchemy import select, and_, desc, func
from sqlalchemy.ext.asyncio import AsyncSession
from sqlalchemy.orm import selectinload

from app.models.procurement_plan import ProcurementPlan, ProcurementRequirement
from app.repositories.base_repository import BaseRepository


class ProcurementPlanRepository(BaseRepository):
    """Repository for procurement plan operations"""

    def __init__(self, db: AsyncSession):
        super().__init__(ProcurementPlan)
        self.db = db

    async def create_plan(self, plan_data: Dict[str, Any]) -> ProcurementPlan:
        """Create a new procurement plan"""
        plan = ProcurementPlan(**plan_data)
        self.db.add(plan)
        await self.db.flush()
        return plan

    async def get_plan_by_id(self, plan_id: uuid.UUID, tenant_id: uuid.UUID) -> Optional[ProcurementPlan]:
        """Get procurement plan by ID"""
        stmt = select(ProcurementPlan).where(
            and_(
                ProcurementPlan.id == plan_id,
                ProcurementPlan.tenant_id == tenant_id
            )
        ).options(selectinload(ProcurementPlan.requirements))

        result = await self.db.execute(stmt)
        return result.scalar_one_or_none()

    async def get_plan_by_date(self, plan_date: date, tenant_id: uuid.UUID) -> Optional[ProcurementPlan]:
        """Get procurement plan for a specific date"""
        stmt = select(ProcurementPlan).where(
            and_(
                ProcurementPlan.plan_date == plan_date,
                ProcurementPlan.tenant_id == tenant_id
            )
        ).options(selectinload(ProcurementPlan.requirements))

        result = await self.db.execute(stmt)
        return result.scalar_one_or_none()

    async def get_current_plan(self, tenant_id: uuid.UUID) -> Optional[ProcurementPlan]:
        """Get the current day's procurement plan"""
        today = date.today()
        return await self.get_plan_by_date(today, tenant_id)

    async def list_plans(
        self,
        tenant_id: uuid.UUID,
        status: Optional[str] = None,
        start_date: Optional[date] = None,
        end_date: Optional[date] = None,
        limit: int = 50,
        offset: int = 0
    ) -> List[ProcurementPlan]:
        """List procurement plans with filters"""
        conditions = [ProcurementPlan.tenant_id == tenant_id]

        if status:
            conditions.append(ProcurementPlan.status == status)
        if start_date:
            conditions.append(ProcurementPlan.plan_date >= start_date)
        if end_date:
            conditions.append(ProcurementPlan.plan_date <= end_date)

        stmt = (
            select(ProcurementPlan)
            .where(and_(*conditions))
            .order_by(desc(ProcurementPlan.plan_date))
            .limit(limit)
            .offset(offset)
            .options(selectinload(ProcurementPlan.requirements))
        )

        result = await self.db.execute(stmt)
        return result.scalars().all()

    async def get_plans_by_tenant(
        self,
        tenant_id: uuid.UUID,
        start_date: Optional[datetime] = None,
        end_date: Optional[datetime] = None
    ) -> List[ProcurementPlan]:
        """Get all procurement plans for a tenant with optional date filters"""
        conditions = [ProcurementPlan.tenant_id == tenant_id]

        if start_date:
            conditions.append(ProcurementPlan.created_at >= start_date)
        if end_date:
            conditions.append(ProcurementPlan.created_at <= end_date)

        stmt = (
            select(ProcurementPlan)
            .where(and_(*conditions))
            .order_by(desc(ProcurementPlan.created_at))
            .options(selectinload(ProcurementPlan.requirements))
        )

        result = await self.db.execute(stmt)
        return result.scalars().all()

    async def update_plan(self, plan_id: uuid.UUID, tenant_id: uuid.UUID, updates: Dict[str, Any]) -> Optional[ProcurementPlan]:
        """Update procurement plan"""
        plan = await self.get_plan_by_id(plan_id, tenant_id)
        if not plan:
            return None

        for key, value in updates.items():
            if hasattr(plan, key):
                setattr(plan, key, value)

        plan.updated_at = datetime.utcnow()
        await self.db.flush()
        return plan

    async def delete_plan(self, plan_id: uuid.UUID, tenant_id: uuid.UUID) -> bool:
        """Delete procurement plan"""
        plan = await self.get_plan_by_id(plan_id, tenant_id)
        if not plan:
            return False

        await self.db.delete(plan)
        return True

    async def generate_plan_number(self, tenant_id: uuid.UUID, plan_date: date) -> str:
        """Generate unique plan number"""
        date_str = plan_date.strftime("%Y%m%d")

        # Count existing plans for the same date
        stmt = select(func.count(ProcurementPlan.id)).where(
            and_(
                ProcurementPlan.tenant_id == tenant_id,
                ProcurementPlan.plan_date == plan_date
            )
        )
        result = await self.db.execute(stmt)
        count = result.scalar() or 0

        return f"PP-{date_str}-{count + 1:03d}"


class ProcurementRequirementRepository(BaseRepository):
    """Repository for procurement requirement operations"""

    def __init__(self, db: AsyncSession):
        super().__init__(ProcurementRequirement)
        self.db = db

    async def create_requirement(self, requirement_data: Dict[str, Any]) -> ProcurementRequirement:
        """Create a new procurement requirement"""
        requirement = ProcurementRequirement(**requirement_data)
        self.db.add(requirement)
        await self.db.flush()
        return requirement

    async def create_requirements_batch(self, requirements_data: List[Dict[str, Any]]) -> List[ProcurementRequirement]:
        """Create multiple procurement requirements"""
        requirements = [ProcurementRequirement(**data) for data in requirements_data]
        self.db.add_all(requirements)
        await self.db.flush()
        return requirements

    async def get_requirement_by_id(self, requirement_id: uuid.UUID, tenant_id: uuid.UUID) -> Optional[ProcurementRequirement]:
        """Get procurement requirement by ID"""
        stmt = select(ProcurementRequirement).join(ProcurementPlan).where(
            and_(
                ProcurementRequirement.id == requirement_id,
                ProcurementPlan.tenant_id == tenant_id
            )
        )

        result = await self.db.execute(stmt)
        return result.scalar_one_or_none()

    async def get_requirements_by_plan(self, plan_id: uuid.UUID) -> List[ProcurementRequirement]:
        """Get all requirements for a specific plan"""
        stmt = select(ProcurementRequirement).where(
            ProcurementRequirement.plan_id == plan_id
        ).order_by(ProcurementRequirement.priority.desc(), ProcurementRequirement.required_by_date)

        result = await self.db.execute(stmt)
        return result.scalars().all()

    async def update_requirement(
        self,
        requirement_id: uuid.UUID,
        updates: Dict[str, Any]
    ) -> Optional[ProcurementRequirement]:
        """Update procurement requirement"""
        stmt = select(ProcurementRequirement).where(
            ProcurementRequirement.id == requirement_id
        )
        result = await self.db.execute(stmt)
        requirement = result.scalar_one_or_none()

        if not requirement:
            return None

        for key, value in updates.items():
            if hasattr(requirement, key):
                setattr(requirement, key, value)

        requirement.updated_at = datetime.utcnow()
        await self.db.flush()
        return requirement

    async def generate_requirement_number(self, plan_id: uuid.UUID) -> str:
        """Generate unique requirement number within a plan"""
        stmt = select(func.count(ProcurementRequirement.id)).where(
            ProcurementRequirement.plan_id == plan_id
        )
        result = await self.db.execute(stmt)
        count = result.scalar() or 0

        return f"REQ-{count + 1:05d}"

    async def get_requirements_by_tenant(
        self,
        tenant_id: uuid.UUID,
        start_date: Optional[datetime] = None,
        end_date: Optional[datetime] = None
    ) -> List[ProcurementRequirement]:
        """Get all procurement requirements for a tenant with optional date filters"""
        conditions = [ProcurementPlan.tenant_id == tenant_id]

        if start_date:
            conditions.append(ProcurementRequirement.created_at >= start_date)
        if end_date:
            conditions.append(ProcurementRequirement.created_at <= end_date)

        stmt = (
            select(ProcurementRequirement)
            .join(ProcurementPlan)
            .where(and_(*conditions))
            .order_by(desc(ProcurementRequirement.created_at))
        )

        result = await self.db.execute(stmt)
        return result.scalars().all()
