"""
Symbol storage service for managing code symbols in SQLite.
"""

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

from nogic.parsing.types import ExtractedFunction, ExtractedClass


@dataclass
class StoredSymbol:
    """A symbol retrieved from the database."""

    id: str
    name: str
    file_id: int
    kind: str
    parent_symbol_id: Optional[str]
    exported: bool
    line_start: Optional[int]
    line_end: Optional[int]
    signature: Optional[str]
    docstring: Optional[str]
    extra_data: Optional[dict]


class SymbolStorageService:
    """Service for storing and querying code symbols."""

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

    def _make_symbol_id(
        self, file_path: str, name: str, parent_name: Optional[str] = None
    ) -> str:
        """Create a unique symbol ID from file path and names."""
        if parent_name:
            return f"{file_path}::{parent_name}::{name}"
        return f"{file_path}::{name}"

    def store_function(
        self,
        file_id: int,
        file_path: str,
        func: ExtractedFunction,
        parent_symbol_id: Optional[str] = None,
    ) -> str:
        """Store a function or method symbol. Returns the symbol ID."""
        if func.class_name:
            symbol_id = self._make_symbol_id(file_path, func.name, func.class_name)
            kind = "method"
        else:
            symbol_id = self._make_symbol_id(file_path, func.name)
            kind = "function"

        extra_data = {
            "decorators": func.decorators,
            "parameters": func.parameters,
            "return_type": func.return_type,
            "is_async": func.is_async,
        }

        self.conn.execute(
            """
            INSERT OR REPLACE INTO symbols
            (id, name, file_id, kind, parent_symbol_id, exported, line_start, line_end, signature, docstring, extra_data)
            VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
            """,
            (
                symbol_id,
                func.name,
                file_id,
                kind,
                parent_symbol_id,
                0,  # exported - would need export detection
                func.start_line,
                func.end_line,
                func.signature,
                func.docstring,
                json.dumps(extra_data),
            ),
        )

        return symbol_id

    def store_class(
        self,
        file_id: int,
        file_path: str,
        cls: ExtractedClass,
    ) -> str:
        """Store a class symbol and its methods. Returns the class symbol ID."""
        symbol_id = self._make_symbol_id(file_path, cls.name)

        extra_data = {
            "decorators": cls.decorators,
            "bases": cls.bases,
        }

        self.conn.execute(
            """
            INSERT OR REPLACE INTO symbols
            (id, name, file_id, kind, parent_symbol_id, exported, line_start, line_end, signature, docstring, extra_data)
            VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
            """,
            (
                symbol_id,
                cls.name,
                file_id,
                "class",
                None,
                0,  # exported
                cls.start_line,
                cls.end_line,
                f"class {cls.name}",
                cls.docstring,
                json.dumps(extra_data),
            ),
        )

        # Store methods with parent reference
        for method in cls.methods:
            self.store_function(file_id, file_path, method, parent_symbol_id=symbol_id)

        return symbol_id

    def delete_file_symbols(self, file_id: int) -> int:
        """Delete all symbols for a file. Returns count deleted."""
        cursor = self.conn.execute(
            "DELETE FROM symbols WHERE file_id = ?", (file_id,)
        )
        return cursor.rowcount

    def get_symbol(self, symbol_id: str) -> Optional[StoredSymbol]:
        """Get a symbol by ID."""
        row = self.conn.execute(
            "SELECT * FROM symbols WHERE id = ?", (symbol_id,)
        ).fetchone()

        if not row:
            return None

        return self._row_to_symbol(row)

    def get_symbols_by_file(self, file_id: int) -> list[StoredSymbol]:
        """Get all symbols in a file."""
        rows = self.conn.execute(
            "SELECT * FROM symbols WHERE file_id = ?", (file_id,)
        ).fetchall()

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

    def get_symbols_by_name(self, name: str) -> list[StoredSymbol]:
        """Get all symbols with a given name."""
        rows = self.conn.execute(
            "SELECT * FROM symbols WHERE name = ?", (name,)
        ).fetchall()

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

    def get_symbols_by_kind(self, kind: str) -> list[StoredSymbol]:
        """Get all symbols of a given kind (class, function, method)."""
        rows = self.conn.execute(
            "SELECT * FROM symbols WHERE kind = ?", (kind,)
        ).fetchall()

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

    def search_symbols(self, query: str, limit: int = 50) -> list[StoredSymbol]:
        """Search symbols by name pattern."""
        rows = self.conn.execute(
            "SELECT * FROM symbols WHERE name LIKE ? LIMIT ?",
            (f"%{query}%", limit),
        ).fetchall()

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

    def _row_to_symbol(self, row: sqlite3.Row) -> StoredSymbol:
        """Convert a database row to a StoredSymbol."""
        extra_data = None
        if row["extra_data"]:
            try:
                extra_data = json.loads(row["extra_data"])
            except json.JSONDecodeError:
                pass

        return StoredSymbol(
            id=row["id"],
            name=row["name"],
            file_id=row["file_id"],
            kind=row["kind"],
            parent_symbol_id=row["parent_symbol_id"],
            exported=bool(row["exported"]),
            line_start=row["line_start"],
            line_end=row["line_end"],
            signature=row["signature"],
            docstring=row["docstring"],
            extra_data=extra_data,
        )
