"""Back-office views: member-facing, gated by main_character + basic_access."""

from allianceauth.eveonline.evelinks.eveimageserver import character_portrait_url
from django.contrib.auth.decorators import login_required, permission_required
from django.db.models import Count, Prefetch, Q
from django.http import Http404, HttpResponseForbidden
from django.shortcuts import get_object_or_404, redirect, render
from django.views.decorators.http import require_POST

from ..forms import AddStockroomForm, ShopForm
from ..market import jita_prices
from ..models import (
    AUTO_STOCK,
    BPC,
    FITTED,
    FIXED_PRICE,
    HULL,
    MODULES,
    PCT_MODES,
    SECTION_CHOICES,
    BpcListing,
    FittedListing,
    HullListing,
    ModuleListing,
    Order,
    OrderLine,
    Shop,
    ShopSource,
)
from ..permissions import can_manage, corp_shops_for, personal_shops_for
from ..sections import handler_for
from ..sections.hulls.forms import AddHullForm, EditHullForm
from ..sources import container_name_for


@login_required
@permission_required("shop.basic_access")
def index(request):
    open_q = Q(orders__status__in=Order.OPEN_STATUSES)
    return render(
        request,
        "shop/index.html",
        {
            "personal_shops": personal_shops_for(request.user).annotate(
                open_orders=Count("orders", filter=open_q)
            ),
            "corp_shops": corp_shops_for(request.user).annotate(
                open_orders=Count("orders", filter=open_q)
            ),
            "can_manage_corp": (
                request.user.has_perm("shop.manage_corporation_shops")
                or request.user.is_superuser
            ),
        },
    )


@login_required
@permission_required("shop.basic_access")
def create(request):
    if request.method == "POST":
        form = ShopForm(request.POST, user=request.user)
        if form.is_valid():
            form.save()
            return redirect("backoffice:index")
    else:
        form = ShopForm(user=request.user)
    return render(request, "shop/shop_form.html", {"form": form, "is_edit": False})


@login_required
@permission_required("shop.basic_access")
def edit(request, pk):
    shop = get_object_or_404(Shop, pk=pk)
    if not can_manage(request.user, shop):
        return HttpResponseForbidden()
    if request.method == "POST":
        form = ShopForm(request.POST, instance=shop, user=request.user)
        if form.is_valid():
            form.save()
            return redirect("backoffice:index")
    else:
        form = ShopForm(instance=shop, user=request.user)
    return render(
        request, "shop/shop_form.html", {"form": form, "is_edit": True, "shop": shop}
    )


@login_required
@permission_required("shop.basic_access")
def delete(request, pk):
    shop = get_object_or_404(Shop, pk=pk)
    if not can_manage(request.user, shop):
        return HttpResponseForbidden()
    if request.method == "POST":
        shop.delete()
        return redirect("backoffice:index")
    return render(request, "shop/shop_confirm_delete.html", {"shop": shop})


@login_required
@permission_required("shop.basic_access")
def manage(request, pk):
    """Bare manage URL — forward to the first enabled section (or a prompt)."""
    shop = get_object_or_404(Shop, pk=pk)
    if not can_manage(request.user, shop):
        return HttpResponseForbidden()
    section = shop.first_enabled_section()
    if section is None:
        return render(request, "shop/manage_no_sections.html", {"shop": shop})
    return redirect("backoffice:manage_section", pk=shop.pk, section=section)


@login_required
@permission_required("shop.basic_access")
def manage_section(request, pk, section):
    shop = get_object_or_404(Shop, pk=pk)
    if not can_manage(request.user, shop):
        return HttpResponseForbidden()
    if section not in dict(SECTION_CHOICES) or not shop.section_enabled(section):
        raise Http404("Section not found")
    ctx = {"shop": shop, "section": section, "sections": shop.enabled_section_choices()}
    handler = handler_for(section)
    if handler is None:  # section enabled but not yet implemented (modules, fitted)
        return render(request, "shop/manage_section_stub.html", ctx)
    ctx.update(handler.manage_context(shop, request))
    return render(request, handler.manage_template, ctx)


@login_required
@permission_required("shop.basic_access")
def orders(request, pk):
    shop = get_object_or_404(Shop, pk=pk)
    if not can_manage(request.user, shop):
        return HttpResponseForbidden()
    order_qs = (
        shop.orders.select_related("buyer")
        .prefetch_related("lines")
        .order_by("-created_at")
    )
    return render(request, "shop/orders.html", {"shop": shop, "orders": order_qs})


@login_required
@permission_required("shop.basic_access")
def order_detail(request, pk, reference):
    shop = get_object_or_404(Shop, pk=pk)
    if not can_manage(request.user, shop):
        return HttpResponseForbidden()
    ordered_lines = OrderLine.objects.select_related("eve_type").order_by(
        "section", "eve_type__name"
    )
    order = get_object_or_404(
        shop.orders.select_related("buyer").prefetch_related(
            Prefetch("lines", queryset=ordered_lines)
        ),
        reference=reference,
    )
    # Attach the reconstructed fit + EFT to fitted-ship lines for the fit modal.
    from ..sections.fitted.reconstruct import fit_display_groups, fit_to_eft

    for line in order.lines.all():
        if line.section == FITTED and line.fit_json:
            line.fit_display = fit_display_groups(line.fit_json)
            line.fit_eft = fit_to_eft(
                line.eve_type.name, line.label or line.eve_type.name, line.fit_json
            )
        else:
            line.fit_display = None
    return render(
        request,
        "shop/order_detail.html",
        {
            "shop": shop,
            "order": order,
            "buyer_portrait": character_portrait_url(order.buyer.character_id, 64),
            "status_choices": Order.STATUS_CHOICES,
        },
    )


@login_required
@permission_required("shop.basic_access")
@require_POST
def set_order_status(request, pk, reference):
    shop = get_object_or_404(Shop, pk=pk)
    if not can_manage(request.user, shop):
        return HttpResponseForbidden()
    order = get_object_or_404(shop.orders, reference=reference)
    status = request.POST.get("status")
    if status in dict(Order.STATUS_CHOICES):
        order.status = status
        order.save(update_fields=["status", "updated_at"])
    return redirect("backoffice:order_detail", pk=shop.pk, reference=reference)


@login_required
@permission_required("shop.basic_access")
def add_stockroom(request, pk, section):
    shop = get_object_or_404(Shop, pk=pk)
    if not can_manage(request.user, shop):
        return HttpResponseForbidden()
    if section not in dict(SECTION_CHOICES) or not shop.section_enabled(section):
        raise Http404("Section not found")
    handler = handler_for(section)
    form_class = handler.stockroom_form_class if handler else AddStockroomForm
    if request.method == "POST":
        form = form_class(request.POST, owner=request.user, shop=shop)
        if form.is_valid():
            container_item_id = form.cleaned_data.get("container_item_id") or None
            is_corp = shop.shop_type == Shop.ShopType.CORPORATION
            owner_id = form.cleaned_data["owner_id"]
            source = ShopSource.objects.create(
                shop=shop,
                section=section,
                corporation_audit_id=owner_id if is_corp else None,
                character_audit_id=None if is_corp else owner_id,
                location_id=form.cleaned_data["location_id"],
                container_item_id=container_item_id,
                container_name=container_name_for(
                    owner_id, container_item_id, is_corp=is_corp
                ),
            )
            if handler:
                handler.stockroom_added(source, form.cleaned_data)
    return redirect("backoffice:manage_section", pk=shop.pk, section=section)


@login_required
@permission_required("shop.basic_access")
def edit_stockroom(request, pk, source_id):
    shop = get_object_or_404(Shop, pk=pk)
    if not can_manage(request.user, shop):
        return HttpResponseForbidden()
    source = get_object_or_404(ShopSource, pk=source_id, shop=shop)
    handler = handler_for(source.section)
    if request.method == "POST" and handler and handler.edit_stockroom_form_class:
        form = handler.edit_stockroom_form_class(request.POST)
        if form.is_valid():
            handler.stockroom_edited(source, form.cleaned_data)
    return redirect("backoffice:manage_section", pk=shop.pk, section=source.section)


@login_required
@permission_required("shop.basic_access")
def remove_stockroom(request, pk, source_id):
    shop = get_object_or_404(Shop, pk=pk)
    if not can_manage(request.user, shop):
        return HttpResponseForbidden()
    source = get_object_or_404(ShopSource, pk=source_id, shop=shop)
    section = source.section
    if request.method == "POST":
        handler = handler_for(section)
        source.delete()
        if handler:
            handler.stockroom_removed(shop)
    return redirect("backoffice:manage_section", pk=shop.pk, section=section)


def _apply_modes(listing, cd):
    """Apply stock/price mode fields from a validated form to a listing + save.

    Auto stock recomputes from sources; a pct price mode fetches a live Jita price
    synchronously (single listing) and keeps-last-known if ESI is unavailable.
    """
    listing.stock_mode = cd["stock_mode"]
    listing.price_mode = cd["price_mode"]
    listing.price_pct = cd.get("price_pct")
    listing.persist = cd["persist"]
    listing.description = cd.get("description", "")
    if cd["price_mode"] == FIXED_PRICE:
        listing.price = cd.get("price") or 0
    if cd["stock_mode"] != AUTO_STOCK:
        listing.stock = cd.get("stock") or 0
    listing.save()
    if listing.stock_mode == AUTO_STOCK:
        listing.recompute_stock()
    if listing.price_mode in PCT_MODES:
        try:
            p = jita_prices(listing.eve_type_id)
            if listing.apply_jita_price(p["buy"], p["sell"]):
                listing.save(update_fields=["price"])
        except Exception:  # noqa: BLE001 - keep last known; cron backfills
            pass


@login_required
@permission_required("shop.basic_access")
def add_hull(request, pk):
    shop = get_object_or_404(Shop, pk=pk)
    if not can_manage(request.user, shop):
        return HttpResponseForbidden()
    if request.method == "POST":
        form = AddHullForm(request.POST)
        if form.is_valid():
            listing, _ = HullListing.objects.get_or_create(
                shop=shop, eve_type=form.cleaned_data["eve_type"]
            )
            _apply_modes(listing, form.cleaned_data)
    return redirect("backoffice:manage_section", pk=shop.pk, section=HULL)


@login_required
@permission_required("shop.basic_access")
def edit_hull(request, pk, listing_id):
    shop = get_object_or_404(Shop, pk=pk)
    if not can_manage(request.user, shop):
        return HttpResponseForbidden()
    listing = get_object_or_404(HullListing, pk=listing_id, shop=shop)
    if request.method == "POST":
        form = EditHullForm(request.POST)
        if form.is_valid():
            _apply_modes(listing, form.cleaned_data)
    return redirect("backoffice:manage_section", pk=shop.pk, section=HULL)


@login_required
@permission_required("shop.basic_access")
def remove_hull(request, pk, listing_id):
    shop = get_object_or_404(Shop, pk=pk)
    if not can_manage(request.user, shop):
        return HttpResponseForbidden()
    if request.method == "POST":
        HullListing.objects.filter(pk=listing_id, shop=shop).delete()
    return redirect("backoffice:manage_section", pk=shop.pk, section=HULL)


def _apply_module_price(listing, cd):
    """Apply a per-listing price rule (fixed or % Jita) + save. A pct mode fetches a
    live Jita price synchronously and keeps last-known if ESI is unavailable."""
    listing.price_mode = cd["price_mode"]
    listing.price_pct = cd.get("price_pct")
    if cd["price_mode"] == FIXED_PRICE:
        listing.price = cd.get("price") or 0
    listing.save()
    if listing.price_mode in PCT_MODES:
        try:
            p = jita_prices(listing.eve_type_id)
            if listing.apply_jita_price(p["buy"], p["sell"]):
                listing.save(update_fields=["price"])
        except Exception:  # noqa: BLE001 - keep last known; cron backfills
            pass


@login_required
@permission_required("shop.basic_access")
def edit_module(request, pk, listing_id):
    shop = get_object_or_404(Shop, pk=pk)
    if not can_manage(request.user, shop):
        return HttpResponseForbidden()
    listing = get_object_or_404(ModuleListing, pk=listing_id, shop=shop)
    if request.method == "POST":
        from ..sections.modules.forms import EditModuleForm

        form = EditModuleForm(request.POST)
        if form.is_valid():
            _apply_module_price(listing, form.cleaned_data)
    return redirect("backoffice:manage_section", pk=shop.pk, section=MODULES)


@login_required
@permission_required("shop.basic_access")
def remove_module(request, pk, listing_id):
    shop = get_object_or_404(Shop, pk=pk)
    if not can_manage(request.user, shop):
        return HttpResponseForbidden()
    if request.method == "POST":
        ModuleListing.objects.filter(pk=listing_id, shop=shop).delete()
    return redirect("backoffice:manage_section", pk=shop.pk, section=MODULES)


@login_required
@permission_required("shop.basic_access")
def edit_fitted(request, pk, listing_id):
    shop = get_object_or_404(Shop, pk=pk)
    if not can_manage(request.user, shop):
        return HttpResponseForbidden()
    listing = get_object_or_404(FittedListing, pk=listing_id, shop=shop)
    if request.method == "POST":
        from ..sections.fitted.forms import EditFittedForm

        form = EditFittedForm(request.POST)
        if form.is_valid():
            listing.price = form.cleaned_data.get("price") or 0
            listing.persist = form.cleaned_data.get("persist") or False
            listing.description = form.cleaned_data.get("description") or ""
            listing.save(update_fields=["price", "persist", "description"])
    return redirect("backoffice:manage_section", pk=shop.pk, section=FITTED)


@login_required
@permission_required("shop.basic_access")
def remove_fitted(request, pk, listing_id):
    shop = get_object_or_404(Shop, pk=pk)
    if not can_manage(request.user, shop):
        return HttpResponseForbidden()
    if request.method == "POST":
        FittedListing.objects.filter(pk=listing_id, shop=shop).delete()
    return redirect("backoffice:manage_section", pk=shop.pk, section=FITTED)


@login_required
@permission_required("shop.basic_access")
def toggle_featured(request, pk, section, listing_id):
    """Star/unstar a listing in any section that supports featuring."""
    shop = get_object_or_404(Shop, pk=pk)
    if not can_manage(request.user, shop):
        return HttpResponseForbidden()
    handler = handler_for(section)
    model = getattr(handler, "featured_model", None)
    if request.method == "POST" and model is not None:
        listing = get_object_or_404(model, pk=listing_id, shop=shop)
        listing.toggle_featured()
    return redirect("backoffice:manage_section", pk=shop.pk, section=section)


@login_required
@permission_required("shop.basic_access")
def save_bpc_prices(request, pk, listing_id):
    shop = get_object_or_404(Shop, pk=pk)
    if not can_manage(request.user, shop):
        return HttpResponseForbidden()
    listing = get_object_or_404(BpcListing, pk=listing_id, shop=shop)
    if request.method == "POST":
        for variant in listing.variants.all():
            fields = []
            raw = request.POST.get(f"price_{variant.pk}")
            if raw is not None:
                try:
                    price = max(0, int(raw))
                except (TypeError, ValueError):
                    price = variant.price
                if price != variant.price:
                    variant.price = price
                    variant.price_per_run = variant.compute_price_per_run()
                    fields += ["price", "price_per_run"]
            hidden = f"hidden_{variant.pk}" in request.POST
            if hidden != variant.hidden:
                variant.hidden = hidden
                fields.append("hidden")
            if fields:
                variant.save(update_fields=fields)
    return redirect("backoffice:manage_section", pk=shop.pk, section=BPC)
