from __future__ import annotations

from sqlalchemy import or_, select
from sqlalchemy.orm import Session

from app.models.agent_asset import (
    AgentAsset,
    AgentAssetReview,
    AgentAssetTestRun,
    AgentAssetVersion,
)


class AgentAssetRepository:
    def __init__(self, db: Session) -> None:
        self.db = db

    def list(
        self,
        *,
        asset_type: str | None = None,
        status: str | None = None,
        domain: str | None = None,
        keyword: str | None = None,
    ) -> list[AgentAsset]:
        stmt = select(AgentAsset)

        if asset_type:
            stmt = stmt.where(AgentAsset.asset_type == asset_type)
        if status:
            stmt = stmt.where(AgentAsset.status == status)
        if domain:
            stmt = stmt.where(AgentAsset.domain == domain)
        if keyword:
            like_keyword = f"%{keyword.strip()}%"
            stmt = stmt.where(
                or_(
                    AgentAsset.name.ilike(like_keyword),
                    AgentAsset.code.ilike(like_keyword),
                    AgentAsset.description.ilike(like_keyword),
                )
            )

        stmt = stmt.order_by(AgentAsset.updated_at.desc(), AgentAsset.created_at.desc())
        return list(self.db.scalars(stmt).all())

    def get(self, asset_id: str) -> AgentAsset | None:
        return self.db.get(AgentAsset, asset_id)

    def get_by_code(self, code: str) -> AgentAsset | None:
        stmt = select(AgentAsset).where(AgentAsset.code == code)
        return self.db.scalar(stmt)

    def list_versions(self, asset_id: str, *, limit: int | None = None) -> list[AgentAssetVersion]:
        stmt = (
            select(AgentAssetVersion)
            .where(AgentAssetVersion.asset_id == asset_id)
            .order_by(AgentAssetVersion.created_at.desc())
        )
        if limit is not None:
            stmt = stmt.limit(limit)
        return list(self.db.scalars(stmt).all())

    def list_versions_for_assets(self, asset_ids: list[str]) -> list[AgentAssetVersion]:
        if not asset_ids:
            return []

        stmt = (
            select(AgentAssetVersion)
            .where(AgentAssetVersion.asset_id.in_(asset_ids))
            .order_by(AgentAssetVersion.asset_id, AgentAssetVersion.created_at.desc())
        )
        return list(self.db.scalars(stmt).all())

    def get_version(self, asset_id: str, version: str) -> AgentAssetVersion | None:
        stmt = select(AgentAssetVersion).where(
            AgentAssetVersion.asset_id == asset_id,
            AgentAssetVersion.version == version,
        )
        return self.db.scalar(stmt)

    def list_reviews(self, asset_id: str, *, limit: int | None = None) -> list[AgentAssetReview]:
        stmt = (
            select(AgentAssetReview)
            .where(AgentAssetReview.asset_id == asset_id)
            .order_by(AgentAssetReview.created_at.desc())
        )
        if limit is not None:
            stmt = stmt.limit(limit)
        return list(self.db.scalars(stmt).all())

    def list_reviews_for_assets(self, asset_ids: list[str]) -> list[AgentAssetReview]:
        if not asset_ids:
            return []

        stmt = (
            select(AgentAssetReview)
            .where(AgentAssetReview.asset_id.in_(asset_ids))
            .order_by(AgentAssetReview.asset_id, AgentAssetReview.created_at.desc())
        )
        return list(self.db.scalars(stmt).all())

    def get_review(
        self, asset_id: str, version: str, review_status: str | None = None
    ) -> AgentAssetReview | None:
        stmt = select(AgentAssetReview).where(
            AgentAssetReview.asset_id == asset_id,
            AgentAssetReview.version == version,
        )
        if review_status:
            stmt = stmt.where(AgentAssetReview.review_status == review_status)
        stmt = stmt.order_by(AgentAssetReview.created_at.desc())
        return self.db.scalar(stmt)

    def create_asset(self, asset: AgentAsset) -> AgentAsset:
        self.db.add(asset)
        self.db.commit()
        self.db.refresh(asset)
        return asset

    def save_asset(self, asset: AgentAsset) -> AgentAsset:
        self.db.add(asset)
        self.db.commit()
        self.db.refresh(asset)
        return asset

    def create_version(self, version: AgentAssetVersion) -> AgentAssetVersion:
        self.db.add(version)
        self.db.commit()
        self.db.refresh(version)
        return version

    def create_review(self, review: AgentAssetReview) -> AgentAssetReview:
        self.db.add(review)
        self.db.commit()
        self.db.refresh(review)
        return review

    def list_test_runs(
        self,
        asset_id: str,
        *,
        version: str | None = None,
        test_type: str | None = None,
        status: str | None = None,
        limit: int | None = None,
    ) -> list[AgentAssetTestRun]:
        stmt = (
            select(AgentAssetTestRun)
            .where(AgentAssetTestRun.asset_id == asset_id)
            .order_by(AgentAssetTestRun.created_at.desc())
        )
        if version:
            stmt = stmt.where(AgentAssetTestRun.version == version)
        if test_type:
            stmt = stmt.where(AgentAssetTestRun.test_type == test_type)
        if status:
            stmt = stmt.where(AgentAssetTestRun.status == status)
        if limit is not None:
            stmt = stmt.limit(limit)
        return list(self.db.scalars(stmt).all())

    def get_latest_test_run(
        self,
        asset_id: str,
        *,
        version: str | None = None,
        test_type: str | None = None,
        status: str | None = None,
    ) -> AgentAssetTestRun | None:
        items = self.list_test_runs(
            asset_id,
            version=version,
            test_type=test_type,
            status=status,
            limit=1,
        )
        return items[0] if items else None

    def create_test_run(self, test_run: AgentAssetTestRun) -> AgentAssetTestRun:
        self.db.add(test_run)
        self.db.commit()
        self.db.refresh(test_run)
        return test_run

    def delete_asset(self, asset: AgentAsset) -> None:
        self.db.delete(asset)
        self.db.commit()
