import os
from models.EducoinsTopup import EducoinsTopup
from models.Coupon import Coupon
from models.PaymentOrder import PaymentOrder
from models.user import User
from bson import ObjectId
import razorpay
from flask import Blueprint, request, jsonify
from datetime import datetime
from mongoengine.errors import ValidationError, DoesNotExist
from utils.jwt_service import jwt_required
from models.transaction_history import TransactionHistory

payments_bp = Blueprint("payments_bp", __name__)

def _to_paise(rupees: int) -> int:
    return int(rupees) * 100

@payments_bp.route("/create-order", methods=["POST"])
@jwt_required
def create_order():
    """
    Body:
    {
      "topup_id": "<MongoID>",
      "coupon_code": "<CODE>"   # optional; we also accept coupon_id if you send it
      "coupon_id": "<MongoID>"  # optional
      "amount": <IGNORED>       # ignored per requirement
    }
    """
    data = request.get_json(silent=True) or {}
    user_id = getattr(request, "user_id", None)
    topup_id = data.get("topup_id")
    coupon_code = data.get("coupon_code")
    coupon_id = data.get("coupon_id")  # optional alternative

    if not topup_id:
        return jsonify({"status": False, "message": "topup_id is required"}), 400

    # --- Step 1: Validate Topup (active) ---
    try:
        topup = EducoinsTopup.objects(id=topup_id, status=1).first()
    except (ValidationError, DoesNotExist):
        topup = None

    if not topup:
        return jsonify({"status": False, "message": "Invalid topup selected"}), 400

    # --- Step 2: Find Coupon (if provided) ---
    coupon = None
    if coupon_code:
        coupon = Coupon.objects(coupon_code=coupon_code).first()
        if not coupon:
            return jsonify({"status": False, "message": "Invalid coupon code"}), 400
    elif coupon_id:
        try:
            coupon = Coupon.objects(id=coupon_id).first()
        except (ValidationError, DoesNotExist):
            coupon = None
        if not coupon:
            return jsonify({"status": False, "message": "Invalid coupon"}), 400

    # If coupon is present, mirror your exact validations
    if coupon:
        if coupon.status != 1:
            return jsonify({"status": False, "message": "Coupon is inactive"}), 400

        now = datetime.utcnow()
        if now < coupon.coupon_start_date or now > coupon.coupon_end_date:
            return jsonify({"status": False, "message": "Coupon is expired"}), 400

    # --- Step 3: Amount calculation (EXACTLY like your apply-coupon) ---
    # You showed: Subtotal = topup.topup_coin_value  (then amount_to_pay = subtotal - discount)
    subtotal = int(topup.topup_coin_value)
    discount_applied = 0

    if coupon:
        if coupon.coupon_type == "flat":
            if coupon.coupon_value >= subtotal:
                return jsonify({"status": False, "message": "Invalid coupon code"}), 400
            discount_applied = int(coupon.coupon_value)

        elif coupon.coupon_type == "percentage":
            discount_applied = (subtotal * int(coupon.coupon_value)) // 100
            if discount_applied >= subtotal:
                return jsonify({"status": False, "message": "Invalid discount"}), 400

    amount_to_pay = subtotal - discount_applied
    if amount_to_pay <= 0:
        return jsonify({"status": False, "message": "Payable amount must be greater than zero"}), 400

    # --- Step 4: Create Razorpay order (amount in paise) ---
    try:
        key_id = os.environ["RAZORPAY_KEY_ID"]
        key_secret = os.environ["RAZORPAY_KEY_SECRET"]
    except KeyError:
        return jsonify({"status": False, "message": "Razorpay credentials not configured"}), 500

    client = razorpay.Client(auth=(key_id, key_secret))
    receipt = f"tp-{str(topup.id)[-6:]}-{int(datetime.utcnow().timestamp())}"

    try:
        rzp_order = client.order.create({
            "amount": _to_paise(amount_to_pay),  # integer paise
            "currency": "INR",
            "receipt": receipt,
            "payment_capture": 1
        })
    except Exception as e:
        return jsonify({"status": False, "message": f"Razorpay order creation failed: {str(e)}"}), 502

    order_id = rzp_order.get("id")
    if not order_id:
        return jsonify({"status": False, "message": "Failed to get Razorpay order id"}), 502

    # --- Step 5: Persist order log ---
    po = PaymentOrder(
        user_id=user_id,
        topup=topup,
        coupon=coupon,
        subtotal=subtotal,
        discount=discount_applied,
        amount=amount_to_pay,
        razorpay_order_id=order_id,
        receipt=receipt,
        status="created",
    ).save()

    # --- Step 6: Respond with order id (for frontend checkout init) ---
    return jsonify({
        "status": True,
        "message": "Order created",
        "order_id": order_id,
        "amount": _to_paise(amount_to_pay),  # paise for convenience
        "payment_order_id": str(po.id),      # your internal record
        "receipt": receipt
    }), 200



@payments_bp.route("/verify", methods=["POST"])
@jwt_required
def verify_order():
    """
    Body:
    {
      "order_id": "<razorpay_order_id>",
      "payment_id": "<razorpay_payment_id>",
      "payment_order_id": "<internal id>"   # optional
    }
    Verifies directly with Razorpay (no signature) and credits coins on 'captured'.
    """
    data = request.get_json(silent=True) or {}
    user_id = getattr(request, "user_id", None)

    order_id = data.get("order_id")
    payment_id = data.get("payment_id")
    payment_order_id = data.get("payment_order_id")

    if not (order_id and payment_id):
        return jsonify({"status": False, "message": "order_id and payment_id are required"}), 400

    # Load our PaymentOrder
    po = None
    if payment_order_id:
        try:
            po = PaymentOrder.objects(id=payment_order_id).first()
        except Exception:
            po = None
    if not po:
        po = PaymentOrder.objects(razorpay_order_id=order_id).first()
        
    if not po:
        return jsonify({"status": False, "message": "Payment order not found"}), 404

    # Ensure the caller owns this order
    if str(po.user_id) != str(user_id):
        
        return jsonify({"status": False, "message": "Forbidden"}), 403

    # Idempotency: already paid?
    if po.status == "paid":
        user = User.objects(id=po.user_id).first()
        return jsonify({
            "status": True,
            "message": "Order already verified and credited",
            "order_status": po.status,
            "coins_wallet_balance": user.coins_wallet_balance if user else None
        }), 200

    # Razorpay client
    try:
        key_id = os.environ["RAZORPAY_KEY_ID"]
        key_secret = os.environ["RAZORPAY_KEY_SECRET"]
    except KeyError:
        return jsonify({"status": False, "message": "Razorpay credentials not configured"}), 500

    client = razorpay.Client(auth=(key_id, key_secret))

    # 1) Fetch payment from Razorpay
    try:
        payment = client.payment.fetch(payment_id)
    except Exception as e:
        return jsonify({"status": False, "message": f"Failed to fetch payment: {str(e)}"}), 502

    payment_status = payment.get("status")          # expect 'captured' on success
    payment_order = payment.get("order_id")         # should equal our order_id
    payment_amount = int(payment.get("amount", 0))  # in paise

    # 2) Cross-check ownership & amount (server-side source of truth)
    expected_paise = int(po.amount) * 100  # our PaymentOrder stores rupees, Razorpay is paise

    if payment_order != order_id:
        return jsonify({"status": False, "message": "Payment not linked to this order_id"}), 400

    if payment_amount != expected_paise:
        return jsonify({"status": False, "message": "Amount mismatch"}, 400)

    if payment_status != "captured":
        # If failed, mark failed; otherwise leave as created (pending/incomplete)
        if payment_status == "failed":
            po.status = "failed"
            po.save()
        return jsonify({
            "status": False,
            "message": f"Payment not captured (status={payment_status})",
            "order_status": po.status
        }), 400

    # 3) Success: credit user's wallet with purchased coins (topup.topup_coins)
    user = User.objects(id=po.user_id).first()
    if not user:
        return jsonify({"status": False, "message": "User not found"}), 404
    if not po.topup:
        return jsonify({"status": False, "message": "Topup not found for this order"}), 404

    coins_to_credit = int(po.topup.topup_coins or 0)
    balance_before = int(user.coins_wallet_balance or 0)
    balance_after = balance_before + coins_to_credit

    # Update user wallet only on captured
    if coins_to_credit > 0:
        user.coins_wallet_balance = balance_after
        user.save()

        # Log transaction history
        TransactionHistory(
            parent_id=po.id,
            user_id=user,
            transaction_type="coins_purchase",
            entry_type="credit",
            amount=coins_to_credit,  # number of coins credited
            description=f"Coins purchase: +{coins_to_credit} coins via Razorpay order {order_id}",
            related_user_id=None,
            balance_before=balance_before,
            balance_after=balance_after,
            transaction_status=1  # success
        ).save()

    # Mark PaymentOrder paid
    po.status = "paid"
    po.save()

    return jsonify({
        "status": True,
        "message": "Payment verified and wallet credited",
        "order_status": po.status,
        "coins_added": coins_to_credit,
        "coins_wallet_balance": balance_after
    }), 200



@payments_bp.route("/transactions/history", methods=["POST"])
@jwt_required
def get_transaction_history():
    """
    Body:
    {
      "entry_type": "credit" | "debit"   # optional; if omitted returns all
    }
    Uses user_id from JWT (request.user_id).
    """
    data = request.get_json(silent=True) or {}
    entry_type = data.get("entry_type")
    user_id = getattr(request, "user_id", None)

    if not user_id:
        return jsonify({"status": False, "message": "Unauthorized"}), 401

    if entry_type and entry_type not in ("credit", "debit"):
        return jsonify({"status": False, "message": "Invalid entry_type"}), 400

    # Ensure valid ObjectId
    try:
        uid = ObjectId(str(user_id))
    except Exception:
        return jsonify({"status": False, "message": "Invalid user id"}), 400

    # Build query
    qs = TransactionHistory.objects(user_id=uid)
    if entry_type:
        qs = qs.filter(entry_type=entry_type)

    # Avoid dereferencing User documents to prevent FieldDoesNotExist on unknown fields
    qs = qs.no_dereference().order_by("-created_date")

    # Serialize results
    items = []
    for tx in qs:
        items.append({
            "id": str(tx.id),
            "user_id": str(uid),
            "transaction_type": tx.transaction_type,
            "entry_type": tx.entry_type,
            "amount": tx.amount,
            "description": tx.description,
            "related_user_id": str(tx.related_user_id) if tx.related_user_id else None,
            "balance_before": tx.balance_before,
            "balance_after": tx.balance_after,
            "transaction_status": tx.transaction_status,
            "created_date": tx.created_date.isoformat() if tx.created_date else None,
        })

    return jsonify({
        "status": True,
        "count": len(items),
        "entry_type": entry_type or "all",
        "data": items
    }), 200
