"""
SQLite backend implementation for InferenceKit.
Supports persistent storage using SQLite database.
"""

import sqlite3
import asyncio
from pathlib import Path
from typing import Optional, Dict, Any, List
from datetime import datetime
from uuid import UUID, uuid4

from ..types import (
    Request,
    Response,
    CacheEntry,
    ProviderConfig,
    CacheConfig,
)
from ..exceptions import BackendError, ConnectionError


class SQLiteBackend:
    """SQLite backend for persistent storage."""

    def __init__(self, cache_config: CacheConfig, provider_config: ProviderConfig):
        self.cache_config = cache_config
        self.provider_config = provider_config
        self.connection: Optional[sqlite3.Connection] = None
        self.database_path = Path(provider_config.base_url or "inferencekit.db")
        self.database_path.parent.mkdir(parents=True, exist_ok=True)

    async def initialize(self) -> None:
        """Initialize the SQLite database."""
        try:
            self.connection = sqlite3.connect(
                str(self.database_path),
                check_same_thread=False
            )
            self.connection.row_factory = sqlite3.Row
            self._create_tables()
        except Exception as e:
            raise ConnectionError(f"Failed to connect to SQLite database: {e}")

    def _create_tables(self) -> None:
        """Create database tables."""
        if not self.connection:
            return

        try:
            cursor = self.connection.cursor()

            # Create requests table
            cursor.execute("""
                CREATE TABLE IF NOT EXISTS requests (
                    id TEXT PRIMARY KEY,
                    model TEXT NOT NULL,
                    provider TEXT NOT NULL,
                    type TEXT NOT NULL,
                    prompt TEXT NOT NULL,
                    parameters TEXT,
                    metadata TEXT,
                    created_at TEXT NOT NULL
                )
            ")

            # Create responses table
            cursor.execute("""
                CREATE TABLE IF NOT EXISTS responses (
                    request_id TEXT PRIMARY KEY,
                    content TEXT NOT NULL,
                    metadata TEXT,
                    created_at TEXT NOT NULL,
                    FOREIGN KEY (request_id) REFERENCES requests (id) ON DELETE CASCADE
                )
            ")

            # Create cache table
            cursor.execute("""
                CREATE TABLE IF NOT EXISTS cache (
                    id INTEGER PRIMARY KEY AUTOINCREMENT,
                    request_id TEXT NOT NULL,
                    similarity_score REAL NOT NULL,
                    cached_at TEXT NOT NULL,
                    FOREIGN KEY (request_id) REFERENCES requests (id) ON DELETE CASCADE
                )
            ")

            # Create indexes
            cursor.execute("CREATE INDEX IF NOT EXISTS idx_requests_created_at ON requests (created_at)")
            cursor.execute("CREATE INDEX IF NOT EXISTS idx_cache_cached_at ON cache (cached_at)")

            self.connection.commit()
        except Exception as e:
            raise BackendError(f"Failed to create tables: {e}")

    async def shutdown(self) -> None:
        """Shutdown the SQLite connection."""
        if self.connection:
            self.connection.close()
            self.connection = None

    async def save_request(self, request: Request) -> None:
        """Save a request to SQLite."""
        if not self.connection:
            return

        try:
            cursor = self.connection.cursor()
            cursor.execute("""
                INSERT OR REPLACE INTO requests
                (id, model, provider, type, prompt, parameters, metadata, created_at)
                VALUES (?, ?, ?, ?, ?, ?, ?, ?)
            ", (
                str(request.id),
                request.model,
                request.provider.value,
                request.type.value,
                request.prompt,
                str(request.parameters),
                str(request.metadata),
                request.created_at.isoformat()
            ))
            self.connection.commit()
        except Exception as e:
            raise BackendError(f"Failed to save request: {e}")

    async def save_response(self, request_id: str, response: Response) -> None:
        """Save a response to SQLite."""
        if not self.connection:
            return

        try:
            cursor = self.connection.cursor()
            cursor.execute("""
                INSERT OR REPLACE INTO responses
                (request_id, content, metadata, created_at)
                VALUES (?, ?, ?, ?)
            ", (
                request_id,
                str(response.content),
                str(response.metadata),
                response.created_at.isoformat()
            ))
            self.connection.commit()
        except Exception as e:
            raise BackendError(f"Failed to save response: {e}")

    async def get_request(self, request_id: str) -> Optional[Request]:
        """Get a request from SQLite."""
        if not self.connection:
            return None

        try:
            cursor = self.connection.cursor()
            cursor.execute("SELECT * FROM requests WHERE id = ?", (request_id,))
            row = cursor.fetchone()

            if row:
                return Request(
                    id=UUID(row["id"]),
                    model=row["model"],
                    provider=row["provider"],
                    type=row["type"],
                    prompt=row["prompt"],
                    parameters=eval(row["parameters"]) if row["parameters"] else {},
                    metadata=eval(row["metadata"]) if row["metadata"] else {},
                    created_at=datetime.fromisoformat(row["created_at"])
                )
            return None
        except Exception as e:
            raise BackendError(f"Failed to get request: {e}")

    async def get_response(self, request_id: str) -> Optional[Response]:
        """Get a response from SQLite."""
        if not self.connection:
            return None

        try:
            cursor = self.connection.cursor()
            cursor.execute("SELECT * FROM responses WHERE request_id = ?", (request_id,))
            row = cursor.fetchone()

            if row:
                return Response(
                    request_id=UUID(row["request_id"]),
                    content=eval(row["content"]),
                    metadata=eval(row["metadata"]) if row["metadata"] else {},
                    created_at=datetime.fromisoformat(row["created_at"])
                )
            return None
        except Exception as e:
            raise BackendError(f"Failed to get response: {e}")

    async def delete_request(self, request_id: str) -> bool:
        """Delete a request from SQLite."""
        if not self.connection:
            return False

        try:
            cursor = self.connection.cursor()
            cursor.execute("DELETE FROM requests WHERE id = ?", (request_id,))
            self.connection.commit()
            return cursor.rowcount > 0
        except Exception as e:
            raise BackendError(f"Failed to delete request: {e}")

    async def delete_response(self, request_id: str) -> bool:
        """Delete a response from SQLite."""
        if not self.connection:
            return False

        try:
            cursor = self.connection.cursor()
            cursor.execute("DELETE FROM responses WHERE request_id = ?", (request_id,))
            self.connection.commit()
            return cursor.rowcount > 0
        except Exception as e:
            raise BackendError(f"Failed to delete response: {e}")

    async def list_requests(self, limit: int = 100, offset: int = 0) -> List[Dict[str, Any]]:
        """List all requests."""
        if not self.connection:
            return []

        try:
            cursor = self.connection.cursor()
            cursor.execute("""
                SELECT * FROM requests
                ORDER BY created_at DESC
                LIMIT ? OFFSET ?
            ", (limit, offset))
            rows = cursor.fetchall()

            return [dict(row) for row in rows]
        except Exception as e:
            raise BackendError(f"Failed to list requests: {e}")

    async def search_requests(self, query: str, limit: int = 10) -> List[Dict[str, Any]]:
        """Search requests by query."""
        if not self.connection:
            return []

        try:
            cursor = self.connection.cursor()
            cursor.execute("""
                SELECT * FROM requests
                WHERE prompt LIKE ?
                ORDER BY created_at DESC
                LIMIT ?
            ", (f"%{query}%", limit))
            rows = cursor.fetchall()

            return [dict(row) for row in rows]
        except Exception as e:
            raise BackendError(f"Failed to search requests: {e}")

    async def clear(self) -> None:
        """Clear all data from SQLite."""
        if not self.connection:
            return

        try:
            cursor = self.connection.cursor()
            cursor.execute("DELETE FROM cache")
            cursor.execute("DELETE FROM responses")
            cursor.execute("DELETE FROM requests")
            self.connection.commit()
        except Exception as e:
            raise BackendError(f"Failed to clear SQLite: {e}")

    async def get_stats(self) -> Dict[str, Any]:
        """Get SQLite statistics."""
        if not self.connection:
            return {}

        try:
            cursor = self.connection.cursor()

            # Get request count
            cursor.execute("SELECT COUNT(*) as count FROM requests")
            request_count = cursor.fetchone()[0]

            # Get response count
            cursor.execute("SELECT COUNT(*) as count FROM responses")
            response_count = cursor.fetchone()[0]

            # Get cache count
            cursor.execute("SELECT COUNT(*) as count FROM cache")
            cache_count = cursor.fetchone()[0]

            return {
                "requests": request_count,
                "responses": response_count,
                "cache_entries": cache_count,
                "database_size": self.database_path.stat().st_size,
                "database_path": str(self.database_path)
            }
        except Exception as e:
            raise BackendError(f"Failed to get stats: {e}")

    async def exists(self, request_id: str) -> bool:
        """Check if a request exists."""
        if not self.connection:
            return False

        try:
            cursor = self.connection.cursor()
            cursor.execute("SELECT 1 FROM requests WHERE id = ? LIMIT 1", (request_id,))
            return cursor.fetchone() is not None
        except Exception as e:
            raise BackendError(f"Failed to check existence: {e}")

    async def get_size(self) -> int:
        """Get the number of stored requests."""
        if not self.connection:
            return 0

        try:
            cursor = self.connection.cursor()
            cursor.execute("SELECT COUNT(*) as count FROM requests")
            return cursor.fetchone()[0]
        except Exception as e:
            raise BackendError(f"Failed to get size: {e}")

    def _dict_factory(self, cursor, row):
        """Convert database rows to dictionaries."""
        d = {}
        for idx, col in enumerate(cursor.description):
            d[col[0]] = row[idx]
        return d