from __future__ import annotations

import json
import uuid
from datetime import date
from typing import Any, Dict

import requests

from config import (
    ASAAS_API_KEY,
    ASAAS_BASE_URL,
    PAYMENT_PROVIDER,
    PAYMENT_WEBHOOK_URL,
    PIX_PROVIDER_ENDPOINT,
    PIX_PROVIDER_TOKEN,
)
from database import create_payment, fetchone


class PaymentError(Exception):
    pass


def _asaas_headers() -> Dict[str, str]:
    if not ASAAS_API_KEY:
        raise PaymentError("ASAAS_API_KEY/PIX_PROVIDER_TOKEN não configurado no .env")

    return {
        "access_token": ASAAS_API_KEY,
        "accept": "application/json",
        "content-type": "application/json",
    }


def _asaas_post(path: str, payload: Dict[str, Any]) -> Dict[str, Any]:
    url = f"{ASAAS_BASE_URL}{path}"
    response = requests.post(url, headers=_asaas_headers(), json=payload, timeout=45)

    if response.status_code >= 400:
        raise PaymentError(f"Asaas POST {path} falhou {response.status_code}: {response.text[:1200]}")

    return response.json()


def _asaas_get(path: str) -> Dict[str, Any]:
    url = f"{ASAAS_BASE_URL}{path}"
    response = requests.get(url, headers=_asaas_headers(), timeout=45)

    if response.status_code >= 400:
        raise PaymentError(f"Asaas GET {path} falhou {response.status_code}: {response.text[:1200]}")

    return response.json()


def _create_asaas_customer(model_id: int) -> str:
    model = fetchone("SELECT * FROM models WHERE id=%s", (model_id,))
    if not model:
        raise PaymentError(f"Modelo #{model_id} não encontrada no banco.")

    username = (model.get("username") or "").lstrip("@")
    display_name = model.get("display_name") or username or f"Cliente Telegram {model_id}"

    cpf_cnpj = str(model.get("cpf_cnpj") or "").strip()
    if not cpf_cnpj:
        raise PaymentError("CPF/CNPJ do pagador não está salvo. Peça o CPF/CNPJ antes de gerar o Pix.")

    payload = {
        "name": display_name,
        "cpfCnpj": cpf_cnpj,
        "externalReference": f"model_{model_id}",
        "notificationDisabled": True,
    }

    data = _asaas_post("/customers", payload)
    customer_id = data.get("id")

    if not customer_id:
        raise PaymentError(f"Asaas não retornou customer id: {data}")

    return str(customer_id)


def _create_asaas_pix_charge(model_id: int, package: Dict[str, Any]) -> Dict[str, str]:
    external_reference = f"PAY_{uuid.uuid4().hex[:18]}"
    amount = float(package["price"])
    credits = int(package["credits"])
    package_id = int(package["id"])

    customer_id = _create_asaas_customer(model_id)

    payment_payload = {
        "customer": customer_id,
        "billingType": "PIX",
        "value": amount,
        "dueDate": date.today().isoformat(),
        "description": f"{package['name']} - {credits} crédito(s)",
        "externalReference": external_reference,
    }

    payment = _asaas_post("/payments", payment_payload)
    payment_id_asaas = str(payment.get("id", ""))

    if not payment_id_asaas:
        raise PaymentError(f"Asaas não retornou id da cobrança: {payment}")

    pix = _asaas_get(f"/payments/{payment_id_asaas}/pixQrCode")

    payment_url = str(payment.get("invoiceUrl") or payment.get("bankSlipUrl") or "")
    qr_code_payload = str(pix.get("payload") or "")
    encoded_image = str(pix.get("encodedImage") or "")

    local_payment_id = create_payment(
        model_id=model_id,
        package_id=package_id,
        amount=amount,
        credits=credits,
        provider="asaas",
        external_reference=external_reference,
        payment_url=payment_url,
        qr_code=qr_code_payload,
        provider_payment_id=payment_id_asaas,
        raw_response=json.dumps({"payment": payment, "pix": pix}, ensure_ascii=False),
    )

    return {
        "payment_id": str(local_payment_id),
        "external_reference": external_reference,
        "provider_payment_id": payment_id_asaas,
        "payment_url": payment_url,
        "qr_code": qr_code_payload,
        "encoded_image": encoded_image,
        "expiration_date": str(pix.get("expirationDate") or ""),
    }


def create_pix_charge(model_id: int, package: Dict[str, Any]) -> Dict[str, str]:
    if PAYMENT_PROVIDER == "asaas":
        return _create_asaas_pix_charge(model_id, package)

    if PAYMENT_PROVIDER == "generic" and PIX_PROVIDER_ENDPOINT:
        external_reference = f"PAY_{uuid.uuid4().hex[:18]}"
        amount = float(package["price"])
        credits = int(package["credits"])
        package_id = int(package["id"])

        payload = {
            "external_reference": external_reference,
            "amount": amount,
            "credits": credits,
            "description": f"Compra de {credits} crédito(s)",
            "webhook_url": PAYMENT_WEBHOOK_URL,
        }
        headers = {"Authorization": f"Bearer {PIX_PROVIDER_TOKEN}", "Content-Type": "application/json"}
        response = requests.post(PIX_PROVIDER_ENDPOINT, headers=headers, json=payload, timeout=30)
        response.raise_for_status()
        data = response.json()

        payment_url = str(data.get("payment_url", ""))
        qr_code = str(data.get("qr_code", ""))
        provider_payment_id = str(data.get("provider_payment_id", ""))

        payment_id = create_payment(
            model_id=model_id,
            package_id=package_id,
            amount=amount,
            credits=credits,
            provider=PAYMENT_PROVIDER,
            external_reference=external_reference,
            payment_url=payment_url,
            qr_code=qr_code,
            provider_payment_id=provider_payment_id,
            raw_response=json.dumps(data, ensure_ascii=False),
        )
        return {
            "payment_id": str(payment_id),
            "external_reference": external_reference,
            "payment_url": payment_url,
            "qr_code": qr_code,
            "encoded_image": "",
        }

    external_reference = f"PAY_{uuid.uuid4().hex[:18]}"
    amount = float(package["price"])
    credits = int(package["credits"])
    package_id = int(package["id"])

    payment_id = create_payment(
        model_id=model_id,
        package_id=package_id,
        amount=amount,
        credits=credits,
        provider="manual",
        external_reference=external_reference,
        payment_url="",
        qr_code="",
        provider_payment_id="",
        raw_response="{}",
    )
    return {
        "payment_id": str(payment_id),
        "external_reference": external_reference,
        "payment_url": "",
        "qr_code": "",
        "encoded_image": "",
    }


