import datetime
import uuid
from typing import Optional, Any

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


logger = get_logger(__name__)


class ExtensionHandoffDAO:
    """DAO for extension_handoffs table.

    States:
      0 = new
      1 = linked
      2 = twitch_connected
    """

    def __init__(self, db: Optional[Db] = None):
        self.db = db if db else Db(DBConfig())

    async def create(
        self, channel_id: str, twitch_login: Optional[str] = None, ttl_minutes: int = 10
    ) -> str:
        """Get or create a new handoff for the channel.

        Idempotent: if an active (status=0) handoff already exists for the channel,
        it will be returned. If it is expired, we renew its expires_at and return it.

        Returns handoff_id (uuid string).
        """
        # Try to find an existing active handoff for this channel
        row = await self.db.fetch_one(
            "SELECT id, expires_at, twitch_login FROM extension_handoffs\n"
            "WHERE channel_id = %s AND status = 0\n"
            "ORDER BY created_at DESC LIMIT 1",
            (channel_id,),
        )
        now = datetime.datetime.now(datetime.timezone.utc)
        new_expiry = now + datetime.timedelta(minutes=ttl_minutes)

        if row:
            existing_id = row[0]
            existing_expires = row[1]
            existing_login = row[2]
            # If expired, renew. Also update twitch_login if newly provided.
            set_cols = []
            set_vals = []
            if existing_expires is None or existing_expires < now:
                set_cols.append("expires_at")
                set_vals.append(new_expiry)
            if twitch_login and twitch_login != existing_login:
                set_cols.append("twitch_login")
                set_vals.append(twitch_login)
            if set_cols:
                await self.db.update(
                    table="extension_handoffs",
                    set_columns=set_cols,
                    set_values=set_vals,
                    where_clause="id = %s",
                    where_params=(existing_id,),
                )
            return str(existing_id)

        # No existing active record → create a new one
        handoff_id = str(uuid.uuid4())
        await self.db.insert(
            table="extension_handoffs",
            columns=[
                "id",
                "channel_id",
                "status",
                "user_id",
                "twitch_login",
                "last_error",
                "created_at",
                "expires_at",
            ],
            values=(
                handoff_id,
                channel_id,
                0,
                None,
                twitch_login,
                None,
                now,
                new_expiry,
            ),
        )
        return str(handoff_id)

    async def get(self, handoff_id: str) -> Optional[tuple[Any, ...]]:
        row = await self.db.fetch_one(
            "SELECT id, channel_id, status, user_id, twitch_login, last_error, created_at, expires_at\n"
            "FROM extension_handoffs WHERE id = %s",
            (handoff_id,),
        )
        return row

    async def mark_linked(self, handoff_id: str, user_id: int) -> None:
        await self.db.update(
            table="extension_handoffs",
            set_columns=["status", "user_id"],
            set_values=[1, user_id],
            where_clause="id = %s",
            where_params=(handoff_id,),
        )

    async def mark_twitch_connected(self, handoff_id: str) -> None:
        await self.db.update(
            table="extension_handoffs",
            set_columns=["status"],
            set_values=[2],
            where_clause="id = %s",
            where_params=(handoff_id,),
        )

    async def set_error(self, handoff_id: str, message: str) -> None:
        await self.db.update(
            table="extension_handoffs",
            set_columns=["last_error"],
            set_values=[message[:1000]],
            where_clause="id = %s",
            where_params=(handoff_id,),
        )

    async def get_last_linked(self, channel_id: str) -> Optional[str]:
        """Return the most recent linked or completed handoff id for a channel.

        Linked states are status in (1, 2): 1=linked, 2=twitch_connected.
        """
        row = await self.db.fetch_one(
            "SELECT id FROM extension_handoffs\n"
            "WHERE channel_id = %s AND status IN (1, 2)\n"
            "ORDER BY created_at DESC LIMIT 1",
            (channel_id,),
        )
        if not row:
            return None
        # fetch_one returns a row tuple where id is at index 0
        return str(row[0])
