import uuid
from datetime import datetime
from typing import Dict, List, Optional

import psycopg2
# from psycopg2 import sql
from psycopg2.extras import RealDictCursor


class PostgreSQLManager:
    def __init__(self, dbname: str, user: str, password: str, host: str = "localhost", port: int = 5432):
        """Initialize the PostgreSQLManager with a connection to the database.

        Args:
            dbname (str): Name of the PostgreSQL database.
            user (str): Database user.
            password (str): Database password.
            host (str): Database host. Defaults to "localhost".
            port (int): Database port. Defaults to 5432.
        """
        self.connection = psycopg2.connect(dbname=dbname, user=user, password=password, host=host, port=port)
        self._create_history_table()

    def _create_history_table(self) -> None:
        """Create the history table if it does not exist."""
        with self.connection.cursor() as cursor:
            cursor.execute("""
                CREATE TABLE IF NOT EXISTS history (
                    id UUID PRIMARY KEY,
                    memory_id TEXT,
                    old_memory TEXT,
                    new_memory TEXT,
                    new_value TEXT,
                    event TEXT,
                    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
                    updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
                    is_deleted INTEGER
                );
            """)
            self.connection.commit()
            cursor.close()

    def add_history(
        self,
        memory_id: str,
        old_memory: str,
        new_memory: str,
        event: str,
        created_at: Optional[datetime] = None,
        updated_at: Optional[datetime] = None,
        is_deleted: int = 0,
    ) -> None:
        """Add a new history entry to the database.

        Args:
            memory_id (str): The ID of the memory being tracked.
            old_memory (str): The previous state of the memory.
            new_memory (str): The new state of the memory.
            event (str): The event that triggered the change.
            created_at (Optional[datetime]): The creation timestamp. Defaults to current timestamp.
            updated_at (Optional[datetime]): The update timestamp. Defaults to current timestamp.
            is_deleted (int): Flag indicating if the entry is deleted. Defaults to 0.
        """
        with self.connection.cursor() as cursor:
            cursor.execute(
                """
                INSERT INTO history (id, memory_id, old_memory, new_memory, event, created_at, updated_at, is_deleted)
                VALUES (%s, %s, %s, %s, %s, %s, %s, %s);
            """,
                (
                    str(uuid.uuid4()),
                    memory_id,
                    old_memory,
                    new_memory,
                    event,
                    created_at or datetime.now(),
                    updated_at or datetime.now(),
                    is_deleted,
                ),
            )
            self.connection.commit()
            cursor.close()

    def get_history(self, memory_id: str) -> List[Dict[str, str]]:
        """Retrieve the history entries for a specific memory ID.

        Args:
            memory_id (str): The ID of the memory to retrieve history for.

        Returns:
            List[Dict[str, str]]: A list of history entries as dictionaries.
        """
        with self.connection.cursor(cursor_factory=RealDictCursor) as cursor:
            cursor.execute(
                """
                SELECT id, memory_id, old_memory, new_memory, event, created_at, updated_at
                FROM history
                WHERE memory_id = %s
                ORDER BY updated_at ASC;
            """,
                (memory_id,),
            )
            self.connection.commit()
            rows = cursor.fetchall()
            cursor.close()
            return rows

    def reset(self) -> None:
        """Reset the history table by dropping and recreating it."""
        with self.connection.cursor() as cursor:
            cursor.execute("DROP TABLE IF EXISTS history;")
            self.connection.commit()
            cursor.close()
            self._create_history_table()

    def close(self) -> None:
        """Close the database connection."""
        self.connection.close()
