from __future__ import annotations

import logging
from typing import Any, Dict, List, Optional, Literal
from datetime import datetime, timedelta

import mercadopago
import requests
from fastapi import HTTPException, status
from requests import RequestException
from pydantic import BaseModel, ConfigDict, model_validator

logger = logging.getLogger(__name__)


class MercadoPagoItem(BaseModel):
    model_config = ConfigDict(
        json_schema_extra={
            "example": {
                "id": "item-123",
                "title": "Exame Laboratorial",
                "quantity": 1,
                "unit_price": 149.9,
                "currency_id": "BRL",
            }
        }
    )

    id: str
    title: str
    quantity: int
    unit_price: float
    currency_id: str = "BRL"


class MercadoPagoPayer(BaseModel):
    model_config = ConfigDict(
        json_schema_extra={
            "example": {
                "name": "Maria",
                "surname": "Silva",
                "email": "maria.teste@example.com",
                "identification": {"type": "CPF", "number": "12345678909"},
            }
        }
    )

    name: Optional[str] = None
    surname: Optional[str] = None
    email: Optional[str] = None
    phone: Optional[Dict[str, str]] = None
    identification: Optional[Dict[str, str]] = None
    address: Optional[Dict[str, Any]] = None


class MercadoPagoPreferenceRequest(BaseModel):
    model_config = ConfigDict(
        json_schema_extra={
            "example": {
                "items": [
                    {
                        "id": "item-123",
                        "title": "Plano mensal",
                        "quantity": 1,
                        "unit_price": 99.9,
                        "currency_id": "BRL",
                    }
                ],
                "payer": {
                    "email": "cliente@example.com",
                    "name": "Cliente",
                    "surname": "Teste",
                },
                "external_reference": "pedido-123",
                "notification_url": "https://bff.saveinmed.com.br/mercadopago/webhook",
                "back_urls": {
                    "success": "https://app.saveinmed.com.br/pagamento/sucesso",
                    "failure": "https://app.saveinmed.com.br/pagamento/erro",
                },
            }
        }
    )

    items: List[MercadoPagoItem]
    payer: Optional[MercadoPagoPayer] = None
    back_urls: Optional[Dict[str, str]] = None
    auto_return: Optional[str] = "all"  # "all" para redirecionar em qualquer status (incluindo pending do PIX)
    payment_methods: Optional[Dict[str, Any]] = None
    notification_url: Optional[str] = None
    statement_descriptor: Optional[str] = None
    external_reference: Optional[str] = None
    expires: Optional[bool] = True
    expiration_date_from: Optional[str] = None
    expiration_date_to: Optional[str] = None


class MercadoPagoTransfer(BaseModel):
    """Regras de split para transferências a recebedores secundários."""

    model_config = ConfigDict(
        json_schema_extra={
            "example": {
                "collector_id": 123456789,
                "amount": 49.9,
                "description": "Repasse para profissional",
            }
        }
    )

    collector_id: int
    amount: Optional[float] = None
    percentage: Optional[float] = None
    description: Optional[str] = None

    @model_validator(mode="after")
    def validate_amount_or_percentage(self) -> "MercadoPagoTransfer":
        has_amount = self.amount is not None
        has_percentage = self.percentage is not None

        if has_amount and has_percentage:
            raise ValueError("Use apenas amount ou percentage em cada transfer.")

        if not has_amount and not has_percentage:
            raise ValueError("Informe amount ou percentage em cada transfer.")

        return self


class MercadoPagoSplitPreferenceRequest(MercadoPagoPreferenceRequest):
    model_config = ConfigDict(
        json_schema_extra={
            "example": {
                "purpose": "wallet_purchase",
                "items": [
                    {
                        "id": "consulta-xyz",
                        "title": "Consulta médica",
                        "quantity": 1,
                        "unit_price": 120.0,
                        "currency_id": "BRL",
                    }
                ],
                "external_reference": "pedido-987",
                "notification_url": "https://bff.saveinmed.com.br/mercadopago/webhook",
                "transfers": [
                    {
                        "collector_id": 11223344,
                        "amount": 20.0,
                        "description": "Comissão da clínica",
                    },
                    {
                        "collector_id": 99887766,
                        "percentage": 50.0,
                        "description": "Participação do especialista",
                    },
                ],
            }
        }
    )

    purpose: Literal["wallet_purchase"] = "wallet_purchase"
    transfers: List[MercadoPagoTransfer]
    marketplace_fee: Optional[float] = None
    metadata: Optional[Dict[str, Any]] = None

class MercadoPagoPreferenceResponse(BaseModel):
    """Subset dos campos retornados pela API de preferência do Mercado Pago."""

    model_config = ConfigDict(
        json_schema_extra={
            "example": {
                "id": "123456789-abcd-efgh-ijkl-123456789012",
                "init_point": "https://www.mercadopago.com.br/checkout/v1/redirect?pref_id=123456789",
                "sandbox_init_point": "https://sandbox.mercadopago.com.br/checkout/v1/redirect?pref_id=123456789",
                "external_reference": "pedido-123",
                "auto_return": "approved",
            }
        }
    )

    id: str
    init_point: Optional[str] = None
    sandbox_init_point: Optional[str] = None
    collector_id: Optional[int] = None
    client_id: Optional[str] = None
    date_created: Optional[str] = None
    last_updated: Optional[str] = None
    items: Optional[List[Dict[str, Any]]] = None
    payer: Optional[Dict[str, Any]] = None
    back_urls: Optional[Dict[str, str]] = None
    auto_return: Optional[str] = None
    payment_methods: Optional[Dict[str, Any]] = None
    notification_url: Optional[str] = None
    statement_descriptor: Optional[str] = None
    external_reference: Optional[str] = None


class MercadoPagoPaymentResponse(BaseModel):
    """Estrutura flexível para respostas de pagamentos."""

    model_config = ConfigDict(
        json_schema_extra={
            "example": {
                "id": 123456789,
                "status": "approved",
                "status_detail": "accredited",
                "transaction_amount": 99.9,
                "payment_method_id": "pix",
                "payer": {"email": "cliente@example.com"},
                "external_reference": "pedido-123",
            }
        }
    )

    id: int
    status: Optional[str] = None
    status_detail: Optional[str] = None
    payment_method_id: Optional[str] = None
    payment_type_id: Optional[str] = None
    date_created: Optional[str] = None
    date_last_updated: Optional[str] = None
    money_release_date: Optional[str] = None
    operation_type: Optional[str] = None
    issuer_id: Optional[str] = None
    payment_method: Optional[Dict[str, Any]] = None
    transaction_amount: Optional[float] = None
    transaction_amount_refunded: Optional[float] = None
    coupon_amount: Optional[float] = None
    differential_pricing_id: Optional[int] = None
    deduction_schema: Optional[str] = None
    transaction_details: Optional[Dict[str, Any]] = None
    fee_details: Optional[List[Dict[str, Any]]] = None
    charges_details: Optional[List[Dict[str, Any]]] = None
    captured: Optional[bool] = None
    binary_mode: Optional[bool] = None
    live_mode: Optional[bool] = None
    order: Optional[Dict[str, Any]] = None
    external_reference: Optional[str] = None
    description: Optional[str] = None
    metadata: Optional[Dict[str, Any]] = None
    money_release_schema: Optional[str] = None
    taxes_amount: Optional[float] = None
    counter_currency: Optional[str] = None
    brand_id: Optional[str] = None
    shipping_amount: Optional[float] = None
    build_version: Optional[str] = None
    pos_id: Optional[str] = None
    store_id: Optional[str] = None
    integrator_id: Optional[str] = None
    platform_id: Optional[str] = None
    corporation_id: Optional[str] = None
    payer: Optional[Dict[str, Any]] = None
    collector_id: Optional[int] = None
    sponsor_id: Optional[int] = None
    application_id: Optional[str] = None
    processing_mode: Optional[str] = None
    merchant_account_id: Optional[str] = None
    merchant_number: Optional[str] = None
    acquirer_reconciliation: Optional[List[Any]] = None


class MercadoPagoService:
    def __init__(self, access_token: str, public_key: str, sandbox: bool = True):
        """
        Inicializa o serviço do Mercado Pago
        
        Args:
            access_token: Token de acesso do Mercado Pago
            public_key: Chave pública do Mercado Pago
            sandbox: Se True, usa o ambiente de sandbox
        """
        self.access_token = access_token
        self.public_key = public_key
        self.sandbox = sandbox
        
        # Inicializa o SDK do Mercado Pago
        self.sdk = mercadopago.SDK(access_token)
        
        if not access_token:
            raise ValueError("Access token do Mercado Pago é obrigatório")
        
        logger.info(f"MercadoPago Service inicializado - Sandbox: {sandbox}")

    def _prepare_preference_payload(self, preference_dict: Dict[str, Any]) -> Dict[str, Any]:
        if not preference_dict.get("expires"):
            preference_dict["expires"] = True

        if not preference_dict.get("expiration_date_to"):
            expiration_date = datetime.now() + timedelta(hours=24)
            preference_dict["expiration_date_to"] = expiration_date.isoformat()

        # Garantir que back_urls sempre tenha "pending" para PIX
        # PIX sempre começa como "pending" e precisa de uma URL de retorno
        if preference_dict.get("back_urls"):
            back_urls = preference_dict["back_urls"]
            # Se não houver URL "pending", usar "success" como fallback
            if not back_urls.get("pending") and back_urls.get("success"):
                back_urls["pending"] = back_urls["success"]
            # Se não houver URL "pending" nem "success", usar "failure" como fallback
            elif not back_urls.get("pending") and back_urls.get("failure"):
                back_urls["pending"] = back_urls["failure"]
        
        # Para PIX, usar auto_return="all" para redirecionar em qualquer status
        # Isso ajuda a redirecionar mesmo quando o pagamento está "pending"
        # IMPORTANTE: "all" redireciona para pending, success ou failure automaticamente
        if not preference_dict.get("auto_return") or preference_dict.get("auto_return") == "approved":
            preference_dict["auto_return"] = "all"

        preference_dict["payment_methods"] = self._build_checkout_pro_payment_methods(
            preference_dict.get("payment_methods")
        )
        
        # Para permitir pagamento sem login (guest checkout):
        # 1. Não enviar dados de identificação obrigatórios do payer
        # 2. Remover campos que podem forçar login
        
        if preference_dict.get("payer"):
            payer = preference_dict["payer"]
            if payer and isinstance(payer, dict):
                # Remover campos que podem forçar login obrigatório
                simplified_payer = {}
                
                # Email pode ser opcional, mas se fornecido, manter
                if payer.get("email"):
                    simplified_payer["email"] = payer["email"]
                
                # Nome pode ser mantido se fornecido
                if payer.get("name"):
                    simplified_payer["name"] = payer["name"]
                
                # NÃO incluir: identification, phone, address - estes podem forçar login
                
                # Se após simplificação ainda tiver dados, usar; senão, remover completamente
                if simplified_payer:
                    preference_dict["payer"] = simplified_payer
                else:
                    preference_dict.pop("payer", None)
        else:
            # Se não houver payer, remover completamente para permitir pagamento como convidado
            preference_dict.pop("payer", None)

        return preference_dict

    def _build_checkout_pro_payment_methods(
        self, payment_methods: Optional[Dict[str, Any]]
    ) -> Dict[str, Any]:
        """
        Configura o checkout Pro para PIX e cartão de crédito à vista.
        Permite pagamento sem login (guest/anônimo).
        """

        forced_excluded_types = {
            "ticket",
            "atm",
            "debit_card",
            "prepaid_card",
            "digital_currency",
        }

        # Remove exclusões de tipos que conflitam com os métodos permitidos
        provided_excluded_types = {
            item.get("id")
            for item in (payment_methods or {}).get("excluded_payment_types", [])
            if item.get("id") not in {"credit_card", "bank_transfer"}
        }

        combined_excluded_types = forced_excluded_types.union(provided_excluded_types)

        # Garantir que PIX não seja excluído
        provided_excluded_methods = [
            item
            for item in (payment_methods or {}).get("excluded_payment_methods", [])
            if item.get("id") != "pix"
        ]

        restricted_payment_methods: Dict[str, Any] = {
            "excluded_payment_types": [
                {"id": payment_type} for payment_type in sorted(combined_excluded_types)
            ],
            "excluded_payment_methods": provided_excluded_methods,
            # Apenas pagamento à vista no cartão de crédito (sem parcelamento)
            "installments": 1,
        }

        return restricted_payment_methods

    async def _create_preference(self, preference_dict: Dict[str, Any]) -> MercadoPagoPreferenceResponse:
        try:
            payload = self._prepare_preference_payload(preference_dict)
            
            # FORÇAR auto_return="all" para redirecionar após PIX (que começa como pending)
            # "all" redireciona automaticamente para pending, success ou failure
            # Isso é essencial para PIX que começa como "pending" e depois vira "approved"
            payload["auto_return"] = "all"
            
            # Garantir que back_urls tenha todas as URLs configuradas
            if payload.get("back_urls"):
                back_urls = payload["back_urls"]
                # Se não tiver pending, usar success como fallback
                if not back_urls.get("pending") and back_urls.get("success"):
                    back_urls["pending"] = back_urls["success"]
            
            logger.info(f"Criando preferência com auto_return={payload.get('auto_return')}, back_urls={payload.get('back_urls')}")

            result = self.sdk.preference().create(payload)

            if result["status"] != 201:
                error_msg = result.get("response", {}).get("message", "Erro desconhecido")
                logger.error(f"Erro ao criar preferência: {error_msg}")
                raise HTTPException(
                    status_code=status.HTTP_400_BAD_REQUEST,
                    detail=f"Erro ao criar preferência no Mercado Pago: {error_msg}"
                )

            preference_response = result["response"]
            logger.info(f"Preferência criada com sucesso: {preference_response['id']}")

            return MercadoPagoPreferenceResponse(**preference_response)

        except Exception as e:
            logger.error(f"Erro ao criar preferência: {str(e)}")
            if isinstance(e, HTTPException):
                raise
            raise HTTPException(
                status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
                detail=f"Erro interno ao criar preferência: {str(e)}"
            )

    async def create_preference(self, preference_data: MercadoPagoPreferenceRequest) -> MercadoPagoPreferenceResponse:
        """
        Cria uma preferência de pagamento padrão no Mercado Pago.
        Permite pagamento sem login (guest/anônimo).
        """

        preference_dict = preference_data.model_dump(exclude_none=True)
        
        # Se o payer não for fornecido ou for opcional, remover para permitir pagamento como convidado
        # O Checkout Pro permite pagamento sem login quando não há dados obrigatórios do payer
        if not preference_dict.get("payer") or not preference_dict["payer"]:
            # Se payer estiver vazio, remover completamente para permitir pagamento como convidado
            preference_dict.pop("payer", None)
        elif isinstance(preference_dict.get("payer"), dict):
            payer = preference_dict["payer"]
            # Se o payer tiver apenas email (opcional), está ok
            # Mas se tiver identification ou phone, pode forçar login
            if payer.get("identification") or (payer.get("phone") and len(payer) > 2):
                # Simplificar payer para permitir pagamento como convidado
                simplified_payer = {}
                if payer.get("email"):
                    simplified_payer["email"] = payer["email"]
                if payer.get("name"):
                    simplified_payer["name"] = payer["name"]
                preference_dict["payer"] = simplified_payer if simplified_payer else None
        
        # Se payer estiver vazio após simplificação, remover
        if not preference_dict.get("payer"):
            preference_dict.pop("payer", None)
            
        return await self._create_preference(preference_dict)

    async def create_split_preference(
        self, split_data: MercadoPagoSplitPreferenceRequest
    ) -> MercadoPagoPreferenceResponse:
        """Cria uma preferência de pagamento com regras de split."""

        preference_dict = split_data.model_dump(exclude_none=True)
        if "purpose" not in preference_dict:
            preference_dict["purpose"] = "wallet_purchase"

        if not preference_dict.get("transfers"):
            raise HTTPException(
                status_code=status.HTTP_400_BAD_REQUEST,
                detail="É obrigatório informar transfers para criar uma preferência com split.",
            )

        return await self._create_preference(preference_dict)

    async def get_payment(self, payment_id: int) -> MercadoPagoPaymentResponse:
        """
        Busca informações de um pagamento específico
        
        Args:
            payment_id: ID do pagamento
            
        Returns:
            Dados do pagamento
        """
        try:
            result = self.sdk.payment().get(payment_id)

            status_code = result.get("status")
            payment_data = result.get("response")

            if status_code != 200 or not isinstance(payment_data, dict):
                error_msg = "Pagamento não encontrado"
                if isinstance(payment_data, dict):
                    error_msg = payment_data.get("message") or payment_data.get("error") or error_msg
                logger.error(
                    "Erro ao buscar pagamento %s: %s (status %s)",
                    payment_id,
                    error_msg,
                    status_code,
                )
                raise HTTPException(
                    status_code=status.HTTP_404_NOT_FOUND,
                    detail=f"Pagamento não encontrado: {error_msg}"
                )

            if "error" in payment_data or payment_data.get("id") is None:
                error_msg = payment_data.get("message") or payment_data.get("error") or "Pagamento não encontrado"
                api_status = payment_data.get("status")
                http_status = status.HTTP_400_BAD_REQUEST
                if isinstance(api_status, int) and api_status in {400, 401, 403, 404}:
                    http_status = api_status
                elif isinstance(api_status, int) and 400 <= api_status < 500:
                    http_status = status.HTTP_400_BAD_REQUEST
                elif isinstance(api_status, int) and api_status >= 500:
                    http_status = status.HTTP_502_BAD_GATEWAY
                logger.error(
                    "Erro na resposta do Mercado Pago ao buscar pagamento %s: %s (status %s)",
                    payment_id,
                    error_msg,
                    api_status,
                )
                raise HTTPException(
                    status_code=http_status,
                    detail=f"Pagamento não encontrado: {error_msg}"
                )

            logger.info(f"Pagamento {payment_id} encontrado com status: {payment_data.get('status')}")

            return MercadoPagoPaymentResponse(**payment_data)
            
        except RequestException as exc:
            logger.error(
                "Erro de comunicação com o Mercado Pago ao buscar pagamento %s: %s",
                payment_id,
                str(exc),
                exc_info=True,
            )
            raise HTTPException(
                status_code=status.HTTP_502_BAD_GATEWAY,
                detail=f"Falha ao se comunicar com o Mercado Pago: {str(exc)}",
            ) from exc
        except Exception as e:
            logger.error(f"Erro ao buscar pagamento {payment_id}: {str(e)}")
            if isinstance(e, HTTPException):
                raise
            raise HTTPException(
                status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
                detail=f"Erro interno ao buscar pagamento: {str(e)}"
            )

    async def search_payments_by_reference(self, external_reference: str) -> List[MercadoPagoPaymentResponse]:
        """
        Busca pagamentos por external_reference usando a API REST do Mercado Pago
        
        Args:
            external_reference: Referência externa do pagamento
            
        Returns:
            Lista de pagamentos encontrados
        """
        try:
            # Usa a API REST do Mercado Pago para buscar pagamentos
            url = "https://api.mercadopago.com/v1/payments/search"
            headers = {
                "Authorization": f"Bearer {self.access_token}",
                "Content-Type": "application/json"
            }
            params = {
                "external_reference": external_reference,
                "sort": "date_created",
                "criteria": "desc"
            }
            
            response = requests.get(url, headers=headers, params=params, timeout=10)
            
            if response.status_code != 200:
                error_msg = response.json().get("message", "Erro ao buscar pagamentos")
                logger.error(f"Erro ao buscar pagamentos por external_reference {external_reference}: {error_msg}")
                raise HTTPException(
                    status_code=status.HTTP_400_BAD_REQUEST,
                    detail=f"Erro ao buscar pagamentos: {error_msg}"
                )
            
            data = response.json()
            results = data.get("results", [])
            payments = [MercadoPagoPaymentResponse(**payment) for payment in results]
            
            logger.info(f"Encontrados {len(payments)} pagamentos para external_reference: {external_reference}")
            return payments
            
        except HTTPException:
            raise
        except Exception as e:
            logger.error(f"Erro ao buscar pagamentos por external_reference: {str(e)}")
            raise HTTPException(
                status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
                detail=f"Erro interno ao buscar pagamentos: {str(e)}"
            )

    async def process_webhook(self, webhook_data: Dict[str, Any]) -> Dict[str, Any]:
        """
        Processa webhook do Mercado Pago
        
        Args:
            webhook_data: Dados do webhook
            
        Returns:
            Dados processados do webhook
        """
        try:
            webhook_type = webhook_data.get("type") or webhook_data.get("topic")
            data_id = (
                webhook_data.get("data", {}).get("id")
                or webhook_data.get("id")
                or webhook_data.get("resource")
            )

            logger.info(f"Processando webhook - Tipo: {webhook_type}, ID: {data_id}")

            if webhook_type == "payment":
                # Busca os dados completos do pagamento
                if not data_id:
                    raise HTTPException(
                        status_code=status.HTTP_400_BAD_REQUEST,
                        detail="ID do pagamento não informado no webhook."
                    )

                try:
                    payment_id = int(data_id)
                except (TypeError, ValueError):
                    raise HTTPException(
                        status_code=status.HTTP_400_BAD_REQUEST,
                        detail="ID do pagamento inválido no webhook."
                    )

                payment_data = await self.get_payment(payment_id)
                
                return {
                    "type": "payment",
                    "payment_id": data_id,
                    "status": payment_data.status,
                    "external_reference": payment_data.external_reference,
                    "transaction_amount": payment_data.transaction_amount,
                    "payment_method_id": payment_data.payment_method_id,
                    "date_created": payment_data.date_created,
                    "date_last_updated": payment_data.date_last_updated
                }
            
            elif webhook_type == "plan":
                logger.info(f"Webhook de plano recebido: {data_id}")
                return {"type": "plan", "plan_id": data_id}
            
            elif webhook_type == "subscription":
                logger.info(f"Webhook de assinatura recebido: {data_id}")
                return {"type": "subscription", "subscription_id": data_id}
            
            else:
                logger.warning(f"Tipo de webhook não suportado: {webhook_type}")
                return {"type": "unknown", "data": webhook_data}
                
        except Exception as e:
            logger.error(f"Erro ao processar webhook: {str(e)}")
            raise HTTPException(
                status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
                detail=f"Erro ao processar webhook: {str(e)}"
            )

    def get_payment_status_mapping(self, mp_status: str) -> str:
        """
        Mapeia status do Mercado Pago para status interno
        
        Args:
            mp_status: Status do Mercado Pago
            
        Returns:
            Status interno correspondente
        """
        status_mapping = {
            "approved": "pago",
            "pending": "pendente", 
            "authorized": "pendente",
            "in_process": "pendente",
            "in_mediation": "pendente",
            "rejected": "falhou",
            "cancelled": "falhou",
            "refunded": "falhou",
            "charged_back": "falhou"
        }
        
        return status_mapping.get(mp_status, "pendente")

    def validate_webhook_signature(self, request_body: str, signature: str) -> bool:
        """
        Valida a assinatura do webhook (implementação básica)
        
        Args:
            request_body: Corpo da requisição
            signature: Assinatura recebida
            
        Returns:
            True se a assinatura é válida
        """
        # TODO: Implementar validação real da assinatura
        # Por enquanto, sempre retorna True
        logger.warning("Validação de assinatura do webhook não implementada")
        return True

