"""
Database management for groknroll
"""

from datetime import datetime
from pathlib import Path
from typing import Any, Optional

from sqlalchemy import create_engine, desc
from sqlalchemy.orm import Session as DBSession
from sqlalchemy.orm import sessionmaker

from groknroll.storage.models import Analysis, Base, Execution, FileIndex, Project, Session


class Database:
    """
    SQLite database for groknroll project state

    Stores:
    - Project metadata
    - File index with AST data
    - RLM execution history
    - Session history
    - Analysis results
    """

    def __init__(self, db_path: Optional[Path] = None):
        """
        Initialize database

        Args:
            db_path: Path to SQLite database file.
                    If None, uses ~/.groknroll/groknroll.db
        """
        if db_path is None:
            db_path = Path.home() / ".groknroll" / "groknroll.db"

        db_path.parent.mkdir(parents=True, exist_ok=True)

        self.db_path = db_path
        self.engine = create_engine(f"sqlite:///{db_path}", echo=False)
        self.SessionLocal = sessionmaker(bind=self.engine)

        # Create tables
        Base.metadata.create_all(self.engine)

    def get_session(self) -> DBSession:
        """Get database session"""
        return self.SessionLocal()

    # =========================================================================
    # Project Operations
    # =========================================================================

    def get_or_create_project(self, project_path: Path) -> Project:
        """Get existing project or create new one"""
        with self.get_session() as session:
            project = session.query(Project).filter_by(path=str(project_path)).first()

            if project is None:
                project = Project(path=str(project_path), name=project_path.name)
                session.add(project)
                session.commit()
                session.refresh(project)

            return project

    def update_project_stats(self, project_id: int, total_files: int, total_lines: int) -> None:
        """Update project statistics"""
        with self.get_session() as session:
            project = session.query(Project).filter_by(id=project_id).first()
            if project:
                project.total_files = total_files
                project.total_lines = total_lines
                project.last_indexed = datetime.utcnow()
                session.commit()

    # =========================================================================
    # File Index Operations
    # =========================================================================

    def index_file(
        self, project_id: int, file_path: Path, relative_path: str, **metadata
    ) -> FileIndex:
        """Index a file"""
        with self.get_session() as session:
            file_index = (
                session.query(FileIndex)
                .filter_by(project_id=project_id, path=str(file_path))
                .first()
            )

            if file_index is None:
                file_index = FileIndex(
                    project_id=project_id, path=str(file_path), relative_path=relative_path
                )
                session.add(file_index)

            # Update metadata
            for key, value in metadata.items():
                if hasattr(file_index, key):
                    setattr(file_index, key, value)

            file_index.updated_at = datetime.utcnow()
            session.commit()
            session.refresh(file_index)

            return file_index

    def get_project_files(self, project_id: int) -> list[FileIndex]:
        """Get all indexed files for project"""
        with self.get_session() as session:
            return session.query(FileIndex).filter_by(project_id=project_id).all()

    def search_files(
        self, project_id: int, query: str, language: Optional[str] = None
    ) -> list[FileIndex]:
        """Search indexed files"""
        with self.get_session() as session:
            q = session.query(FileIndex).filter_by(project_id=project_id)

            if language:
                q = q.filter_by(language=language)

            q = q.filter(FileIndex.relative_path.like(f"%{query}%"))

            return q.all()

    # =========================================================================
    # Execution History
    # =========================================================================

    def log_execution(self, project_id: int, task: str, response: str, **metrics) -> Execution:
        """Log RLM execution"""
        with self.get_session() as session:
            execution = Execution(
                project_id=project_id,
                task=task,
                response=response,
                status=metrics.get("status", "success"),
                **metrics,
            )
            session.add(execution)
            session.commit()
            session.refresh(execution)

            return execution

    def get_recent_executions(self, project_id: int, limit: int = 10) -> list[Execution]:
        """Get recent executions"""
        with self.get_session() as session:
            return (
                session.query(Execution)
                .filter_by(project_id=project_id)
                .order_by(desc(Execution.started_at))
                .limit(limit)
                .all()
            )

    def get_execution_stats(self, project_id: int) -> dict[str, Any]:
        """Get execution statistics"""
        with self.get_session() as session:
            executions = session.query(Execution).filter_by(project_id=project_id).all()

            total_cost = sum(e.total_cost or 0.0 for e in executions)
            total_time = sum(e.total_time or 0.0 for e in executions)
            total_count = len(executions)
            success_count = sum(1 for e in executions if e.status == "success")

            return {
                "total_executions": total_count,
                "successful": success_count,
                "failed": total_count - success_count,
                "total_cost": total_cost,
                "total_time": total_time,
                "avg_cost": total_cost / total_count if total_count > 0 else 0.0,
                "avg_time": total_time / total_count if total_count > 0 else 0.0,
            }

    # =========================================================================
    # Session Management
    # =========================================================================

    def create_session(self, project_id: int, session_type: str) -> Session:
        """Create new session"""
        with self.get_session() as session:
            new_session = Session(project_id=project_id, session_type=session_type)
            session.add(new_session)
            session.commit()
            session.refresh(new_session)

            return new_session

    def end_session(self, session_id: int) -> None:
        """End session"""
        with self.get_session() as session:
            sess = session.query(Session).filter_by(id=session_id).first()
            if sess:
                sess.ended_at = datetime.utcnow()
                session.commit()

    # =========================================================================
    # Analysis Results
    # =========================================================================

    def save_analysis(
        self, project_id: int, analysis_type: str, results: dict[str, Any], **metadata
    ) -> Analysis:
        """Save analysis results"""
        with self.get_session() as session:
            analysis = Analysis(
                project_id=project_id, analysis_type=analysis_type, results=results, **metadata
            )
            session.add(analysis)
            session.commit()
            session.refresh(analysis)

            return analysis

    def get_latest_analysis(self, project_id: int, analysis_type: str) -> Optional[Analysis]:
        """Get latest analysis of given type"""
        with self.get_session() as session:
            return (
                session.query(Analysis)
                .filter_by(project_id=project_id, analysis_type=analysis_type)
                .order_by(desc(Analysis.created_at))
                .first()
            )
