from typing import Optional
from sqlalchemy.exc import IntegrityError
from sqlalchemy.ext.asyncio import AsyncSession
from fastapi import APIRouter, Depends, HTTPException, Query, status

from src.core.database import get_db
from src.core.auth import require_enterprise_or_front
from src.services.comandos import ComandoScreenshotService
from src.schemas.comandos import (
    ComandoScreenshotCreate,
    ComandoScreenshotUpdate,
    ComandoScreenshotResponse,
)
from src.core.api_responses import (
    build_success_payload,
    to_schema_dict,
    SuccessResponse,
    ErrorResponse,
)

router = APIRouter(
    prefix="/comandos/screenshot",
    tags=["Comandos Screenshot"],
)

comando_service = ComandoScreenshotService()


def _get_constraint_error_message(error: IntegrityError) -> str:
    error_parts: list[str] = []
    if hasattr(error, "orig"):
        try:
            orig_args = getattr(error.orig, "args", None)
            if orig_args:
                error_parts.extend([str(a) for a in orig_args])
        except Exception:
            pass
        try:
            error_parts.append(str(error.orig))
        except Exception:
            pass
    try:
        error_parts.append(str(error))
    except Exception:
        pass

    error_str_lower = " ".join(error_parts).lower()

    if (
        "fk_comandos_computadores" in error_str_lower
        or "foreign key" in error_str_lower
        or "computador_id" in error_str_lower
    ):
        return (
            "computador_id não encontrado em computadoresOnline "
            "ou inválido para criação do comando."
        )

    return "Conflito ao criar comando de screenshot."


@router.post(
    "/criar",
    summary="Criar comando de screenshot",
    description=(
        "Cria um novo comando de screenshot para um computador.\n\n"
        "Se houver um comando pendente para o computador, ele será "
        "automaticamente marcado como cancelado.\n\n"
        "O novo comando ficará com status 'pending' até que o cliente "
        "o execute.\n\n"
        "Requer token válido."),
    status_code=status.HTTP_201_CREATED,
    response_model=SuccessResponse,
    responses={
        201: {
            "description": "Comando criado com sucesso"},
        409: {
            "description": "Conflito de integridade no banco",
            "model": ErrorResponse},
        401: {
            "description": "Credenciais inválidas ou ausentes",
            "model": ErrorResponse},
        500: {
            "description": "Erro interno",
            "model": ErrorResponse},
    })
async def criar_comando(
    payload: ComandoScreenshotCreate,
    db: AsyncSession = Depends(get_db),
    _auth=Depends(require_enterprise_or_front),
):
    try:
        comando = await comando_service.criar_comando(db, payload)
    except IntegrityError as e:
        await db.rollback()
        error_message = _get_constraint_error_message(e)
        raise HTTPException(
            status_code=status.HTTP_409_CONFLICT,
            detail=error_message,
        ) from e

    data = to_schema_dict(ComandoScreenshotResponse, comando)
    message = (
        "Comando de screenshot criado com sucesso "
        f"(ID: {data.get('id')})"
    )
    return build_success_payload(data=data, message=message)


@router.get(
    "/pendente",
    summary="Obter comando pendente",
    description=(
        "Retorna o comando de screenshot pendente mais antigo para um "
        "computador.\n\n"
        "Usado pelo cliente para verificar se há comandos a "
        "executar.\n\n"
        "Requer token válido."),
    response_model=SuccessResponse,
    responses={
        200: {
            "description": (
                "Comando pendente ou mensagem de que não há comandos"
            )},
        401: {
            "description": "Credenciais inválidas ou ausentes",
            "model": ErrorResponse},
        500: {
            "description": "Erro interno",
            "model": ErrorResponse},
    })
async def obter_comando_pendente(
    computador_id: str = Query(..., description="ID do computador"),
    db: AsyncSession = Depends(get_db),
    _auth=Depends(require_enterprise_or_front),
):
    comando = await comando_service.obter_comando_pendente(db, computador_id)

    if not comando:
        return build_success_payload(message="Sem comandos pendentes")

    data = to_schema_dict(ComandoScreenshotResponse, comando)
    return build_success_payload(
        data=data, message="Comando pendente encontrado")


@router.delete(
    "/cancelar/{solicitante}",
    summary="Cancelar comandos pendentes por usuário",
    description=(
        "Cancela todos os comandos de screenshot com status 'pending' "
        "de um solicitante específico.\n\n"
        "Requer token válido."
    ),
    response_model=SuccessResponse,
    responses={
        200: {"description": "Cancelamento processado com sucesso"},
        401: {
            "description": "Credenciais inválidas ou ausentes",
            "model": ErrorResponse,
        },
        500: {"description": "Erro interno", "model": ErrorResponse},
    },
)
async def cancelar_comandos_por_solicitante(
    solicitante: str,
    db: AsyncSession = Depends(get_db),
    _auth=Depends(require_enterprise_or_front),
):
    qtd_cancelados = await comando_service.cancelar_pendentes_por_solicitante(
        db,
        solicitante,
    )

    data = {
        "solicitante": solicitante,
        "cancelados": qtd_cancelados,
    }

    if qtd_cancelados == 0:
        message = "Nenhum comando pendente encontrado para cancelamento"
    elif qtd_cancelados == 1:
        message = "1 comando pendente cancelado"
    else:
        message = f"{qtd_cancelados} comandos pendentes cancelados"

    return build_success_payload(data=data, message=message)


@router.get(
    "/{comando_id}",
    summary="Obter comando por ID",
    description=(
        "Retorna os dados de um comando de screenshot específico.\n\n"
        "Usado pelo frontend para verificar o status e resultado.\n\n"
        "Requer token válido."),
    response_model=SuccessResponse,
    responses={
        200: {
            "description": "Dados do comando"},
        404: {
            "description": "Comando não encontrado",
            "model": ErrorResponse},
        401: {
            "description": "Credenciais inválidas ou ausentes",
            "model": ErrorResponse},
        500: {
            "description": "Erro interno",
            "model": ErrorResponse},
    })
async def obter_comando(
    comando_id: int,
    db: AsyncSession = Depends(get_db),
    _auth=Depends(require_enterprise_or_front),
):
    comando = await comando_service.obter_comando(db, comando_id)

    if not comando:
        raise HTTPException(
            status_code=404,
            detail=f"Comando {comando_id} não encontrado"
        )

    data = to_schema_dict(ComandoScreenshotResponse, comando)
    return build_success_payload(data=data, message="Comando encontrado")


@router.put(
    "/{comando_id}/atualizar",
    summary="Atualizar comando",
    description=(
        "Atualiza o status e dados de um comando de screenshot.\n\n"
        "Usado pelo cliente para reportar o resultado da execução.\n\n"
        "Requer token válido."),
    response_model=SuccessResponse,
    responses={
        200: {
            "description": "Comando atualizado com sucesso"},
        404: {
            "description": "Comando não encontrado",
            "model": ErrorResponse},
        401: {
            "description": "Credenciais inválidas ou ausentes",
            "model": ErrorResponse},
        500: {
            "description": "Erro interno",
            "model": ErrorResponse},
    })
async def atualizar_comando(
    comando_id: int,
    payload: ComandoScreenshotUpdate,
    db: AsyncSession = Depends(get_db),
    _auth=Depends(require_enterprise_or_front),
):
    comando = await comando_service.atualizar_comando(db, comando_id, payload)

    if not comando:
        raise HTTPException(
            status_code=404,
            detail=f"Comando '{comando_id}' não encontrado"
        )

    data = to_schema_dict(ComandoScreenshotResponse, comando)
    message = f"Comando atualizado com sucesso (Status: {data.get('status')})"
    return build_success_payload(data=data, message=message)


@router.get(
    "",
    summary="Listar comandos",
    description=(
        "Lista comandos de screenshot com filtros opcionais.\n\n"
        "Requer token válido."
    ),
    response_model=SuccessResponse,
    responses={
        200: {"description": "Lista de comandos"},
        401: {
            "description": "Credenciais inválidas ou ausentes",
            "model": ErrorResponse},
        500: {"description": "Erro interno", "model": ErrorResponse},
    }
)
async def listar_comandos(
    computador_id: Optional[str] = Query(
        None,
        description="Filtrar por ID do computador",
    ),
    status_filter: Optional[str] = Query(
        None,
        alias="status",
        description="Filtrar por status (pending, completed, error, canceled)",
    ),
    db: AsyncSession = Depends(get_db),
    _auth=Depends(require_enterprise_or_front),
):
    comandos = await comando_service.listar_comandos(
        db,
        computador_id=computador_id,
        status=status_filter,
    )

    data = [to_schema_dict(ComandoScreenshotResponse, cmd) for cmd in comandos]
    qtd = len(data)

    if qtd == 0:
        message = "Nenhum comando encontrado"
    elif qtd == 1:
        message = "1 comando encontrado"
    else:
        message = f"{qtd} comandos encontrados"

    return build_success_payload(data=data, message=message)
