from datetime import datetime, timezone
from typing import Optional, List
from enum import Enum
from pydantic import BaseModel

from common.database import DBConfig, Db
from common.logging import get_logger, span

logger = get_logger(__name__)


class TimestampType(str, Enum):
    """Types of timestamps supported."""

    CLIP_REQUEST = "clip_request"
    BOOKMARK = "bookmark"


# Query templates
GET_TIMESTAMPS_QUERY = """
    SELECT id, user_id, type, event_time, stream_started_at, stream_id, is_deleted
    FROM timestamps
    WHERE user_id = %s AND type = %s AND is_deleted = FALSE
    ORDER BY event_time DESC
"""

GET_TIMESTAMPS_BY_STREAM_QUERY = """
    SELECT id, user_id, type, event_time, stream_started_at, stream_id, is_deleted
    FROM timestamps
    WHERE user_id = %s AND type = %s AND stream_id = %s AND is_deleted = FALSE
    ORDER BY event_time DESC
"""

GET_ALL_TIMESTAMPS_QUERY = """
    SELECT id, user_id, type, event_time, stream_started_at, stream_id, is_deleted
    FROM timestamps
    WHERE user_id = %s AND is_deleted = FALSE
    ORDER BY event_time DESC
"""

MARK_DELETED_QUERY = """
    UPDATE timestamps
    SET is_deleted = TRUE
    WHERE id = %s AND user_id = %s
"""


class Timestamp(BaseModel):
    """Unified timestamp model for all timestamp types."""

    id: int
    user_id: int
    type: TimestampType
    event_time: datetime
    stream_started_at: datetime
    stream_id: Optional[int] = None
    is_deleted: bool


class ClipRequestTimestamp(BaseModel):
    """Legacy alias for backward compatibility with clip request timestamps."""

    id: int
    user_id: int
    event_time: datetime
    stream_started_at: datetime
    stream_id: Optional[int] = None

    @classmethod
    def from_timestamp(cls, ts: Timestamp) -> "ClipRequestTimestamp":
        """Convert a Timestamp to ClipRequestTimestamp."""
        return cls(
            id=ts.id,
            user_id=ts.user_id,
            event_time=ts.event_time,
            stream_started_at=ts.stream_started_at,
            stream_id=ts.stream_id,
        )


class StreamDeckSignalDAO:
    """Unified DAO for storing and retrieving all timestamp types."""

    def __init__(self, db: Optional[Db] = None):
        self.db = db if db else Db(DBConfig(pg_dsn=None))
        logger.debug("TimestampDAO initialized", extra={"component": "TimestampDAO"})

    @staticmethod
    def _ensure_utc(dt: datetime) -> datetime:
        """Ensure datetime is in UTC timezone."""
        if dt.tzinfo is None:
            return dt.replace(tzinfo=timezone.utc)
        return dt.astimezone(timezone.utc)

    async def create(
        self,
        user_id: int,
        timestamp_type: TimestampType,
        event_time: datetime,
        stream_started_at: datetime,
        stream_id: Optional[int] = None,
        is_deleted: bool = False,
    ) -> int:
        """
        Create a timestamp record.

        Args:
            user_id: The ID of the user.
            timestamp_type: The type of timestamp (e.g., clip_request, bookmark).
            event_time: The timestamp of the event in UTC.
            stream_started_at: The stream start time in UTC.
            stream_id: Optional ID of the associated stream.
            is_deleted: Whether the timestamp is marked as deleted.

        Returns:
            The ID of the inserted record.
        """
        event_time_utc = self._ensure_utc(event_time)
        stream_started_at_utc = self._ensure_utc(stream_started_at)

        with span(
            logger,
            "create_timestamp",
            {"user_id": user_id, "type": timestamp_type.value},
        ):
            columns = [
                "user_id",
                "type",
                "event_time",
                "stream_started_at",
                "stream_id",
                "is_deleted",
            ]
            values = [
                user_id,
                timestamp_type.value,
                event_time_utc,
                stream_started_at_utc,
                stream_id,
                is_deleted,
            ]

            try:
                timestamp_id = await self.db.insert(
                    table="timestamps", columns=columns, values=tuple(values)
                )

                if timestamp_id:
                    logger.info(
                        "Created timestamp record",
                        extra={
                            "id": timestamp_id,
                            "user_id": user_id,
                            "type": timestamp_type.value,
                            "event_time": event_time_utc.isoformat(),
                            "stream_started_at": stream_started_at_utc.isoformat(),
                            "stream_id": stream_id,
                            "is_deleted": is_deleted,
                        },
                    )
                    return timestamp_id

                logger.error(
                    f"Failed to create timestamp for user {user_id}",
                    extra={"user_id": user_id},
                )
                raise RuntimeError("Failed to insert timestamp record")

            except Exception as e:
                logger.error(
                    f"Error creating timestamp: {e}",
                    extra={"user_id": user_id, "error": str(e)},
                )
                raise

    async def get_by_user(
        self,
        user_id: int,
        timestamp_type: TimestampType,
    ) -> List[Timestamp]:
        """
        Retrieve all timestamps of a specific type for a user.

        Args:
            user_id: The ID of the user.
            timestamp_type: The type of timestamp to retrieve.

        Returns:
            A list of Timestamp records ordered by event_time descending.
        """
        with span(
            logger,
            "get_timestamps_by_user",
            {"user_id": user_id, "type": timestamp_type.value},
        ):
            rows = await self.db.fetch_all(
                GET_TIMESTAMPS_QUERY, (user_id, timestamp_type.value)
            )

            if not rows:
                logger.debug(
                    f"No {timestamp_type.value} found for user",
                    extra={"user_id": user_id},
                )
                return []

            timestamps = [
                Timestamp(
                    id=row[0],
                    user_id=row[1],
                    type=TimestampType(row[2]),
                    event_time=row[3],
                    stream_started_at=row[4],
                    stream_id=row[5],
                    is_deleted=row[6],
                )
                for row in rows
            ]

            logger.info(
                "Retrieved timestamps for user",
                extra={
                    "user_id": user_id,
                    "type": timestamp_type.value,
                    "count": len(timestamps),
                },
            )
            return timestamps

    async def get_by_user_and_stream(
        self,
        user_id: int,
        timestamp_type: TimestampType,
        stream_id: int,
    ) -> List[Timestamp]:
        """
        Retrieve all timestamps of a specific type for a user and stream.

        Args:
            user_id: The ID of the user.
            timestamp_type: The type of timestamp to retrieve.
            stream_id: The ID of the stream.

        Returns:
            A list of Timestamp records ordered by event_time descending.
        """
        with span(
            logger,
            "get_timestamps_by_user_and_stream",
            {"user_id": user_id, "type": timestamp_type.value, "stream_id": stream_id},
        ):
            rows = await self.db.fetch_all(
                GET_TIMESTAMPS_BY_STREAM_QUERY,
                (user_id, timestamp_type.value, stream_id),
            )

            if not rows:
                logger.debug(
                    f"No {timestamp_type.value} found for user and stream",
                    extra={"user_id": user_id, "stream_id": stream_id},
                )
                return []

            timestamps = [
                Timestamp(
                    id=row[0],
                    user_id=row[1],
                    type=TimestampType(row[2]),
                    event_time=row[3],
                    stream_started_at=row[4],
                    stream_id=row[5],
                    is_deleted=row[6],
                )
                for row in rows
            ]

            logger.info(
                "Retrieved timestamps for user and stream",
                extra={
                    "user_id": user_id,
                    "type": timestamp_type.value,
                    "stream_id": stream_id,
                    "count": len(timestamps),
                },
            )
            return timestamps

    async def get_all_by_user(self, user_id: int) -> List[Timestamp]:
        """
        Retrieve all timestamps of all types for a user.

        Args:
            user_id: The ID of the user.

        Returns:
            A list of all Timestamp records ordered by event_time descending.
        """
        with span(logger, "get_all_timestamps_by_user", {"user_id": user_id}):
            rows = await self.db.fetch_all(GET_ALL_TIMESTAMPS_QUERY, (user_id,))

            if not rows:
                logger.debug(
                    "No timestamps found for user",
                    extra={"user_id": user_id},
                )
                return []

            timestamps = [
                Timestamp(
                    id=row[0],
                    user_id=row[1],
                    type=TimestampType(row[2]),
                    event_time=row[3],
                    stream_started_at=row[4],
                    stream_id=row[5],
                    is_deleted=row[6],
                )
                for row in rows
            ]

            logger.info(
                "Retrieved all timestamps for user",
                extra={"user_id": user_id, "count": len(timestamps)},
            )
            return timestamps

    async def mark_deleted(self, timestamp_id: int, user_id: int) -> None:
        """
        Mark a timestamp as deleted.

        Args:
            timestamp_id: The ID of the timestamp to mark as deleted.
            user_id: The ID of the user (for authorization check).
        """
        with span(
            logger,
            "mark_timestamp_deleted",
            {"timestamp_id": timestamp_id, "user_id": user_id},
        ):
            await self.db.execute(MARK_DELETED_QUERY, (timestamp_id, user_id))
            logger.info(
                "Marked timestamp as deleted",
                extra={"timestamp_id": timestamp_id, "user_id": user_id},
            )

    # Legacy method for backward compatibility
    async def create_clip_request(
        self,
        user_id: int,
        event_time: datetime,
        stream_started_at: datetime,
        stream_id: Optional[int] = None,
        is_deleted: bool = False,
    ) -> int:
        """
        Legacy method: Create a clip request timestamp.

        Delegates to create() with TimestampType.CLIP_REQUEST.
        """
        return await self.create(
            user_id=user_id,
            timestamp_type=TimestampType.CLIP_REQUEST,
            event_time=event_time,
            stream_started_at=stream_started_at,
            stream_id=stream_id,
            is_deleted=is_deleted,
        )

    # Legacy method for backward compatibility
    async def get_clip_requests_by_user(
        self, user_id: int
    ) -> List[ClipRequestTimestamp]:
        """
        Legacy method: Retrieve all clip request timestamps for a user.

        Delegates to get_by_user() with TimestampType.CLIP_REQUEST,
        then converts to ClipRequestTimestamp for backward compatibility.
        """
        timestamps = await self.get_by_user(user_id, TimestampType.CLIP_REQUEST)
        return [ClipRequestTimestamp.from_timestamp(ts) for ts in timestamps]
