"""Previews domain - data access layer."""
from __future__ import annotations
from datetime import datetime, timezone
from typing import Optional
from uuid import UUID

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

from src.domain.previews.models import PreviewEnvironment, RoutesManifest


class PreviewRepository:
    def __init__(self, db: AsyncSession):
        self._db = db

    async def get_by_id(self, preview_id: UUID) -> Optional[PreviewEnvironment]:
        result = await self._db.execute(
            select(PreviewEnvironment).where(PreviewEnvironment.id == preview_id)
        )
        return result.scalar_one_or_none()

    async def list_for_project(self, project_id: UUID) -> list[PreviewEnvironment]:
        result = await self._db.execute(
            select(PreviewEnvironment)
            .where(PreviewEnvironment.project_id == project_id)
            .order_by(PreviewEnvironment.created_at.desc())
        )
        return list(result.scalars().all())

    async def get_latest_for_revision(self, project_id: UUID, revision_id: UUID) -> Optional[PreviewEnvironment]:
        result = await self._db.execute(
            select(PreviewEnvironment)
            .where(PreviewEnvironment.project_id == project_id)
            .where(PreviewEnvironment.revision_id == revision_id)
            .order_by(PreviewEnvironment.created_at.desc())
            .limit(1)
        )
        return result.scalar_one_or_none()

    async def create(
        self,
        project_id: UUID,
        revision_id: UUID,
        status: str = "provisioning",
    ) -> PreviewEnvironment:
        pe = PreviewEnvironment(
            project_id=project_id,
            revision_id=revision_id,
            status=status,
        )
        self._db.add(pe)
        await self._db.flush()
        await self._db.refresh(pe)
        return pe

    async def update_status(
        self,
        preview_id: UUID,
        status: str,
        base_url: Optional[str] = None,
        provider_preview_url: Optional[str] = None,
    ) -> Optional[PreviewEnvironment]:
        pe = await self.get_by_id(preview_id)
        if not pe:
            return None
        pe.status = status
        if base_url is not None:
            pe.base_url = base_url
        if provider_preview_url is not None:
            pe.provider_preview_url = provider_preview_url
        await self._db.flush()
        return pe

    async def stop(self, preview_id: UUID) -> None:
        pe = await self.get_by_id(preview_id)
        if pe:
            pe.status = "stopped"
            pe.stopped_at = datetime.now(timezone.utc)
            await self._db.flush()


class RoutesManifestRepository:
    def __init__(self, db: AsyncSession):
        self._db = db

    async def get_latest(
        self, project_id: UUID, revision_id: Optional[UUID] = None
    ) -> Optional[RoutesManifest]:
        q = select(RoutesManifest).where(
            RoutesManifest.project_id == project_id
        )
        if revision_id:
            q = q.where(RoutesManifest.revision_id == revision_id)
        q = q.order_by(RoutesManifest.created_at.desc()).limit(1)
        result = await self._db.execute(q)
        return result.scalar_one_or_none()

    async def create(
        self,
        project_id: UUID,
        manifest: dict,
        source: str,
        revision_id: Optional[UUID] = None,
    ) -> RoutesManifest:
        rm = RoutesManifest(
            project_id=project_id,
            revision_id=revision_id,
            manifest=manifest,
            source=source,
        )
        self._db.add(rm)
        await self._db.flush()
        await self._db.refresh(rm)
        return rm
