"""Usage domain — data access layer."""
from __future__ import annotations

from datetime import date
from typing import Optional
from uuid import UUID, uuid4

from sqlalchemy import select
from sqlalchemy.ext.asyncio import AsyncSession

from src.domain.usage.models import ProjectHostedAppUsageSnapshot, WorkspaceUsageSnapshot


class WorkspaceUsageSnapshotRepository:
    def __init__(self, db: AsyncSession) -> None:
        self._db = db

    async def get_by_date(
        self, workspace_id: UUID, snapshot_date: date
    ) -> Optional[WorkspaceUsageSnapshot]:
        result = await self._db.execute(
            select(WorkspaceUsageSnapshot).where(
                WorkspaceUsageSnapshot.workspace_id == workspace_id,
                WorkspaceUsageSnapshot.snapshot_date == snapshot_date,
            )
        )
        return result.scalar_one_or_none()

    async def list_for_workspace(
        self,
        workspace_id: UUID,
        from_date: Optional[date] = None,
        to_date: Optional[date] = None,
        limit: int = 30,
    ) -> list[WorkspaceUsageSnapshot]:
        q = select(WorkspaceUsageSnapshot).where(
            WorkspaceUsageSnapshot.workspace_id == workspace_id
        )
        if from_date:
            q = q.where(WorkspaceUsageSnapshot.snapshot_date >= from_date)
        if to_date:
            q = q.where(WorkspaceUsageSnapshot.snapshot_date <= to_date)
        q = q.order_by(WorkspaceUsageSnapshot.snapshot_date.desc()).limit(limit)
        result = await self._db.execute(q)
        return list(result.scalars().all())

    async def get_latest_for_period(
        self, workspace_id: UUID, period_start: date
    ) -> Optional[WorkspaceUsageSnapshot]:
        """Latest snapshot on or after billing period start — for notification dedup."""
        result = await self._db.execute(
            select(WorkspaceUsageSnapshot)
            .where(
                WorkspaceUsageSnapshot.workspace_id == workspace_id,
                WorkspaceUsageSnapshot.snapshot_date >= period_start,
            )
            .order_by(WorkspaceUsageSnapshot.snapshot_date.desc())
            .limit(1)
        )
        return result.scalar_one_or_none()

    async def upsert(
        self, workspace_id: UUID, snapshot_date: date, **fields
    ) -> WorkspaceUsageSnapshot:
        row = await self.get_by_date(workspace_id, snapshot_date)
        if row:
            for k, v in fields.items():
                setattr(row, k, v)
        else:
            row = WorkspaceUsageSnapshot(
                id=uuid4(),
                workspace_id=workspace_id,
                snapshot_date=snapshot_date,
                **fields,
            )
            self._db.add(row)
        await self._db.flush()
        return row

    async def update(
        self, row: WorkspaceUsageSnapshot, **fields
    ) -> WorkspaceUsageSnapshot:
        for k, v in fields.items():
            setattr(row, k, v)
        await self._db.flush()
        return row


class ProjectHostedAppUsageSnapshotRepository:
    """Persistence for monitor-only generated-app edge traffic."""

    def __init__(self, db: AsyncSession) -> None:
        self._db = db

    async def list_for_project(
        self,
        project_id: UUID,
        *,
        from_date: Optional[date] = None,
        to_date: Optional[date] = None,
        limit: int = 30,
    ) -> list[ProjectHostedAppUsageSnapshot]:
        query = select(ProjectHostedAppUsageSnapshot).where(
            ProjectHostedAppUsageSnapshot.project_id == project_id
        )
        if from_date:
            query = query.where(ProjectHostedAppUsageSnapshot.snapshot_date >= from_date)
        if to_date:
            query = query.where(ProjectHostedAppUsageSnapshot.snapshot_date <= to_date)
        result = await self._db.execute(
            query.order_by(ProjectHostedAppUsageSnapshot.snapshot_date.desc()).limit(limit)
        )
        return list(result.scalars().all())

    async def upsert(
        self,
        *,
        workspace_id: UUID,
        project_id: UUID,
        snapshot_date: date,
        requests: int,
        egress_bytes: int,
    ) -> ProjectHostedAppUsageSnapshot:
        result = await self._db.execute(
            select(ProjectHostedAppUsageSnapshot).where(
                ProjectHostedAppUsageSnapshot.project_id == project_id,
                ProjectHostedAppUsageSnapshot.snapshot_date == snapshot_date,
            )
        )
        row = result.scalar_one_or_none()
        if row is None:
            row = ProjectHostedAppUsageSnapshot(
                workspace_id=workspace_id,
                project_id=project_id,
                snapshot_date=snapshot_date,
                requests=requests,
                egress_bytes=egress_bytes,
            )
            self._db.add(row)
        else:
            row.workspace_id = workspace_id
            row.requests = requests
            row.egress_bytes = egress_bytes
        await self._db.flush()
        return row
