"""Mission file reference and prerequisite repository

Handles junction tables for mission files.
"""

from __future__ import annotations

import sqlite3

from pydantic import TypeAdapter

from unfamiliar.db.connection import DatabaseError
from unfamiliar.models import MissionFileRef

# Type adapter for parsing database rows into models
_file_ref_adapter = TypeAdapter(MissionFileRef)


class MissionFileRefRepository:
    """Repository for mission file references and prerequisites

    Handles:
    - File references (files/lines to investigate)
    - Prerequisites (DAG of mission dependencies)
    """

    def __init__(self, conn: sqlite3.Connection) -> None:
        """Initialize repository with a database connection.
        Args:
            conn: A sqlite3.Connection with Row factory enabled.
        """
        self._conn = conn

    def add_file_refs(self, mission_id: int, refs: list[MissionFileRef]) -> None:
        """Add file references to a mission.

        Args:
            mission_id: The mission's primary key.
            refs: List of MissionFileRef objects (without ids).

        Raises:
            DatabaseError: On insert failure.
        """
        if not refs:
            return

        rows = [
            (
                mission_id,
                r.file_path,
                r.line_start,
                r.line_end,
                r.description,
                r.file_hash,
            )
            for r in refs
        ]

        try:
            self._conn.executemany(
                """
                INSERT INTO mission_file_ref (
                    mission_id, file_path, line_start, line_end,
                    description, file_hash
                ) VALUES (?, ?, ?, ?, ?, ?)
                """,
                rows,
            )
            self._conn.commit()
        except sqlite3.Error as exc:
            raise DatabaseError("Failed to add file references") from exc

    def get_file_refs(self, mission_id: int) -> list[MissionFileRef]:
        """Get all file references for a mission.

        Args:
            mission_id: The mission's primary key.

        Returns:
            List of MissionFileRef objects (may be empty).
        """
        rows = self._conn.execute(
            """
            SELECT id
                , mission_id
                , file_path
                , line_start
                , line_end
                , description
                , file_hash
            FROM mission_file_ref WHERE mission_id = ?
            ORDER BY file_path, line_start
            """,
            (mission_id,),
        ).fetchall()
        return [_row_to_file_ref(row) for row in rows]

    def add_prerequisites(self, dependent_id: int, prerequisite_ids: list[int]) -> None:
        """Add prerequisite relationships to a mission.

        Args:
            dependent_id: The mission that requires prerequisites.
            prerequisite_ids: List of mission IDs that must be completed first.

        Raises:
            DatabaseError: On insert failure or invalid mission IDs.
        """
        if not prerequisite_ids:
            return

        rows = [(pid, dependent_id) for pid in prerequisite_ids]

        try:
            self._conn.executemany(
                """
                INSERT OR IGNORE INTO mission_prerequisite (
                    prerequisite_id, dependent_id
                ) VALUES (?, ?)
                """,
                rows,
            )
            self._conn.commit()
        except sqlite3.Error as exc:
            raise DatabaseError("Failed to add prerequisites") from exc

    def get_prerequisites(self, mission_id: int) -> list[int]:
        """Get prerequisite mission IDs for a mission.

        Args:
            mission_id: The mission to query.

        Returns:
            List of prerequisite mission IDs.
        """
        rows = self._conn.execute(
            "SELECT prerequisite_id FROM mission_prerequisite WHERE dependent_id = ?",
            (mission_id,),
        ).fetchall()
        return [row["prerequisite_id"] for row in rows]

    def get_dependents(self, mission_id: int) -> list[int]:
        """Get dependent mission IDs for a mission.

        Args:
            mission_id: The mission to query.

        Returns:
            List of dependent mission IDs (missions that require this one).
        """
        rows = self._conn.execute(
            "SELECT dependent_id FROM mission_prerequisite WHERE prerequisite_id = ?",
            (mission_id,),
        ).fetchall()
        return [row["dependent_id"] for row in rows]


def _row_to_file_ref(row: sqlite3.Row) -> MissionFileRef:
    """Convert a database row to a MissionFileRef model."""
    return _file_ref_adapter.validate_python(
        {
            "id": row["id"],
            "mission_id": row["mission_id"],
            "file_path": row["file_path"],
            "line_start": row["line_start"],
            "line_end": row["line_end"],
            "description": row["description"],
            "file_hash": row["file_hash"],
        }
    )
