import json
from datetime import datetime, timezone
from typing import Annotated, Any, Optional
from uuid import UUID

from fastapi import APIRouter, Depends, HTTPException, Request
from pydantic import BaseModel, EmailStr

import reflector.auth as auth
from reflector.dailyco_api import RecordingType
from reflector.dailyco_api.client import DailyApiError
from reflector.db.meetings import (
    MeetingConsent,
    meeting_consent_controller,
    meetings_controller,
)
from reflector.db.rooms import rooms_controller
from reflector.logger import logger
from reflector.utils.string import NonEmptyString
from reflector.video_platforms.factory import create_platform_client

router = APIRouter()


class MeetingConsentRequest(BaseModel):
    consent_given: bool


@router.post("/meetings/{meeting_id}/consent")
async def meeting_audio_consent(
    meeting_id: str,
    request: MeetingConsentRequest,
    user_request: Request,
    user: Annotated[Optional[auth.UserInfo], Depends(auth.current_user_optional)],
):
    meeting = await meetings_controller.get_by_id(meeting_id)
    if not meeting:
        raise HTTPException(status_code=404, detail="Meeting not found")

    user_id = user["sub"] if user else None

    consent = MeetingConsent(
        meeting_id=meeting_id,
        user_id=user_id,
        consent_given=request.consent_given,
        consent_timestamp=datetime.now(timezone.utc),
    )

    updated_consent = await meeting_consent_controller.upsert(consent)

    return {"status": "success", "consent_id": updated_consent.id}


@router.patch("/meetings/{meeting_id}/deactivate")
async def meeting_deactivate(
    meeting_id: str,
    user: Annotated[Optional[auth.UserInfo], Depends(auth.current_user)],
):
    user_id = user["sub"] if user else None
    if not user_id:
        raise HTTPException(status_code=401, detail="Authentication required")

    meeting = await meetings_controller.get_by_id(meeting_id)
    if not meeting:
        raise HTTPException(status_code=404, detail="Meeting not found")

    if not meeting.is_active:
        return {"status": "success", "meeting_id": meeting_id}

    # Only room owner or meeting creator can deactivate
    room = await rooms_controller.get_by_id(meeting.room_id)
    if not room:
        raise HTTPException(status_code=404, detail="Room not found")

    if user_id != room.user_id and user_id != meeting.user_id:
        raise HTTPException(
            status_code=403, detail="Only the room owner can deactivate meetings"
        )

    await meetings_controller.update_meeting(meeting_id, is_active=False)

    return {"status": "success", "meeting_id": meeting_id}


class StartRecordingRequest(BaseModel):
    type: RecordingType
    instanceId: UUID


@router.post("/meetings/{meeting_id}/recordings/start")
async def start_recording(
    meeting_id: NonEmptyString,
    body: StartRecordingRequest,
    user: Annotated[Optional[auth.UserInfo], Depends(auth.current_user_optional)],
) -> dict[str, Any]:
    """Start cloud or raw-tracks recording via Daily.co REST API.

    Both cloud and raw-tracks are started via REST API to bypass enable_recording limitation of allowing only 1 recording at a time.
    Uses different instanceIds for cloud vs raw-tracks (same won't work)
    """
    meeting = await meetings_controller.get_by_id(meeting_id)
    if not meeting:
        raise HTTPException(status_code=404, detail="Meeting not found")

    log = logger.bind(
        meeting_id=meeting_id,
        room_name=meeting.room_name,
        recording_type=body.type,
        instance_id=body.instanceId,
    )

    try:
        client = create_platform_client("daily")
        result = await client.start_recording(
            room_name=meeting.room_name,
            recording_type=body.type,
            instance_id=body.instanceId,
        )

        log.info(f"Started {body.type} recording via REST API")

        return {"status": "ok", "result": result}

    except DailyApiError as e:
        # Parse Daily.co error response to detect "has an active stream"
        try:
            error_body = json.loads(e.response_body)
            error_info = error_body.get("info", "")

            # "has an active stream" means recording already started by another participant
            # This is SUCCESS from business logic perspective - return 200
            if "has an active stream" in error_info:
                log.info(
                    f"{body.type} recording already active (started by another participant)"
                )
                return {"status": "already_active", "instanceId": str(body.instanceId)}
        except (json.JSONDecodeError, KeyError):
            pass  # Fall through to error handling

        # All other Daily.co API errors
        log.error(f"Failed to start {body.type} recording", error=str(e))
        raise HTTPException(
            status_code=500, detail=f"Failed to start recording: {str(e)}"
        )

    except Exception as e:
        # Non-Daily.co errors
        log.error(f"Failed to start {body.type} recording", error=str(e))
        raise HTTPException(
            status_code=500, detail=f"Failed to start recording: {str(e)}"
        )


class AddEmailRecipientRequest(BaseModel):
    email: EmailStr


@router.post("/meetings/{meeting_id}/email-recipient")
async def add_email_recipient(
    meeting_id: str,
    request: AddEmailRecipientRequest,
    user: Annotated[Optional[auth.UserInfo], Depends(auth.current_user_optional)],
):
    """Add an email address to receive the transcript link when processing completes."""
    meeting = await meetings_controller.get_by_id(meeting_id)
    if not meeting:
        raise HTTPException(status_code=404, detail="Meeting not found")

    include_link = user is not None
    recipients = await meetings_controller.add_email_recipient(
        meeting_id, request.email, include_link=include_link
    )

    return {"status": "success", "email_recipients": recipients}
