"""Chat history persistence."""

from __future__ import annotations

import json
import sqlite3
import uuid
from datetime import datetime, timezone
from pathlib import Path
from typing import Any

from oclawma.web.models import ChatMessage, ConversationSummary, MessageRole


class ChatHistory:
    """Manages chat message history persistence using SQLite."""

    def __init__(self, db_path: str | Path = ":memory:") -> None:
        """Initialize chat history.

        Args:
            db_path: Path to SQLite database file, or :memory: for in-memory.
        """
        self.db_path = str(db_path)
        self._memory_conn: sqlite3.Connection | None = None
        self._init_db()

    def _get_connection(self) -> sqlite3.Connection:
        """Get database connection.

        For in-memory databases, returns the persistent connection.
        For file databases, creates a new connection each time.
        """
        if self.db_path == ":memory:":
            if self._memory_conn is None:
                self._memory_conn = sqlite3.connect(self.db_path)
            return self._memory_conn
        return sqlite3.connect(self.db_path)

    def _close_if_file(self, conn: sqlite3.Connection) -> None:
        """Close connection if it's a file database."""
        if self.db_path != ":memory:":
            conn.close()

    def _init_db(self) -> None:
        """Initialize database schema."""
        conn = self._get_connection()
        try:
            conn.execute(
                """
                CREATE TABLE IF NOT EXISTS conversations (
                    id TEXT PRIMARY KEY,
                    title TEXT NOT NULL DEFAULT 'New Conversation',
                    created_at TEXT NOT NULL,
                    updated_at TEXT NOT NULL
                )
            """
            )
            conn.execute(
                """
                CREATE TABLE IF NOT EXISTS messages (
                    id TEXT PRIMARY KEY,
                    conversation_id TEXT NOT NULL,
                    role TEXT NOT NULL,
                    content TEXT NOT NULL,
                    timestamp TEXT NOT NULL,
                    metadata TEXT DEFAULT '{}',
                    FOREIGN KEY (conversation_id) REFERENCES conversations(id) ON DELETE CASCADE
                )
            """
            )
            conn.execute(
                """
                CREATE INDEX IF NOT EXISTS idx_messages_conversation
                ON messages(conversation_id, timestamp)
            """
            )
            conn.commit()
        finally:
            self._close_if_file(conn)

    def create_conversation(self, title: str = "New Conversation") -> str:
        """Create a new conversation.

        Args:
            title: Conversation title.

        Returns:
            Conversation ID.
        """
        conversation_id = str(uuid.uuid4())
        now = datetime.now(timezone.utc).isoformat()
        conn = self._get_connection()
        try:
            conn.execute(
                "INSERT INTO conversations (id, title, created_at, updated_at) VALUES (?, ?, ?, ?)",
                (conversation_id, title, now, now),
            )
            conn.commit()
        finally:
            self._close_if_file(conn)
        return conversation_id

    def get_conversation(self, conversation_id: str) -> dict[str, Any] | None:
        """Get conversation details.

        Args:
            conversation_id: Conversation ID.

        Returns:
            Conversation data or None if not found.
        """
        conn = self._get_connection()
        try:
            conn.row_factory = sqlite3.Row
            row = conn.execute(
                "SELECT * FROM conversations WHERE id = ?",
                (conversation_id,),
            ).fetchone()
            if row:
                return dict(row)
            return None
        finally:
            self._close_if_file(conn)

    def update_conversation_title(self, conversation_id: str, title: str) -> bool:
        """Update conversation title.

        Args:
            conversation_id: Conversation ID.
            title: New title.

        Returns:
            True if updated, False if not found.
        """
        now = datetime.now(timezone.utc).isoformat()
        conn = self._get_connection()
        try:
            cursor = conn.execute(
                "UPDATE conversations SET title = ?, updated_at = ? WHERE id = ?",
                (title, now, conversation_id),
            )
            conn.commit()
            return cursor.rowcount > 0
        finally:
            self._close_if_file(conn)

    def list_conversations(self, limit: int = 50, offset: int = 0) -> list[ConversationSummary]:
        """List conversations.

        Args:
            limit: Maximum number of results.
            offset: Offset for pagination.

        Returns:
            List of conversation summaries.
        """
        conn = self._get_connection()
        try:
            conn.row_factory = sqlite3.Row
            rows = conn.execute(
                """
                SELECT c.*, COUNT(m.id) as message_count
                FROM conversations c
                LEFT JOIN messages m ON c.id = m.conversation_id
                GROUP BY c.id
                ORDER BY c.updated_at DESC
                LIMIT ? OFFSET ?
                """,
                (limit, offset),
            ).fetchall()

            return [
                ConversationSummary(
                    id=row["id"],
                    title=row["title"],
                    message_count=row["message_count"],
                    created_at=datetime.fromisoformat(row["created_at"]),
                    updated_at=datetime.fromisoformat(row["updated_at"]),
                )
                for row in rows
            ]
        finally:
            self._close_if_file(conn)

    def add_message(
        self,
        conversation_id: str,
        role: MessageRole,
        content: str,
        metadata: dict[str, Any] | None = None,
    ) -> ChatMessage:
        """Add a message to a conversation.

        Args:
            conversation_id: Conversation ID.
            role: Message role.
            content: Message content.
            metadata: Optional metadata.

        Returns:
            Created message.
        """
        message_id = str(uuid.uuid4())
        timestamp = datetime.now(timezone.utc)
        meta_json = json.dumps(metadata or {})

        conn = self._get_connection()
        try:
            # Ensure conversation exists
            exists = conn.execute(
                "SELECT 1 FROM conversations WHERE id = ?",
                (conversation_id,),
            ).fetchone()
            if not exists:
                raise ValueError(f"Conversation {conversation_id} not found")

            conn.execute(
                """
                INSERT INTO messages (id, conversation_id, role, content, timestamp, metadata)
                VALUES (?, ?, ?, ?, ?, ?)
                """,
                (
                    message_id,
                    conversation_id,
                    role.value,
                    content,
                    timestamp.isoformat(),
                    meta_json,
                ),
            )
            # Update conversation timestamp
            conn.execute(
                "UPDATE conversations SET updated_at = ? WHERE id = ?",
                (timestamp.isoformat(), conversation_id),
            )
            conn.commit()
        finally:
            self._close_if_file(conn)

        return ChatMessage(
            id=message_id,
            role=role,
            content=content,
            timestamp=timestamp,
            metadata=metadata or {},
        )

    def get_messages(
        self,
        conversation_id: str,
        limit: int = 100,
        offset: int = 0,
    ) -> list[ChatMessage]:
        """Get messages from a conversation.

        Args:
            conversation_id: Conversation ID.
            limit: Maximum number of results.
            offset: Offset for pagination.

        Returns:
            List of messages.
        """
        conn = self._get_connection()
        try:
            conn.row_factory = sqlite3.Row
            rows = conn.execute(
                """
                SELECT * FROM messages
                WHERE conversation_id = ?
                ORDER BY timestamp ASC
                LIMIT ? OFFSET ?
                """,
                (conversation_id, limit, offset),
            ).fetchall()

            return [
                ChatMessage(
                    id=row["id"],
                    role=MessageRole(row["role"]),
                    content=row["content"],
                    timestamp=datetime.fromisoformat(row["timestamp"]),
                    metadata=json.loads(row["metadata"]),
                )
                for row in rows
            ]
        finally:
            self._close_if_file(conn)

    def delete_conversation(self, conversation_id: str) -> bool:
        """Delete a conversation and all its messages.

        Args:
            conversation_id: Conversation ID.

        Returns:
            True if deleted, False if not found.
        """
        conn = self._get_connection()
        try:
            cursor = conn.execute(
                "DELETE FROM conversations WHERE id = ?",
                (conversation_id,),
            )
            conn.commit()
            return cursor.rowcount > 0
        finally:
            self._close_if_file(conn)

    def clear_conversation(self, conversation_id: str) -> bool:
        """Clear all messages from a conversation.

        Args:
            conversation_id: Conversation ID.

        Returns:
            True if conversation exists and messages cleared.
        """
        conn = self._get_connection()
        try:
            # Check if conversation exists
            exists = conn.execute(
                "SELECT 1 FROM conversations WHERE id = ?",
                (conversation_id,),
            ).fetchone()
            if not exists:
                return False

            conn.execute(
                "DELETE FROM messages WHERE conversation_id = ?",
                (conversation_id,),
            )
            now = datetime.now(timezone.utc).isoformat()
            conn.execute(
                "UPDATE conversations SET updated_at = ? WHERE id = ?",
                (now, conversation_id),
            )
            conn.commit()
            return True
        finally:
            self._close_if_file(conn)
