"""
LipiaOnline Developer Payment Service
Handles STK-push payments to the developer via the LipiaOnline gateway.

API docs : https://lipia-online-docs.vercel.app/initiate-stk-push
Base URL : https://lipia-api.kreativelabske.com/api/v2
Auth     : Authorization: Bearer <API_KEY>  (header — never in body)

The API key is read exclusively from Django settings (LIPIA_ONLINE_API_KEY).
It is NEVER logged, printed, or exposed in responses.

© PhinTech Solutions Company Ltd — System v1.0
"""

import uuid
import logging
import requests
from django.conf import settings
from django.utils import timezone

logger = logging.getLogger(__name__)

# ── Correct LipiaOnline v2 endpoint ──────────────────────────────────────────
LIPIA_BASE_URL = "https://lipia-api.kreativelabske.com/api/v2"
LIPIA_STK_URL  = f"{LIPIA_BASE_URL}/payments/stk-push"

# Developer's LipiaOnline payment link slug (public — not a secret)
LIPIA_LINK_SLUG = "PHINTECHSOLUTIONS"


def _get_api_key() -> str:
    """Return the LipiaOnline API key from settings. Never log this value."""
    key = getattr(settings, 'LIPIA_ONLINE_API_KEY', '').strip()
    if not key:
        raise ValueError(
            "LIPIA_ONLINE_API_KEY is not configured. "
            "Add it to your .env file: LIPIA_ONLINE_API_KEY=<your_key>"
        )
    return key


def generate_reference() -> str:
    """Generate a unique payment reference for this system."""
    ts = timezone.now().strftime('%Y%m%d%H%M%S')
    short_uuid = str(uuid.uuid4()).replace('-', '')[:8].upper()
    return f"PHINT-{ts}-{short_uuid}"


def _build_callback_url() -> str:
    """Build the absolute callback URL for this deployment."""
    site_url = getattr(settings, 'SITE_URL', 'https://uzuriapps.xyz').rstrip('/')
    return f"{site_url}/payments/developer/callback/lipia/"


def initiate_developer_payment(phone_number: str, amount: float,
                                reference: str,
                                description: str = "Developer Service Fee") -> dict:
    """
    Initiate an STK push via LipiaOnline to pay the developer.

    Correct API (v2):
        POST https://lipia-api.kreativelabske.com/api/v2/payments/stk-push
        Headers:
            Authorization: Bearer <API_KEY>
            Content-Type: application/json
        Body:
            phone_number, amount (int), external_reference, callback_url, metadata

    Returns:
        dict: success (bool), message (str), checkout_id (str|None),
              transaction_reference (str|None), raw_response (dict)
    """
    # ── Normalise phone to 07xx / 01xx / 2547xx — LipiaOnline accepts all formats
    phone = str(phone_number).strip()
    if phone.startswith('+'):
        phone = phone[1:]          # strip leading +

    api_key = _get_api_key()

    payload = {
        "phone_number":      phone,
        "amount":            int(float(amount)),   # must be a positive integer
        "external_reference": reference,
        "callback_url":      _build_callback_url(),
        "metadata": {
            "system":      "Haven Grazuri",
            "developer":   "PhinTech Solutions Company Ltd",
            "description": description[:100],
        },
    }

    headers = {
        "Authorization": f"Bearer {api_key}",   # key in header, NOT body
        "Content-Type":  "application/json",
    }

    # Log without the API key
    logger.info(
        f"[DevPayment] Initiating STK push | ref={reference} | "
        f"phone={phone[:4]}***{phone[-3:]} | amount=KES {amount}"
    )

    try:
        resp = requests.post(LIPIA_STK_URL, json=payload, headers=headers, timeout=30)
        resp.raise_for_status()
        data = resp.json()

        # LipiaOnline v2 success response:
        # { "success": true, "status": "success", "data": { "TransactionReference": "...",
        #   "ResponseCode": 0, "ResponseDescription": "..." }, ... }
        success = data.get("success") is True
        inner   = data.get("data", {})
        checkout_id = (
            inner.get("CheckoutRequestID")
            or inner.get("TransactionReference")
            or data.get("CheckoutRequestID")
        )
        tx_ref = inner.get("TransactionReference") or checkout_id
        message = (
            data.get("customerMessage")
            or data.get("message")
            or ("STK push sent — check your phone" if success else "STK push failed")
        )

        logger.info(
            f"[DevPayment] STK response | ref={reference} | "
            f"success={success} | tx_ref={tx_ref}"
        )

        return {
            "success":               success,
            "message":               message,
            "checkout_id":           checkout_id,
            "transaction_reference": tx_ref,
            "raw_response":          data,
        }

    except requests.exceptions.HTTPError as exc:
        status_code = exc.response.status_code if exc.response is not None else "?"
        body = ""
        try:
            body = exc.response.text[:300]
        except Exception:
            pass
        logger.error(
            f"[DevPayment] HTTP {status_code} for ref={reference}: {body}"
        )
        return {
            "success": False,
            "message": f"HTTP {status_code} error from LipiaOnline: {body}",
            "checkout_id": None,
            "transaction_reference": None,
            "raw_response": {"error": body, "status_code": status_code},
        }

    except requests.exceptions.Timeout:
        logger.error(f"[DevPayment] Timeout for ref={reference}")
        return {
            "success": False,
            "message": "Request timed out. Please retry.",
            "checkout_id": None,
            "transaction_reference": None,
            "raw_response": {},
        }

    except requests.exceptions.RequestException as exc:
        logger.error(f"[DevPayment] Network error for ref={reference}: {exc}")
        return {
            "success": False,
            "message": f"Network error: {exc}",
            "checkout_id": None,
            "transaction_reference": None,
            "raw_response": {},
        }

    except Exception as exc:
        logger.error(f"[DevPayment] Unexpected error for ref={reference}: {exc}")
        return {
            "success": False,
            "message": f"Unexpected error: {exc}",
            "checkout_id": None,
            "transaction_reference": None,
            "raw_response": {},
        }


def verify_callback_is_from_this_system(external_reference: str) -> bool:
    """
    Verify that a LipiaOnline callback belongs to a payment WE initiated.
    Checks that the ExternalReference exists in our DeveloperPayment table.
    Payments made by other users to the same LipiaOnline link are ignored.
    """
    from .developer_payment_models import DeveloperPayment
    return DeveloperPayment.objects.filter(reference=external_reference).exists()


def process_lipia_callback(callback_data: dict) -> dict:
    """
    Process an incoming LipiaOnline payment callback/webhook.

    LipiaOnline v2 callback payload:
    {
        "status": true/false,
        "response": {
            "Amount": 100,
            "ExternalReference": "PHINT-...",
            "MpesaReceiptNumber": "NEF61H8J60",
            "Phone": "254712345678",
            "ResultCode": 0,
            "ResultDesc": "Success...",
            "Status": "Success",
            "CheckoutRequestID": "...",
            "MerchantRequestID": "...",
            "Metadata": {...}
        }
    }

    Only processes callbacks whose ExternalReference matches a payment we initiated.
    All other callbacks are silently ignored.
    """
    from .developer_payment_models import DeveloperPayment

    # LipiaOnline wraps fields inside "response"
    response_data = callback_data.get("response", callback_data)

    external_ref = (
        response_data.get("ExternalReference")
        or callback_data.get("external_reference")
        or callback_data.get("order_id")          # legacy fallback
    )

    if not external_ref:
        logger.warning("[DevPayment] Callback with no ExternalReference — ignoring")
        return {"processed": False, "message": "No ExternalReference in callback"}

    # CRITICAL: only process payments we initiated from this system
    if not verify_callback_is_from_this_system(external_ref):
        logger.info(
            f"[DevPayment] Callback for ref={external_ref} not from this system — ignoring"
        )
        return {"processed": False, "message": "Not a system-originated payment — ignored"}

    try:
        payment = DeveloperPayment.objects.get(reference=external_ref)
    except DeveloperPayment.DoesNotExist:
        logger.error(f"[DevPayment] DeveloperPayment not found for ref={external_ref}")
        return {"processed": False, "message": "Payment record not found"}

    # Determine outcome from LipiaOnline v2 fields
    top_level_status = callback_data.get("status")          # bool: true = success
    result_code      = response_data.get("ResultCode")       # 0 = success
    status_str       = str(response_data.get("Status", "")).lower()
    mpesa_receipt    = response_data.get("MpesaReceiptNumber", "").strip()
    checkout_id      = response_data.get("CheckoutRequestID", "")
    result_desc      = response_data.get("ResultDesc", "")

    is_success = (
        top_level_status is True
        or result_code == 0
        or status_str == "success"
        or bool(mpesa_receipt)
    )

    if is_success:
        payment.mark_completed(
            mpesa_receipt=mpesa_receipt or None,
            transaction_id=checkout_id or None,
            callback_data=callback_data,
        )
        logger.info(
            f"[DevPayment] COMPLETED | ref={external_ref} | "
            f"receipt={mpesa_receipt} | amount=KES {payment.amount}"
        )
        return {"processed": True, "message": "Payment completed successfully"}
    else:
        reason = result_desc or "Payment not completed"
        payment.mark_failed(reason=reason, callback_data=callback_data)
        logger.warning(
            f"[DevPayment] FAILED | ref={external_ref} | reason={reason}"
        )
        return {"processed": True, "message": f"Payment failed: {reason}"}
