"""Builder domain - timeline and changed files data access."""
from __future__ import annotations
from typing import Optional
from uuid import UUID

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

from src.domain.builder.models import ChangedFile, TimelineEvent


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

    async def create(
        self,
        project_id: UUID,
        event_type: str,
        *,
        run_id: Optional[UUID] = None,
        revision_id: Optional[UUID] = None,
        preview_id: Optional[UUID] = None,
        deploy_id: Optional[UUID] = None,
        phase: Optional[str] = None,
        title: Optional[str] = None,
        detail_text: Optional[str] = None,
        payload: Optional[dict] = None,
        sequence_no: int = 0,
    ) -> TimelineEvent:
        ev = TimelineEvent(
            project_id=project_id,
            run_id=run_id,
            revision_id=revision_id,
            preview_id=preview_id,
            deploy_id=deploy_id,
            event_type=event_type,
            phase=phase,
            title=title,
            detail_text=detail_text,
            payload=payload,
            sequence_no=sequence_no,
        )
        self._db.add(ev)
        await self._db.flush()
        await self._db.refresh(ev)
        return ev

    async def list_for_run(
        self, project_id: UUID, run_id: UUID, limit: int = 100
    ) -> list[TimelineEvent]:
        result = await self._db.execute(
            select(TimelineEvent)
            .where(TimelineEvent.project_id == project_id)
            .where(TimelineEvent.run_id == run_id)
            # Older events were persisted with sequence_no=0. Keep the
            # sequence as the primary order for newer events, but make equal
            # sequence values deterministic so late inserts cannot reshuffle
            # the visible timeline.
            .order_by(TimelineEvent.sequence_no, TimelineEvent.created_at, TimelineEvent.id)
            .limit(limit)
        )
        return list(result.scalars().all())

    async def list_for_project(
        self, project_id: UUID, run_id: Optional[UUID] = None, limit: int = 50
    ) -> list[TimelineEvent]:
        q = select(TimelineEvent).where(TimelineEvent.project_id == project_id)
        if run_id:
            q = q.where(TimelineEvent.run_id == run_id)
        q = q.order_by(TimelineEvent.created_at.desc()).limit(limit)
        result = await self._db.execute(q)
        events = list(result.scalars().all())
        events.reverse()
        return events


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

    async def create(
        self,
        project_id: UUID,
        file_path: str,
        change_type: str,
        *,
        run_id: Optional[UUID] = None,
        revision_id: Optional[UUID] = None,
        language: Optional[str] = None,
        summary: Optional[str] = None,
        lines_added: Optional[int] = None,
        lines_removed: Optional[int] = None,
        patch_excerpt: Optional[str] = None,
    ) -> ChangedFile:
        cf = ChangedFile(
            project_id=project_id,
            run_id=run_id,
            revision_id=revision_id,
            file_path=file_path,
            change_type=change_type,
            language=language,
            summary=summary,
            lines_added=lines_added,
            lines_removed=lines_removed,
            patch_excerpt=patch_excerpt,
        )
        self._db.add(cf)
        await self._db.flush()
        await self._db.refresh(cf)
        return cf

    async def list_for_run(self, project_id: UUID, run_id: UUID) -> list[ChangedFile]:
        result = await self._db.execute(
            select(ChangedFile)
            .where(ChangedFile.project_id == project_id)
            .where(ChangedFile.run_id == run_id)
        )
        return list(result.scalars().all())

    async def list_for_revision(
        self, project_id: UUID, revision_id: UUID
    ) -> list[ChangedFile]:
        result = await self._db.execute(
            select(ChangedFile)
            .where(ChangedFile.project_id == project_id)
            .where(ChangedFile.revision_id == revision_id)
        )
        return list(result.scalars().all())
