from decimal import Decimal
from django.utils import timezone
from django.conf import settings
from recharge.models import (
    RechargeTransaction,
     Operator,
    Circle
)
from payments.models import PaymentTransaction, RechargePaymentSummary
from notifications.services import send_notification
from payments.utils import distribute_commissions
from recharge.services.a1topup_api import A1TopupAPI
from commissions.utils import record_commission_split
from django.utils.timezone import now
from decimal import Decimal
from django.conf import settings
from django.utils import timezone
from django.utils.timezone import now

from recharge.services.provider_factory import get_recharge_client
from recharge.models import Operator, Circle, RechargeTransaction
from payments.models import RechargePaymentSummary
from notifications.services import send_notification
from commissions.utils import record_commission_split
# helper: unique <=10 char id used as Goter txnid
from payments.ids import generate_unique_provider_txnid
from recharge.services.provider_utils import get_provider_key, get_source_value
from recharge.services.recharge_flow import handle_recharge_outcome
from decimal import Decimal, InvalidOperation, ROUND_HALF_UP
from django.conf import settings
from django.utils import timezone
from django.utils.timezone import now

from recharge.services.provider_factory import get_recharge_client
from recharge.models import Operator, Circle, RechargeTransaction
from payments.models import RechargePaymentSummary
from notifications.services import send_notification
from commissions.utils import record_commission_split

from payments.ids import generate_unique_provider_txnid
from recharge.services.provider_utils import get_provider_key, get_source_value
from recharge.services.recharge_flow import handle_recharge_outcome
from django.db import transaction, IntegrityError
from datetime import datetime
from decimal import Decimal
import logging

TWOPL = Decimal("0.01")

def _d(x) -> Decimal:
    try:
        return Decimal(str(x))
    except (InvalidOperation, ValueError, TypeError):
        return Decimal("0")

def q2(x: Decimal) -> Decimal:
    return _d(x).quantize(TWOPL, rounding=ROUND_HALF_UP)

def _parse_req_time(s: str | None):
    if not s:
        return None
    try:
        # Example: "2024-09-25 23:36:18"
        return datetime.strptime(s, "%Y-%m-%d %H:%M:%S")
    except Exception:
        return None
def rupee_str_for_provider(amount: Decimal, *, whole_only=False) -> str:
    """
    - If whole_only=True and amount is an exact integer (e.g. 19.00) -> "19"
    - Else -> "xx.yy" with exactly two decimals
    """
    a = q2(amount)
    if whole_only and a == a.to_integral_value():
        return str(int(a))
    return f"{a:.2f}"

def fmt2(x) -> str:
    """Always return a string with two decimals, even if input was str/None."""
    return format(q2(x), ".2f")

def initiate_recharge(
    *,
    user,
    number: str,
    operator: str,
    circle: str,
    amount: Decimal,                         # rupees (2dp)
    order_id: str,
    use_ext: Decimal = Decimal("0.00"),      # rupees (2dp)
    use_int: Decimal = Decimal("0.00"),      # rupees (2dp)
    gateway_ref: str | None = None,          # human label like "External Wallet + Razorpay"
    client_txn_id: str | None = None,
    # >>> NEW (optional, pass through from perform_wallet_recharge) <<<
    razorpay_order_id: str | None = None,
    razorpay_payment_id: str | None = None,
):
    """
    Goter-first recharge:
      - sends a <=10 char unique client_txn_id to provider
      - stores provider_order_id (Goter: orderId)
      - status/message from Goter: status/resText
    """
    log = logging.getLogger(__name__)
    # log.info("transaction %s saved", order_id)
    # log.info("=============== You're Inside Initiate recharge function ===============")
    # --- Normalize all money to Decimal(2dp) ---
    amount   = q2(amount)

    send_whole_for_prepaid = True  # toggle for your test
    payload_amount = rupee_str_for_provider(
        amount,
        whole_only=(send_whole_for_prepaid)
    )

    use_ext  = q2(use_ext)
    use_int  = q2(use_int)
    paid_via_gateway = q2(amount - use_ext - use_int)
    # log.info(use_ext)
    # log.info(use_int)
    # 1) Resolve provider + DB source label
    provider_key = get_provider_key()         # "goterpay" | "a1topup"
    source_value = get_source_value()         # "Goter"   | "A1Topup"
    vendor_code  = source_value

    # 2) Ensure a unique <=10 id for provider
    # client_txn_id = client_txn_id or generate_unique_provider_txnid(prefix="VP", length=10)
    client_txn_id = client_txn_id or order_id  # single canonical 10-char VP ID

    # 3) Call provider via adapter
    client = get_recharge_client()
    try:
        # log.info("getting goterpay client connection")
        api_resp = client.mobile_recharge(
            txnid=client_txn_id,
            number=str(number),
            # amount=f"{amount:.2f}",
            amount=payload_amount,
            operator_code=str(operator),
            circle_code=str(circle),
        )
        # log.info("Sent prepaid amount=%s (original=%s)", payload_amount, f"{amount:.2f}")
        # log.info("Initiate recharge api response: %s", api_resp)  # parameterized logging

    except Exception as e:
        # log.info("exception at apiresponse initiate recharge")
        api_resp = {"status": "FAILED", "resText": f"API Error: {e}", "orderId": ""}
        # log.info(api_resp)

    # 4) Normalize provider result
    # status_raw = (api_resp.get("status") or "").strip().upper()
    # if status_raw not in {"SUCCESS", "FAILED", "PENDING", "ERROR"}:
    #     status_raw = "ERROR"

    raw_status = (api_resp.get("status") or "").strip().upper()
    provider_status_raw = raw_status  # keep for debugging
    mapped = {"SUCCESS":"Success","FAILED":"Failure","PENDING":"Pending","ERROR":"Error"}
    status_title = mapped.get(raw_status, "Error")
    # log.info(status_title)
    # status_title      = status_raw.title()
    status_message = (api_resp.get("resText") or "").strip()
    # log.info(status_message)
    # Optional: hard cap very long texts (provider sometimes dumps HTML)
    MAX_MSG = 2000
    if len(status_message) > MAX_MSG:
        status_message = status_message[:MAX_MSG] + "…"
    # status_message    = api_resp.get("resText") or ""
    provider_order_id = api_resp.get("orderId") or ""
    # client_txn_id = api_resp.get("TxnId") or ""

    # Extract provider extras (with safe fallbacks)
    operator_order_id = api_resp.get("optId") or ""
    provider_service = api_resp.get("service") or ""
    provider_commission = api_resp.get("Comi") or ""
    provider_req_time_s = api_resp.get("reqTime") or ""
    provider_voucher_cd = api_resp.get("VoucherCode") or ""
    provider_recharge_note = api_resp.get("resText") or ""

    # Normalize commission to Decimal(3)
    try:
        provider_commission_dec = Decimal(str(provider_commission))
    except Exception:
        provider_commission_dec = None

    provider_req_dt = _parse_req_time(provider_req_time_s)
    # log.info("Iam at position provider req dt recharge/utils.py initiate recharge function")
    # 5) Update payment summary (prefer DecimalField in model; if FloatField exists it will coerce)
    RechargePaymentSummary.objects.update_or_create(
        order_id=order_id,
        defaults={
            "user": user,
            "recharge_amount": amount,            # Summary keeps FloatField today
            "used_external_wallet": use_ext,
            "used_internal_wallet": use_int,
            "paid_via_gateway": paid_via_gateway,

            "recharge_status": status_title,
            "gateway_reference": (razorpay_payment_id or razorpay_order_id or provider_order_id or client_txn_id),
            "status_message": status_message or provider_recharge_note,
            "full_response": api_resp,
            # NEW fields
            "provider_order_id" : provider_order_id,
            "operator_order_id": operator_order_id,
            "provider_service": provider_service,
            "provider_commission": provider_commission_dec,
            "provider_request_time": provider_req_dt,
            "provider_voucher_code": provider_voucher_cd,
            "razorpay_order_id":razorpay_order_id,
            "razorpay_payment_id":razorpay_payment_id
        }
    )
    # log.info("4. status title %s", status_title)
    # log.info("Iam at Upsert transaction")
    # 6) Upsert transaction
    try:
        # log.info(operator)
        # log.info(circle)
        op_obj = Operator.objects.get(code=operator, source__iexact=source_value)
        cir_obj = Circle.objects.get(code=circle, source__iexact=source_value)
    except Operator.DoesNotExist:
        # log.info("Iam at operator exception")
        status_title, status_message = "Error", f"Unknown operator code: {operator}"
        # log.info(status_title)
        # log.info(status_message)
        # reflect error in summary and return early
        RechargePaymentSummary.objects.filter(order_id=order_id).update(
            recharge_status=status_title,
            status_message=status_message,
            updated_at=timezone.now(),
        )
        # log.info("5. status title %s", status_title)
        return {"status": status_title, "order_id": order_id, "message": status_message, "raw": api_resp}
    except Circle.DoesNotExist:
        # log.info("Iam at circle exception")
        status_title, status_message = "Error", f"Unknown circle code: {circle}"
        # log.info(status_title)
        # log.info(status_message)
        RechargePaymentSummary.objects.filter(order_id=order_id).update(
            recharge_status=status_title,
            status_message=status_message,
            updated_at=timezone.now(),
        )
        # log.info("6. status title %s", status_title)
        # log.info("Iam returning after exceptions")
        return {"status": status_title, "order_id": order_id, "message": status_message, "raw": api_resp}
    # log.info(status_title)
    with transaction.atomic():
        tx, created = RechargeTransaction.objects.update_or_create(
            order_id=order_id,
            defaults=dict(
                user = user,
                operator=op_obj,
                circle=cir_obj,
                number=number,
                amount=amount,
                client_txn_id=client_txn_id,
                provider_order_id=api_resp.get("orderId") or "",
                status=status_title,
                status_message=status_message,
                response_data=api_resp,
                status_updated_at=timezone.now(),
                used_ext_wallet=use_ext,
                used_viralpe_wallet=use_int,
                paid_via_gateway=paid_via_gateway,
                razorpay_order_id=razorpay_order_id,
                razorpay_payment_id=razorpay_payment_id,
                # created_at auto_add stays as first insert; don't overwrite.
            )
        )
    # log.info("Iam at Post-success hooks")
    # 7) Post-success hooks
    if status_title.lower() == "success":
        # log.info("Iam at inside success block")
        provider_commission=provider_commission_dec or None
        handle_recharge_outcome(
            user,
            tx,
            order_id,
            "success",
            tx.status_message,
            tx.amount,
            operator,
            vendor_code,
            (tx.razorpay_order_id or provider_order_id),
            tx.paid_via_gateway,
            provider_service,
            provider_commission,
        )
    elif status_title.lower() in ("failure", "error"):
        log.info("Iam at inside failure block")
        # Trigger refunds + failed notification
        provider_commission=provider_commission_dec or None
        handle_recharge_outcome(
            user,
            tx,
            order_id,
            "failure",
            tx.status_message,
            tx.amount,
            operator,
            vendor_code,
            (tx.razorpay_order_id or provider_order_id),
            tx.paid_via_gateway,
            provider_service,
            provider_commission,

        )
    # Pending: handled by your poller
    log.info("Iam at final return")
    log.info(status_title)
    return {
        "status": status_title,
        "order_id": order_id,
        "client_txn_id": client_txn_id,
        "provider_order_id": provider_order_id,
        "message": status_message,
        "raw": api_resp,
        "amount": fmt2(amount),
        "used_external_wallet": fmt2(use_ext),
        "used_internal_wallet": fmt2(use_int),
        "paid_via_gateway": fmt2(paid_via_gateway),
        "razorpay_order_id":razorpay_order_id,
        "razorpay_payment_id":razorpay_payment_id
    }

