from __future__ import annotations

import asyncio
from typing import Any, Dict

from appwrite.exception import AppwriteException
from fastapi import Depends, HTTPException, Request, status
from jwt import PyJWTError

from src.core.appwrite_client import get_appwrite_clients
from src.core.authz import assert_user_in_company
from src.core.auth import parse_token
from src.core.config import get_settings
from src.core.appwrite_endpoint import get_normalized_appwrite_endpoint
from src.modules.auth.service import AuthService
from src.modules.carrinhos.service import CarrinhosService
from src.modules.categorias.service import CategoriasService
from src.modules.empresas.service import EmpresasService
from src.modules.empresas_dados.service import EmpresasDadosService
from src.modules.empresas_socios.service import EmpresasSociosService
from src.modules.enderecos.service import EnderecosService
from src.modules.entregas.service import EntregasService
from src.modules.faturas.service import FaturasService
from src.modules.laboratorios.service import LaboratoriosService
from src.modules.pagamentos.service import PagamentosService
from src.modules.pagamentos.mercadopago_service import MercadoPagoService
from src.modules.pedidos.service import PedidosService
from src.modules.produtos_catalogo.service import ProdutosCatalogoService
from src.modules.produtos_venda.service import ProdutosVendaService
from src.modules.produtos_estoque.service import ProdutosEstoqueService
from src.modules.usuario_empresa_perfis.service import UsuarioEmpresaPerfisService
from src.modules.subcategorias.service import SubcategoriasService
from src.modules.usuarios.service import UsuariosService
from src.modules.usuarios_data.service import UsuariosDataService
from src.modules.usuarios_perfil.service import UsuariosPerfilService


def require_api_key() -> None:
    """Mantido por compatibilidade com rotas existentes. Segurança via middleware."""
    return None


async def get_current_user_or_401(request: Request) -> Dict[str, Any]:
    settings = get_settings()

    token: str | None = None
    auth_header = request.headers.get("Authorization")
    if auth_header:
        scheme, _, credentials = auth_header.partition(" ")
        if scheme.lower() == "bearer" and credentials:
            token = credentials.strip()

    if not token:
        token = request.cookies.get(settings.session_cookie_name)

    if not token:
        raise HTTPException(status_code=status.HTTP_401_UNAUTHORIZED, detail="Not authenticated")

    try:
        payload = parse_token(token, settings.jwt_secret, settings.jwt_algorithm)
    except PyJWTError as exc:  # pragma: no cover - defensive
        raise HTTPException(status_code=status.HTTP_401_UNAUTHORIZED, detail="Invalid token") from exc

    sub = payload.get("sub")
    if not isinstance(sub, str) or not sub:
        raise HTTPException(status_code=status.HTTP_401_UNAUTHORIZED, detail="Invalid token payload")

    _, databases = get_appwrite_clients()

    try:
        document = await asyncio.to_thread(
            databases.get_document,
            settings.appwrite_database_id,
            settings.appwrite_collection_usuarios_id,
            sub,
        )
    except AppwriteException as exc:  # pragma: no cover - runtime guard
        if exc.code == 404:
            raise HTTPException(status_code=status.HTTP_401_UNAUTHORIZED, detail="User not found") from exc
        raise HTTPException(status_code=exc.code or status.HTTP_500_INTERNAL_SERVER_ERROR, detail=exc.message) from exc

    service = AuthService(
        databases=databases,
        database_id=settings.appwrite_database_id,
        collection_id=settings.appwrite_collection_usuarios_id,
    )

    return await service._run(service._map_user, document)


require_user = get_current_user_or_401
require_user_dependency = Depends(get_current_user_or_401)


def get_categorias_service() -> CategoriasService:
    settings = get_settings()
    _, databases = get_appwrite_clients()
    return CategoriasService(
        databases=databases,
        database_id=settings.appwrite_database_id,
        collection_id=settings.appwrite_collection_categorias_id,
    )


def get_produtos_catalogo_service() -> ProdutosCatalogoService:
    settings = get_settings()
    _, databases = get_appwrite_clients()
    return ProdutosCatalogoService(
        databases=databases,
        database_id=settings.appwrite_database_id,
        collection_id=settings.appwrite_collection_produtos_catalogo_id,
        laboratorios_collection_id=settings.appwrite_collection_laboratorios_id,
        categorias_collection_id=settings.appwrite_collection_categorias_id,
        subcategorias_collection_id=settings.appwrite_collection_subcategorias_id,
    )


def get_carrinhos_service() -> CarrinhosService:
    settings = get_settings()
    _, databases = get_appwrite_clients()
    return CarrinhosService(
        databases=databases,
        database_id=settings.appwrite_database_id,
        collection_id=settings.appwrite_collection_carrinhos_id,
    )


def get_subcategorias_service() -> SubcategoriasService:
    settings = get_settings()
    _, databases = get_appwrite_clients()
    return SubcategoriasService(
        databases=databases,
        database_id=settings.appwrite_database_id,
        collection_id=settings.appwrite_collection_subcategorias_id,
    )


def get_usuarios_service() -> UsuariosService:
    settings = get_settings()
    _, databases = get_appwrite_clients()
    return UsuariosService(
        databases=databases,
        database_id=settings.appwrite_database_id,
        collection_id=settings.appwrite_collection_usuarios_id,
    )


def get_usuarios_data_service() -> UsuariosDataService:
    settings = get_settings()
    _, databases = get_appwrite_clients()
    return UsuariosDataService(
        databases=databases,
        database_id=settings.appwrite_database_id,
        collection_id=settings.appwrite_collection_usuarios_data_id,
    )


def get_empresas_service() -> EmpresasService:
    settings = get_settings()
    _, databases = get_appwrite_clients()
    return EmpresasService(
        databases=databases,
        database_id=settings.appwrite_database_id,
        collection_id=settings.appwrite_collection_empresas_dados_id,
        enderecos_collection_id=settings.appwrite_collection_enderecos_id,
    )


def get_empresas_dados_service() -> EmpresasDadosService:
    settings = get_settings()
    _, databases = get_appwrite_clients()
    return EmpresasDadosService(
        databases=databases,
        database_id=settings.appwrite_database_id,
        collection_id=settings.appwrite_collection_empresas_dados_id,
        enderecos_collection_id=settings.appwrite_collection_enderecos_id,
        mapbox_access_token=settings.mapbox_access_token,
    )


def get_empresas_socios_service() -> EmpresasSociosService:
    settings = get_settings()
    _, databases = get_appwrite_clients()
    return EmpresasSociosService(
        databases=databases,
        database_id=settings.appwrite_database_id,
        collection_id=settings.appwrite_collection_empresas_socios_id,
    )


def get_usuarios_perfil_service() -> UsuariosPerfilService:
    settings = get_settings()
    _, databases = get_appwrite_clients()
    return UsuariosPerfilService(
        databases=databases,
        database_id=settings.appwrite_database_id,
        collection_id=settings.appwrite_collection_usuarios_perfil_id,
    )


def get_enderecos_service() -> EnderecosService:
    settings = get_settings()
    _, databases = get_appwrite_clients()
    return EnderecosService(
        databases=databases,
        database_id=settings.appwrite_database_id,
        collection_id=settings.appwrite_collection_enderecos_id,
    )


def get_entregas_service() -> EntregasService:
    settings = get_settings()
    _, databases = get_appwrite_clients()
    collection_id = settings.appwrite_collection_entregas_id.strip()
    if not collection_id:
        raise HTTPException(
            status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
            detail="APPWRITE_COLLECTION_ENTREGAS_ID is not configured.",
        )
    return EntregasService(
        databases=databases,
        database_id=settings.appwrite_database_id,
        collection_id=collection_id,
        empresas_service=get_empresas_dados_service(),
        mapbox_access_token=settings.mapbox_access_token,
    )


def get_pedidos_service() -> PedidosService:
    settings = get_settings()
    _, databases = get_appwrite_clients()
    return PedidosService(
        databases=databases,
        database_id=settings.appwrite_database_id,
        collection_id=settings.appwrite_collection_pedidos_id,
        project_id=settings.appwrite_project_id,
        endpoint=get_normalized_appwrite_endpoint(),
        session_cookie=settings.appwrite_session_cookie,
        api_key=settings.appwrite_api_key,
    )


def get_pagamentos_service() -> PagamentosService:
    settings = get_settings()
    _, databases = get_appwrite_clients()
    return PagamentosService(
        databases=databases,
        database_id=settings.appwrite_database_id,
        collection_id=settings.appwrite_collection_pagamentos_id,
        project_id=settings.appwrite_project_id,
        endpoint=get_normalized_appwrite_endpoint(),
        session_cookie=settings.appwrite_session_cookie,
        api_key=settings.appwrite_api_key,
    )


def get_mercadopago_service() -> MercadoPagoService:
    settings = get_settings()
    return MercadoPagoService(
        access_token=settings.mercado_pago_access_token,
        public_key=settings.mercado_pago_public_key,
        sandbox=settings.mercado_pago_sandbox
    )


def get_faturas_service() -> FaturasService:
    settings = get_settings()
    _, db = get_appwrite_clients()
    return FaturasService(
        db,
        settings.appwrite_database_id,
        settings.appwrite_collection_faturas_id,
        project_id=settings.appwrite_project_id,
        endpoint=get_normalized_appwrite_endpoint(),
        session_cookie=settings.appwrite_session_cookie,
        api_key=settings.appwrite_api_key,
    )


def get_laboratorios_service() -> LaboratoriosService:
    settings = get_settings()
    _, db = get_appwrite_clients()
    return LaboratoriosService(db, settings.appwrite_database_id, settings.appwrite_collection_laboratorios_id)


def get_produtos_estoque_service() -> ProdutosEstoqueService:
    settings = get_settings()
    _, db = get_appwrite_clients()
    return ProdutosEstoqueService(db, settings.appwrite_database_id, settings.appwrite_collection_produtos_estoque_id)


def get_produtos_venda_service() -> ProdutosVendaService:
    settings = get_settings()
    _, db = get_appwrite_clients()
    return ProdutosVendaService(
        db,
        settings.appwrite_database_id,
        settings.appwrite_collection_produtos_venda_id,
        project_id=settings.appwrite_project_id,
        endpoint=get_normalized_appwrite_endpoint(),
        session_cookie=settings.appwrite_session_cookie,
        api_key=settings.appwrite_api_key,
    )


def get_usuario_empresa_perfis_service() -> UsuarioEmpresaPerfisService:
    settings = get_settings()
    _, db = get_appwrite_clients()
    return UsuarioEmpresaPerfisService(
        databases=db,
        database_id=settings.appwrite_database_id,
        collection_id=settings.appwrite_collection_usuario_empresa_perfis_id,
        usuarios_collection_id=settings.appwrite_collection_usuarios_id,
        empresas_collection_id=settings.appwrite_collection_empresas_id,
        perfis_collection_id=settings.appwrite_collection_usuarios_perfil_id,
    )


def _extract_empresa_id_from_request(request: Request) -> str | None:
    header_value = request.headers.get("X-Empresa-Id")
    if isinstance(header_value, str) and header_value.strip():
        return header_value.strip()

    query_params = request.query_params
    for key in ("empresaId", "empresa_id"):
        value = query_params.get(key)
        if isinstance(value, str) and value.strip():
            return value.strip()

    path_params = getattr(request, "path_params", {}) or {}
    for key in ("empresa_id", "empresaId"):
        value = path_params.get(key)
        if isinstance(value, str) and value.strip():
            return value.strip()

    return None


async def require_company_scope(
    request: Request,
    user: Dict[str, Any] = Depends(get_current_user_or_401),
) -> str:
    empresa_id = _extract_empresa_id_from_request(request)
    if not empresa_id:
        raise HTTPException(
            status_code=status.HTTP_400_BAD_REQUEST,
            detail="Cabeçalho X-Empresa-Id ou parâmetro empresaId é obrigatório.",
        )

    if bool(user.get("superadmin")):
        return empresa_id

    user_id = user.get("id")
    if not isinstance(user_id, str) or not user_id:
        raise HTTPException(
            status_code=status.HTTP_401_UNAUTHORIZED,
            detail="Usuário inválido no contexto da requisição.",
        )

    await assert_user_in_company(user_id, empresa_id)
    return empresa_id


def get_auth_service() -> AuthService:
    settings = get_settings()
    _, databases = get_appwrite_clients()
    return AuthService(
        databases=databases,
        database_id=settings.appwrite_database_id,
        collection_id=settings.appwrite_collection_usuarios_id,
    )
