import logging
import uuid
from datetime import timedelta
from decimal import Decimal, InvalidOperation
from functools import wraps

from django.contrib.auth import login
from django.contrib.auth.decorators import login_required
from django.contrib.auth.models import User
from django.http import JsonResponse, HttpResponse
from django.shortcuts import render, redirect, get_object_or_404
from django.utils import timezone
from django.utils.text import slugify
from django.views.decorators.csrf import csrf_exempt
from django.views.decorators.http import require_POST

from .models import Vendor, VendorProduct, ProductImage, VendorSubscription

logger = logging.getLogger(__name__)

MAX_VENDOR_PRODUCTS = 30


# ─────────────────────────────────────────────────────────────────────────────
# Helpers / decorators
# ─────────────────────────────────────────────────────────────────────────────

def vendor_required(view_func):
    @wraps(view_func)
    @login_required
    def _wrapped(request, *args, **kwargs):
        if not hasattr(request.user, "vendor_profile"):
            if request.user.is_staff or request.user.is_superuser:
                return redirect("stores:dashboard")
            return redirect("vendors:register")
        return view_func(request, *args, **kwargs)
    return _wrapped


def subscription_required(view_func):
    """Redirect vendor to subscription page if their sub is not active."""
    @wraps(view_func)
    @vendor_required
    def _wrapped(request, *args, **kwargs):
        vendor = request.user.vendor_profile
        if not vendor.subscription_is_active:
            return redirect("vendors:subscription_required")
        return view_func(request, *args, **kwargs)
    return _wrapped


def _get_aca_settings():
    from stores.models import ACASettings
    return ACASettings.load()


def _build_paystack_callback(request, reference):
    return request.build_absolute_uri(f"/vendor/subscription/verify/?reference={reference}")


# ─────────────────────────────────────────────────────────────────────────────
# Registration
# ─────────────────────────────────────────────────────────────────────────────

def vendor_register(request):
    if request.user.is_authenticated:
        if hasattr(request.user, "vendor_profile"):
            return redirect("vendors:dashboard")

    settings_obj = _get_aca_settings()

    if request.method == "POST":
        username = request.POST.get("username", "").strip()
        email = request.POST.get("email", "").strip()
        password = request.POST.get("password", "").strip()
        password2 = request.POST.get("password2", "").strip()
        store_name = request.POST.get("store_name", "").strip()
        description = request.POST.get("description", "").strip()
        phone = request.POST.get("phone", "").strip()

        errors = []
        if not username:
            errors.append("Username is required.")
        if not email:
            errors.append("Email is required.")
        if not password:
            errors.append("Password is required.")
        if len(password) < 6:
            errors.append("Password must be at least 6 characters.")
        if password != password2:
            errors.append("Passwords do not match.")
        if not store_name:
            errors.append("Store name is required.")
        if User.objects.filter(username=username).exists():
            errors.append("Username already taken.")
        if email and User.objects.filter(email=email).exists():
            errors.append("Email already registered.")

        slug = slugify(store_name) if store_name else ""
        if slug and Vendor.objects.filter(store_slug=slug).exists():
            errors.append("A store with this name already exists.")

        if errors:
            return render(request, "vendors/register.html", {
                "errors": errors,
                "form_data": request.POST,
                "settings": settings_obj,
            })

        user = User.objects.create_user(
            username=username, email=email, password=password
        )
        vendor = Vendor.objects.create(
            user=user,
            store_name=store_name,
            store_slug=slug,
            description=description,
            phone=phone,
            is_approved=True,
        )

        if settings_obj.vendor_registration_free:
            months = settings_obj.vendor_free_duration_months or 1
            now = timezone.now()
            VendorSubscription.objects.create(
                vendor=vendor,
                status=VendorSubscription.STATUS_FREE,
                amount_paid=0,
                currency="GHS",
                activated_at=now,
                expires_at=now + timedelta(days=30 * months),
            )
            login(request, user)
            return redirect("vendors:dashboard")

        reference = f"ACAVND-{slug.upper()[:12]}-{uuid.uuid4().hex[:8].upper()}"
        VendorSubscription.objects.create(
            vendor=vendor,
            status=VendorSubscription.STATUS_PENDING,
            amount_paid=0,
            currency="GHS",
            paystack_reference=reference,
        )

        try:
            from escrow.services.paystack import initialize_payment
            callback_url = _build_paystack_callback(request, reference)
            ps_data = initialize_payment(
                email=email,
                amount_ghs=float(settings_obj.vendor_registration_fee),
                reference=reference,
                callback_url=callback_url,
                metadata={
                    "vendor_id": vendor.id,
                    "store_name": store_name,
                    "aca_vendor_subscription": True,
                },
            )
            request.session["vendor_sub_reference"] = reference
            request.session["vendor_sub_user_id"] = user.id
            return redirect(ps_data["authorization_url"])
        except Exception as exc:
            logger.error("Paystack init for vendor sub failed: %s", exc)
            vendor.delete()
            user.delete()
            return render(request, "vendors/register.html", {
                "errors": [
                    "We could not initiate payment right now. Please try again in a moment."
                ],
                "form_data": request.POST,
                "settings": settings_obj,
            })

    return render(request, "vendors/register.html", {"settings": settings_obj})


# ─────────────────────────────────────────────────────────────────────────────
# Subscription payment verification (Paystack callback)
# ─────────────────────────────────────────────────────────────────────────────

def vendor_subscription_verify(request):
    reference = request.GET.get("reference", "").strip()
    if not reference:
        return redirect("vendors:register")

    try:
        sub = VendorSubscription.objects.select_related("vendor__user").get(
            paystack_reference=reference
        )
    except VendorSubscription.DoesNotExist:
        return render(request, "vendors/subscription_result.html", {
            "success": False,
            "message": "We could not find a subscription matching that payment reference.",
        })

    if sub.status == VendorSubscription.STATUS_ACTIVE:
        login(request, sub.vendor.user)
        return redirect("vendors:dashboard")

    try:
        from escrow.services.paystack import verify_payment
        tx = verify_payment(reference)
        if tx.get("status") != "success":
            return render(request, "vendors/subscription_result.html", {
                "success": False,
                "message": "Payment was not completed. Please try again.",
                "reference": reference,
                "vendor": sub.vendor,
            })
    except Exception as exc:
        logger.error("Paystack verify for vendor sub failed: %s", exc)
        return render(request, "vendors/subscription_result.html", {
            "success": False,
            "message": "Could not verify payment with Paystack. Contact support.",
            "reference": reference,
        })

    settings_obj = _get_aca_settings()
    months = settings_obj.vendor_subscription_months or 12
    now = timezone.now()
    sub.status = VendorSubscription.STATUS_ACTIVE
    sub.amount_paid = settings_obj.vendor_registration_fee
    sub.activated_at = now
    sub.expires_at = now + timedelta(days=30 * months)
    sub.save(update_fields=["status", "amount_paid", "activated_at", "expires_at", "updated_at"])

    logger.info("Vendor subscription activated: %s (ref %s)", sub.vendor.store_name, reference)

    login(request, sub.vendor.user)
    return render(request, "vendors/subscription_result.html", {
        "success": True,
        "vendor": sub.vendor,
        "subscription": sub,
    })


@csrf_exempt
def vendor_subscription_webhook(request):
    if request.method != "POST":
        return HttpResponse(status=405)

    import json
    from escrow.services.paystack import verify_webhook_signature

    signature = request.headers.get("X-Paystack-Signature", "")
    if not verify_webhook_signature(request.body, signature):
        return HttpResponse(status=400)

    try:
        event = json.loads(request.body)
    except json.JSONDecodeError:
        return HttpResponse(status=400)

    if event.get("event") != "charge.success":
        return HttpResponse(status=200)

    data = event.get("data", {})
    reference = data.get("reference", "")
    metadata = data.get("metadata") or {}

    if not metadata.get("aca_vendor_subscription"):
        return HttpResponse(status=200)

    try:
        sub = VendorSubscription.objects.select_related("vendor").get(
            paystack_reference=reference,
            status=VendorSubscription.STATUS_PENDING,
        )
    except VendorSubscription.DoesNotExist:
        return HttpResponse(status=200)

    settings_obj = _get_aca_settings()
    months = settings_obj.vendor_subscription_months or 12
    now = timezone.now()
    sub.status = VendorSubscription.STATUS_ACTIVE
    sub.amount_paid = settings_obj.vendor_registration_fee
    sub.activated_at = now
    sub.expires_at = now + timedelta(days=30 * months)
    sub.save(update_fields=["status", "amount_paid", "activated_at", "expires_at", "updated_at"])

    logger.info("Vendor sub activated via webhook: %s", sub.vendor.store_name)
    return HttpResponse(status=200)


# ─────────────────────────────────────────────────────────────────────────────
# Subscription renewal / expired page
# ─────────────────────────────────────────────────────────────────────────────

@login_required
def subscription_required_page(request):
    if not hasattr(request.user, "vendor_profile"):
        return redirect("vendors:register")

    vendor = request.user.vendor_profile
    if vendor.subscription_is_active:
        return redirect("vendors:dashboard")

    settings_obj = _get_aca_settings()

    try:
        sub = vendor.subscription
    except VendorSubscription.DoesNotExist:
        sub = None

    return render(request, "vendors/subscription_required.html", {
        "vendor": vendor,
        "subscription": sub,
        "settings": settings_obj,
    })


@login_required
@require_POST
def subscription_renew(request):
    if not hasattr(request.user, "vendor_profile"):
        return redirect("vendors:register")

    vendor = request.user.vendor_profile
    settings_obj = _get_aca_settings()

    if settings_obj.vendor_registration_free:
        months = settings_obj.vendor_free_duration_months or 1
        now = timezone.now()
        VendorSubscription.objects.update_or_create(
            vendor=vendor,
            defaults={
                "status": VendorSubscription.STATUS_FREE,
                "amount_paid": 0,
                "activated_at": now,
                "expires_at": now + timedelta(days=30 * months),
                "paystack_reference": "",
            },
        )
        return redirect("vendors:dashboard")

    reference = f"ACARNW-{vendor.store_slug.upper()[:10]}-{uuid.uuid4().hex[:8].upper()}"
    VendorSubscription.objects.update_or_create(
        vendor=vendor,
        defaults={
            "status": VendorSubscription.STATUS_PENDING,
            "amount_paid": 0,
            "paystack_reference": reference,
        },
    )

    try:
        from escrow.services.paystack import initialize_payment
        callback_url = _build_paystack_callback(request, reference)
        ps_data = initialize_payment(
            email=vendor.user.email,
            amount_ghs=float(settings_obj.vendor_registration_fee),
            reference=reference,
            callback_url=callback_url,
            metadata={
                "vendor_id": vendor.id,
                "store_name": vendor.store_name,
                "aca_vendor_subscription": True,
            },
        )
        return redirect(ps_data["authorization_url"])
    except Exception as exc:
        logger.error("Paystack renewal init failed: %s", exc)
        return render(request, "vendors/subscription_required.html", {
            "vendor": vendor,
            "subscription": getattr(vendor, "subscription", None),
            "settings": settings_obj,
            "error": "Could not initiate payment right now. Please try again.",
        })


# ─────────────────────────────────────────────────────────────────────────────
# Dashboard & products
# ─────────────────────────────────────────────────────────────────────────────

@vendor_required
def vendor_dashboard(request):
    vendor = request.user.vendor_profile

    products = vendor.products.prefetch_related("images").all()
    product_count = products.count()

    from cart.models import VendorOrder
    orders = VendorOrder.objects.filter(vendor=vendor).select_related("order")[:10]

    sub = getattr(vendor, "subscription", None)

    return render(request, "vendors/dashboard.html", {
        "vendor": vendor,
        "products": products,
        "orders": orders,
        "product_count": product_count,
        "active_count": products.filter(in_stock=True).count(),
        "product_limit": MAX_VENDOR_PRODUCTS,
        "remaining_slots": max(MAX_VENDOR_PRODUCTS - product_count, 0),
        "at_product_limit": product_count >= MAX_VENDOR_PRODUCTS,
        "subscription": sub,
        "subscription_active": vendor.subscription_is_active,
    })


@subscription_required
def vendor_add_product(request):
    vendor = request.user.vendor_profile
    product_count = vendor.products.count()
    at_product_limit = product_count >= MAX_VENDOR_PRODUCTS

    if request.method == "POST":
        if at_product_limit:
            return render(request, "vendors/add_product.html", {
                "errors": [f"You have reached your product limit of {MAX_VENDOR_PRODUCTS}."],
                "form_data": request.POST,
                "vendor": vendor,
                "product_count": product_count,
                "product_limit": MAX_VENDOR_PRODUCTS,
                "remaining_slots": 0,
                "at_product_limit": True,
            })

        name = request.POST.get("name", "").strip()
        description = request.POST.get("description", "").strip()
        price_str = request.POST.get("price", "").strip()
        currency = request.POST.get("currency", "GHS").strip() or "GHS"
        category = request.POST.get("category", "").strip()
        brand = request.POST.get("brand", "").strip()
        quantity_str = request.POST.get("quantity", "0").strip()

        errors = []
        price = 0
        quantity = 0

        if not name:
            errors.append("Product name is required.")
        if not price_str:
            errors.append("Price is required.")
        else:
            try:
                price = float(price_str)
                if price < 0:
                    errors.append("Price must be positive.")
            except ValueError:
                errors.append("Invalid price.")

        try:
            quantity = int(quantity_str)
            if quantity < 0:
                errors.append("Quantity cannot be negative.")
        except ValueError:
            errors.append("Invalid quantity.")

        images = request.FILES.getlist("images")
        for img in images:
            if not img.content_type.startswith("image/"):
                errors.append(f"'{img.name}' is not a valid image file.")
            if img.size > 5 * 1024 * 1024:
                errors.append(f"'{img.name}' exceeds the 5 MB size limit.")

        if errors:
            return render(request, "vendors/add_product.html", {
                "errors": errors,
                "form_data": request.POST,
                "vendor": vendor,
                "product_count": product_count,
                "product_limit": MAX_VENDOR_PRODUCTS,
                "remaining_slots": max(MAX_VENDOR_PRODUCTS - product_count, 0),
                "at_product_limit": at_product_limit,
            })

        # in_stock is determined by quantity in model.save()
        product = VendorProduct.objects.create(
            vendor=vendor,
            name=name,
            description=description,
            price=price,
            currency=currency,
            category=category,
            brand=brand,
            quantity=quantity,
            # in_stock set automatically by model save()
        )
        for i, img_file in enumerate(images):
            ProductImage.objects.create(product=product, image=img_file, position=i)

        return redirect("vendors:dashboard")

    return render(request, "vendors/add_product.html", {
        "vendor": vendor,
        "product_count": product_count,
        "product_limit": MAX_VENDOR_PRODUCTS,
        "remaining_slots": max(MAX_VENDOR_PRODUCTS - product_count, 0),
        "at_product_limit": at_product_limit,
    })


@subscription_required
def vendor_edit_product(request, product_id):
    vendor = request.user.vendor_profile
    product = get_object_or_404(VendorProduct, id=product_id, vendor=vendor)

    if request.method == "POST":
        name = request.POST.get("name", "").strip()
        price_str = request.POST.get("price", "").strip()
        quantity_str = request.POST.get("quantity", "0").strip()

        errors = []
        price = 0
        quantity = 0

        if not name:
            errors.append("Product name is required.")
        if not price_str:
            errors.append("Price is required.")
        else:
            try:
                price = float(price_str)
                if price < 0:
                    errors.append("Price must be positive.")
            except ValueError:
                errors.append("Invalid price.")

        try:
            quantity = int(quantity_str)
            if quantity < 0:
                errors.append("Quantity cannot be negative.")
        except ValueError:
            errors.append("Invalid quantity.")

        new_images = request.FILES.getlist("images")
        for img in new_images:
            if not img.content_type.startswith("image/"):
                errors.append(f"'{img.name}' is not a valid image file.")
            if img.size > 5 * 1024 * 1024:
                errors.append(f"'{img.name}' exceeds the 5 MB size limit.")

        if errors:
            return render(request, "vendors/edit_product.html", {
                "errors": errors, "product": product, "vendor": vendor,
            })

        product.name = name
        product.description = request.POST.get("description", "").strip()
        product.price = price
        product.currency = request.POST.get("currency", "GHS").strip() or "GHS"
        product.category = request.POST.get("category", "").strip()
        product.brand = request.POST.get("brand", "").strip()
        product.quantity = quantity
        # in_stock set automatically by model save()
        product.save()

        delete_ids = request.POST.get("delete_images", "").strip()
        if delete_ids:
            ids_to_delete = [int(x) for x in delete_ids.split(",") if x.strip().isdigit()]
            ProductImage.objects.filter(id__in=ids_to_delete, product=product).delete()

        existing_max = product.images.order_by("-position").values_list("position", flat=True).first() or 0
        for i, img_file in enumerate(new_images):
            ProductImage.objects.create(product=product, image=img_file, position=existing_max + 1 + i)

        return redirect("vendors:dashboard")

    return render(request, "vendors/edit_product.html", {"product": product, "vendor": vendor})


@vendor_required
@require_POST
def vendor_delete_product(request, product_id):
    vendor = request.user.vendor_profile
    product = get_object_or_404(VendorProduct, id=product_id, vendor=vendor)
    product.delete()
    return redirect("vendors:dashboard")


@vendor_required
@require_POST
def vendor_delete_image(request, image_id):
    vendor = request.user.vendor_profile
    image = get_object_or_404(ProductImage, id=image_id, product__vendor=vendor)
    image.image.delete(save=False)
    image.delete()
    return JsonResponse({"success": True})


@vendor_required
def vendor_orders(request):
    vendor = request.user.vendor_profile

    from cart.models import VendorOrder
    orders = (
        VendorOrder.objects.filter(vendor=vendor)
        .select_related("order")
        .prefetch_related("order__items", "escrow")
        .order_by("-created_at")
    )

    try:
        has_payout = bool(vendor.payout_account.paystack_recipient_code)
    except Exception:
        has_payout = False

    return render(request, "vendors/orders_updated.html", {
        "vendor": vendor,
        "orders": orders,
        "has_payout_account": has_payout,
    })


@vendor_required
@require_POST
def vendor_update_order_status(request, order_id):
    vendor = request.user.vendor_profile
    from cart.models import VendorOrder
    vendor_order = get_object_or_404(VendorOrder, id=order_id, vendor=vendor)
    new_status = request.POST.get("status", "").strip()
    valid_statuses = [s[0] for s in VendorOrder.STATUS_CHOICES]
    if new_status in valid_statuses:
        vendor_order.status = new_status
        vendor_order.save(update_fields=["status", "updated_at"])
    return redirect("vendors:orders")


@vendor_required
def vendor_settings(request):
    vendor = request.user.vendor_profile

    if request.method == "POST":
        vendor.store_name = request.POST.get("store_name", vendor.store_name).strip()
        vendor.description = request.POST.get("description", "").strip()
        vendor.logo_url = request.POST.get("logo_url", "").strip()
        vendor.phone = request.POST.get("phone", "").strip()
        vendor.address = request.POST.get("address", "").strip()
        delivery_fee_raw = request.POST.get("delivery_fee", "").strip()
        free_delivery_threshold_raw = request.POST.get("free_delivery_threshold", "").strip()

        try:
            vendor.delivery_fee = Decimal(delivery_fee_raw or "0")
            if vendor.delivery_fee < 0:
                raise InvalidOperation()
        except Exception:
            vendor.delivery_fee = Decimal("0")

        try:
            if free_delivery_threshold_raw:
                threshold = Decimal(free_delivery_threshold_raw)
                vendor.free_delivery_threshold = threshold if threshold >= 0 else None
            else:
                vendor.free_delivery_threshold = None
        except Exception:
            vendor.free_delivery_threshold = None

        new_slug = slugify(vendor.store_name)
        if not Vendor.objects.filter(store_slug=new_slug).exclude(id=vendor.id).exists():
            vendor.store_slug = new_slug

        vendor.save()
        return render(request, "vendors/store_settings.html", {"vendor": vendor, "saved": True})

    return render(request, "vendors/store_settings.html", {"vendor": vendor})
