"""
Relationship storage service for managing code relationships in SQLite.
"""

import json
import sqlite3
from dataclasses import dataclass
from typing import Optional

from nogic.parsing.types import ExtractedCall, ExtractedImport, ExtractedClass


@dataclass
class StoredImport:
    """A file-level import retrieved from the database."""

    id: int
    source_file_id: int
    target_file_id: Optional[int]
    module_name: str


@dataclass
class StoredSymbolImport:
    """A symbol-level import retrieved from the database."""

    id: int
    source_file_id: int
    module_name: str
    symbol_names: list[str]
    import_type: str
    alias: Optional[str]
    line: Optional[int]


@dataclass
class StoredCall:
    """A call relationship retrieved from the database."""

    id: int
    caller_id: str
    callee_id: Optional[str]
    callee_name: str
    call_type: str
    receiver: Optional[str]
    line: Optional[int]


@dataclass
class StoredInheritance:
    """An inheritance relationship retrieved from the database."""

    id: int
    child_id: str
    parent_name: str
    parent_id: Optional[str]
    type: str


class RelationshipStorageService:
    """Service for storing and querying code relationships."""

    def __init__(self, conn: sqlite3.Connection):
        self.conn = conn
        self.conn.row_factory = sqlite3.Row

    # --- Import Storage ---

    def store_import(
        self,
        source_file_id: int,
        imp: ExtractedImport,
        target_file_id: Optional[int] = None,
    ) -> None:
        """Store an import relationship."""
        # Store file-level import
        self.conn.execute(
            """
            INSERT OR IGNORE INTO imports (source_file_id, target_file_id, module_name)
            VALUES (?, ?, ?)
            """,
            (source_file_id, target_file_id, imp.module),
        )

        # Determine import type
        if imp.is_wildcard:
            import_type = "wildcard"
            symbol_names = ["*"]
        elif imp.name:
            import_type = "named"
            symbol_names = [imp.name]
        else:
            import_type = "namespace"
            symbol_names = [imp.module.split(".")[-1]]

        # Store symbol-level import
        self.conn.execute(
            """
            INSERT INTO symbol_imports (source_file_id, module_name, symbol_names, import_type, alias, line)
            VALUES (?, ?, ?, ?, ?, ?)
            """,
            (
                source_file_id,
                imp.module,
                json.dumps(symbol_names),
                import_type,
                imp.alias,
                imp.line,
            ),
        )

    def delete_file_imports(self, file_id: int) -> int:
        """Delete all imports for a file. Returns count deleted."""
        count1 = self.conn.execute(
            "DELETE FROM imports WHERE source_file_id = ?", (file_id,)
        ).rowcount
        count2 = self.conn.execute(
            "DELETE FROM symbol_imports WHERE source_file_id = ?", (file_id,)
        ).rowcount
        return count1 + count2

    def get_file_imports(self, file_id: int) -> list[StoredImport]:
        """Get all file-level imports for a file."""
        rows = self.conn.execute(
            "SELECT * FROM imports WHERE source_file_id = ?", (file_id,)
        ).fetchall()

        return [
            StoredImport(
                id=row["id"],
                source_file_id=row["source_file_id"],
                target_file_id=row["target_file_id"],
                module_name=row["module_name"],
            )
            for row in rows
        ]

    def get_symbol_imports(self, file_id: int) -> list[StoredSymbolImport]:
        """Get all symbol-level imports for a file."""
        rows = self.conn.execute(
            "SELECT * FROM symbol_imports WHERE source_file_id = ?", (file_id,)
        ).fetchall()

        return [
            StoredSymbolImport(
                id=row["id"],
                source_file_id=row["source_file_id"],
                module_name=row["module_name"],
                symbol_names=json.loads(row["symbol_names"]),
                import_type=row["import_type"],
                alias=row["alias"],
                line=row["line"],
            )
            for row in rows
        ]

    # --- Call Storage ---

    def store_call(
        self,
        call: ExtractedCall,
        caller_symbol_id: str,
        callee_symbol_id: Optional[str] = None,
    ) -> None:
        """Store a call relationship."""
        # Determine call type
        if call.receiver:
            call_type = "method"
        else:
            call_type = "function"

        self.conn.execute(
            """
            INSERT INTO calls (caller_id, callee_id, callee_name, call_type, receiver, line)
            VALUES (?, ?, ?, ?, ?, ?)
            """,
            (
                caller_symbol_id,
                callee_symbol_id,
                call.name,
                call_type,
                call.receiver,
                call.line,
            ),
        )

    def delete_caller_calls(self, caller_id: str) -> int:
        """Delete all calls from a specific caller symbol."""
        cursor = self.conn.execute(
            "DELETE FROM calls WHERE caller_id = ?", (caller_id,)
        )
        return cursor.rowcount

    def delete_file_calls(self, file_id: int) -> int:
        """Delete all calls from symbols in a file."""
        cursor = self.conn.execute(
            """
            DELETE FROM calls WHERE caller_id IN (
                SELECT id FROM symbols WHERE file_id = ?
            )
            """,
            (file_id,),
        )
        return cursor.rowcount

    def get_calls_from(self, caller_id: str) -> list[StoredCall]:
        """Get all calls made by a symbol."""
        rows = self.conn.execute(
            "SELECT * FROM calls WHERE caller_id = ?", (caller_id,)
        ).fetchall()

        return [self._row_to_call(row) for row in rows]

    def get_calls_to(self, callee_id: str) -> list[StoredCall]:
        """Get all calls to a symbol."""
        rows = self.conn.execute(
            "SELECT * FROM calls WHERE callee_id = ?", (callee_id,)
        ).fetchall()

        return [self._row_to_call(row) for row in rows]

    def get_calls_by_name(self, name: str) -> list[StoredCall]:
        """Get all calls to a function/method by name (including unresolved)."""
        rows = self.conn.execute(
            "SELECT * FROM calls WHERE callee_name = ?", (name,)
        ).fetchall()

        return [self._row_to_call(row) for row in rows]

    def _row_to_call(self, row: sqlite3.Row) -> StoredCall:
        """Convert a database row to a StoredCall."""
        return StoredCall(
            id=row["id"],
            caller_id=row["caller_id"],
            callee_id=row["callee_id"],
            callee_name=row["callee_name"],
            call_type=row["call_type"],
            receiver=row["receiver"],
            line=row["line"],
        )

    # --- Inheritance Storage ---

    def store_inheritance(
        self,
        child_symbol_id: str,
        cls: ExtractedClass,
        parent_symbol_ids: Optional[dict[str, str]] = None,
    ) -> None:
        """Store inheritance relationships for a class."""
        parent_symbol_ids = parent_symbol_ids or {}

        for base_name in cls.bases:
            parent_id = parent_symbol_ids.get(base_name)

            self.conn.execute(
                """
                INSERT INTO inheritance (child_id, parent_name, parent_id, type)
                VALUES (?, ?, ?, ?)
                """,
                (
                    child_symbol_id,
                    base_name,
                    parent_id,
                    "extends",  # Python only has extends, not implements
                ),
            )

    def delete_child_inheritance(self, child_id: str) -> int:
        """Delete all inheritance relationships for a child class."""
        cursor = self.conn.execute(
            "DELETE FROM inheritance WHERE child_id = ?", (child_id,)
        )
        return cursor.rowcount

    def delete_file_inheritance(self, file_id: int) -> int:
        """Delete all inheritance relationships for classes in a file."""
        cursor = self.conn.execute(
            """
            DELETE FROM inheritance WHERE child_id IN (
                SELECT id FROM symbols WHERE file_id = ? AND kind = 'class'
            )
            """,
            (file_id,),
        )
        return cursor.rowcount

    def get_class_parents(self, child_id: str) -> list[StoredInheritance]:
        """Get all parent classes for a class."""
        rows = self.conn.execute(
            "SELECT * FROM inheritance WHERE child_id = ?", (child_id,)
        ).fetchall()

        return [self._row_to_inheritance(row) for row in rows]

    def get_class_children(self, parent_id: str) -> list[StoredInheritance]:
        """Get all child classes for a class."""
        rows = self.conn.execute(
            "SELECT * FROM inheritance WHERE parent_id = ?", (parent_id,)
        ).fetchall()

        return [self._row_to_inheritance(row) for row in rows]

    def _row_to_inheritance(self, row: sqlite3.Row) -> StoredInheritance:
        """Convert a database row to a StoredInheritance."""
        return StoredInheritance(
            id=row["id"],
            child_id=row["child_id"],
            parent_name=row["parent_name"],
            parent_id=row["parent_id"],
            type=row["type"],
        )

    # --- Bulk Operations ---

    def delete_all_file_relationships(self, file_id: int) -> dict[str, int]:
        """Delete all relationships for a file. Returns counts by type."""
        return {
            "imports": self.delete_file_imports(file_id),
            "calls": self.delete_file_calls(file_id),
            "inheritance": self.delete_file_inheritance(file_id),
        }
