from django_filters.rest_framework import DjangoFilterBackend
from rest_framework.filters import SearchFilter
from rest_framework.permissions import IsAuthenticated
from rest_framework.decorators import action
from decimal import Decimal
import json
import re
from rest_framework.response import Response
from rest_framework import status
from rest_framework.exceptions import ValidationError
from django.db import transaction, models
from django.utils import timezone
from account.permissions import (
    HasTabPermission,
    user_can_see_all_shop_data,
    get_user_data_filter,
    get_user_shop_filter,
)
from shop_management.views.data.base import DataRootViewSet
from shop_management.views.data.pagination import CustomPagination
from shop_management.serializers.data.stock import StockSerializer,Stock
from shop_management.serializers.data.stock_transfer import StockTransferSerializer
from shop_management.models.data.stock_transfer import StockTransfer
from shop_management.models.data.shop import Shop
from shop_management.models.data.product import Product, Category
from shop_management.models.data.supplier import Supplier
from shop_management.models.data.customer import Customer
from shop_management.models.data.utils import Unit, Currency, PaymentStatus
from shop_management.models.data.purchase import Purchase, PurchaseItem
from shop_management.models.data.purchase_payment import PurchasePayment
from shop_management.models.data.sale import Sale, SaleItem
from shop_management.models.data.account_transaction import AccountTransaction


def normalize_currency_code(value):
    code = str(value or "").strip().upper()
    if code in ("AFN", "AFG") or "AFGHAN" in code or "افغ" in code:
        return "AFG"
    if code == "USD" or "DOLLAR" in code or "دالر" in code:
        return "USD"
    return code


def resolve_shop_currency(shop_id, currency_or_id, user=None, create=False):
    """Map any currency (or PK) to the matching Currency row for ``shop_id``.

    Currencies are shop-scoped, so transfers must never reuse another branch's
    Currency PK when reading or writing destination stock.
    """
    if not shop_id:
        return None

    source = currency_or_id
    if source is not None and not isinstance(source, Currency):
        try:
            source = Currency.objects.filter(pk=int(source)).first()
        except (TypeError, ValueError):
            source = None
    if not source:
        return None
    if source.shop_id == int(shop_id):
        return source

    code = normalize_currency_code(source.title)
    titles = []
    if source.title:
        titles.append(source.title)
    if code and code not in {t.upper() for t in titles}:
        titles.append(code)

    for title in titles:
        match = Currency.objects.filter(shop_id=shop_id, title__iexact=title).first()
        if match:
            return match

    if code:
        for candidate in Currency.objects.filter(shop_id=shop_id):
            if normalize_currency_code(candidate.title) == code:
                return candidate

    if create and (code or source.title):
        return Currency.objects.create(
            shop_id=shop_id,
            title=code or source.title,
            created_by=user,
        )
    return None


class StockViewSet(DataRootViewSet):
    serializer_class = StockSerializer
    permission_classes = [IsAuthenticated, HasTabPermission]
    required_tab = "stock"
    filter_backends = [DjangoFilterBackend, SearchFilter]
    filterset_fields = ['shop', 'purchase_item', 'purchase_item__product', 'purchase_item__product__category']
    search_fields = [
        'shop__name',
        'purchase_item__product__name',
        'purchase_item__product__code',
        'purchase_item__purchase__supplier__name',
        'shop__owner__username'
    ]
    pagination_class = CustomPagination

    def get_queryset(self):
        base_filter = dict(
            is_deleted=False,
            quantity__gt=0,
            purchase_item__isnull=False,
            purchase_item__is_deleted=False,
            purchase_item__purchase__is_deleted=False
        )
        if user_can_see_all_shop_data(self.request.user, self.required_tab):
            return Stock.objects.filter(**base_filter).order_by('-id')
        return (
            Stock.objects.filter(get_user_data_filter(self.request.user, "shop"), **base_filter)
            .distinct()
            .order_by("-id")
        )
    
    def filter_queryset(self, queryset):
        queryset = super().filter_queryset(queryset)
        
        # Handle product filter explicitly
        product = self.request.query_params.get('purchase_item__product', None)
        if product:
            try:
                product_id = int(product)
                queryset = queryset.filter(purchase_item__product_id=product_id)
            except (ValueError, TypeError):
                pass

        category = self.request.query_params.get('purchase_item__product__category', None)
        if category:
            try:
                category_id = int(category)
                queryset = queryset.filter(purchase_item__product__category_id=category_id)
            except (ValueError, TypeError):
                pass
        
        return queryset

    def destroy(self, request, *args, **kwargs):
        permanent = str(request.query_params.get("permanent", "")).lower() in ("1", "true", "yes")
        if not permanent:
            return super().destroy(request, *args, **kwargs)

        with transaction.atomic():
            stock = Stock.objects.select_for_update().filter(pk=kwargs.get("pk")).first()
            if not stock:
                return Response(status=status.HTTP_404_NOT_FOUND)

            # StockTransfer keeps PROTECT links to stock rows. Clear those links first
            # so a requested permanent stock delete does not fail with a 500 error.
            StockTransfer.objects.select_for_update().filter(source_stock=stock).update(source_stock=None)
            StockTransfer.objects.select_for_update().filter(target_stock=stock).update(target_stock=None)
            stock.delete()

        return Response(status=status.HTTP_204_NO_CONTENT)

    def _normalize_currency_code(self, value):
        return normalize_currency_code(value)

    def _decimal_or_none(self, value):
        if value in (None, ""):
            return None
        try:
            return Decimal(str(value))
        except Exception:
            return None

    def _filter_stock_by_currency(self, queryset, currency_id):
        if not currency_id:
            return queryset
        return queryset.filter(
            models.Q(purchase_item__currency_id=currency_id)
            | models.Q(
                purchase_item__currency__isnull=True,
                purchase_item__purchase__currency_id=currency_id,
            )
        )

    def _stock_price_by_currency(self, stock):
        currency = self._normalize_currency_code(
            stock.currency.title if stock.currency else ""
        )
        if not currency:
            return {}

        extra_prices = {}
        if stock.comments:
            try:
                parsed = json.loads(stock.comments)
                if isinstance(parsed, dict):
                    extra_prices = parsed.get("price_by_currency", {}) or {}
            except Exception:
                extra_prices = {}

        price_key = "buy_price_afg" if currency == "AFG" else "buy_price_usd" if currency == "USD" else ""
        extra_price = self._decimal_or_none(extra_prices.get(price_key)) if price_key else None
        current_price = self._decimal_or_none(
            stock.average_buy_price if getattr(stock, "average_buy_price", None) is not None else (
                stock.purchase_item.buy_price if stock.purchase_item else None
            )
        )
        price = current_price if current_price is not None else extra_price
        return {currency: price} if price is not None else {}

    @action(detail=False, methods=["get"], url_path="summary")
    def summary(self, request):
        queryset = self.filter_queryset(self.get_queryset()).select_related(
            "purchase_item",
            "purchase_item__product",
            "purchase_item__purchase",
            "purchase_item__purchase__currency",
        )

        product_ids = set()
        total_quantity = Decimal("0")
        totals_by_currency = {}

        for stock in queryset:
            quantity = Decimal(str(stock.quantity or 0))
            if quantity <= 0:
                continue

            product_id = stock.purchase_item.product_id if stock.purchase_item else None
            if product_id:
                product_ids.add(product_id)

            total_quantity += quantity
            for currency, price in self._stock_price_by_currency(stock).items():
                totals_by_currency[currency] = (
                    totals_by_currency.get(currency, Decimal("0")) + quantity * price
                )

        stock_values = [
            {"currency": currency, "amount": str(amount.quantize(Decimal("0.01")))}
            for currency, amount in sorted(totals_by_currency.items())
        ]

        return Response(
            {
                "item_count": len(product_ids),
                "available_qty": str(total_quantity),
                "stock_value": stock_values,
                "status": "In Stock" if total_quantity > 0 else "Out of Stock",
            }
        )

    @action(detail=False, methods=["post"], url_path="import_excel")
    def import_excel(self, request):
        rows = request.data.get("rows", [])
        if not isinstance(rows, list) or not rows:
            return Response({"detail": "No rows provided."}, status=status.HTTP_400_BAD_REQUEST)

        user_shops = Shop.objects.filter(get_user_shop_filter(request.user))
        if not user_shops.exists():
            return Response({"detail": "No accessible shop found for user."}, status=status.HTTP_400_BAD_REQUEST)
        selected_shop_id = (
            request.data.get("shop")
            or request.headers.get("X-Selected-Shop")
            or request.META.get("HTTP_X_SELECTED_SHOP")
        )
        default_shop = (
            user_shops.filter(pk=selected_shop_id).first()
            if selected_shop_id
            else user_shops.first()
        ) or user_shops.first()
        shop_by_normalized_name = {
            (shop.name or "").strip().lower(): shop for shop in user_shops
        }

        created = 0
        skipped = []
        pending_purchases = {}

        with transaction.atomic():
            price_col_map = {}
            for index, row in enumerate(rows, start=1):
                arabic_digits = str.maketrans("٠١٢٣٤٥٦٧٨٩۰۱۲۳۴۵۶۷۸۹", "01234567890123456789")

                def _norm_text(v):
                    return str(v or "").strip().lower().replace(" ", "_")

                def find_value_by_key(raw_row, accepted_keys):
                    for k, v in raw_row.items():
                        key = _norm_text(k)
                        if key in accepted_keys:
                            return v
                    return None

                def parse_number(value):
                    if value in (None, ""):
                        return None
                    if isinstance(value, (int, float, Decimal)):
                        return float(value)
                    text = str(value).strip().translate(arabic_digits)
                    if not text:
                        return None
                    text = text.replace("\u00A0", "").replace(" ", "")
                    text = re.sub(r"[^0-9,.\-]", "", text)
                    if not text:
                        return None
                    if "," in text and "." in text:
                        text = text.replace(",", "")
                    elif "," in text:
                        parts = text.split(",")
                        if len(parts) == 2 and len(parts[1]) <= 2:
                            text = ".".join(parts)
                        else:
                            text = "".join(parts)
                    return float(text)

                # Support two-header Excel layout like:
                # Buy Price -> AFG/USD, Wholesale Price -> AFG/USD, Retail Price -> AFG/USD
                # If frontend sends sheet_to_json with first row as headers, the row that contains
                # "AFG/USD" will appear as data. Convert that row into dynamic keys for following rows.
                # Detect second header row containing AFG/USD and map actual columns.
                if not price_col_map:
                    values_upper = [str(v).strip().upper() for v in row.values()]
                    if values_upper.count("AFG") + values_upper.count("USD") >= 3:
                        keys = list(row.keys())
                        norm_keys = [_norm_text(k) for k in keys]
                        buy_idx = norm_keys.index("buy_price") if "buy_price" in norm_keys else -1
                        wh_idx = norm_keys.index("wholesale_price") if "wholesale_price" in norm_keys else -1
                        rt_idx = norm_keys.index("retail_price") if "retail_price" in norm_keys else -1

                        if buy_idx >= 0 and buy_idx + 1 < len(keys):
                            price_col_map["buy_price_afg"] = keys[buy_idx]
                            price_col_map["buy_price_usd"] = keys[buy_idx + 1]
                        if wh_idx >= 0 and wh_idx + 1 < len(keys):
                            price_col_map["wholesale_price_afg"] = keys[wh_idx]
                            price_col_map["wholesale_price_usd"] = keys[wh_idx + 1]
                        if rt_idx >= 0 and rt_idx + 1 < len(keys):
                            price_col_map["retail_price_afg"] = keys[rt_idx]
                            price_col_map["retail_price_usd"] = keys[rt_idx + 1]

                        # Positional fallback: detect the first 6 AFG/USD cells and map as
                        # buy(AFG,USD), wholesale(AFG,USD), retail(AFG,USD).
                        if not all(
                            k in price_col_map
                            for k in (
                                "buy_price_afg",
                                "buy_price_usd",
                                "wholesale_price_afg",
                                "wholesale_price_usd",
                                "retail_price_afg",
                                "retail_price_usd",
                            )
                        ):
                            currency_col_keys = []
                            for k in keys:
                                v = str(row.get(k, "")).strip().upper()
                                if v in ("AFG", "USD"):
                                    currency_col_keys.append(k)
                            if len(currency_col_keys) >= 6:
                                (
                                    price_col_map["buy_price_afg"],
                                    price_col_map["buy_price_usd"],
                                    price_col_map["wholesale_price_afg"],
                                    price_col_map["wholesale_price_usd"],
                                    price_col_map["retail_price_afg"],
                                    price_col_map["retail_price_usd"],
                                ) = currency_col_keys[:6]

                        skipped.append({"row": index, "reason": "Detected AFG/USD header row (ignored)"})
                        continue

                normalized_row = {
                    (str(k).strip().lower().replace(" ", "_") if k is not None else ""): v
                    for k, v in row.items()
                }

                product_name = (
                    normalized_row.get("product")
                    or normalized_row.get("product_name")
                    or normalized_row.get("item")
                    or normalized_row.get("item_name")
                    or normalized_row.get("name")
                    or ""
                )
                product_code = (
                    normalized_row.get("code")
                    or normalized_row.get("product_code")
                    or normalized_row.get("item_code")
                    or ""
                )
                # Fallback for Excel merged/shifted headers where key may differ.
                if not product_code:
                    product_code = find_value_by_key(
                        row,
                        {
                            "code",
                            "product_code",
                            "productcode",
                        },
                    ) or ""
                supplier_name = (
                    normalized_row.get("supplier")
                    or normalized_row.get("supplier_name")
                    or normalized_row.get("vendor")
                    or ""
                )
                unit_title = (
                    normalized_row.get("unit")
                    or normalized_row.get("unit_title")
                    or normalized_row.get("uom")
                    or "PCS"
                )
                currency_title = (
                    normalized_row.get("currency")
                    or normalized_row.get("currency_title")
                    or "AFN"
                )
                branch_name = (
                    normalized_row.get("branch")
                    or normalized_row.get("shop")
                    or normalized_row.get("branch_name")
                    or normalized_row.get("shop_name")
                )

                quantity = (
                    normalized_row.get("quantity")
                    or normalized_row.get("qty")
                    or normalized_row.get("available_quantity")
                    or normalized_row.get("available_qty")
                    or normalized_row.get("stock_qty")
                )
                buy_price = (
                    normalized_row.get("buy_price")
                    or normalized_row.get("cost_price")
                    or normalized_row.get("purchase_price")
                    or 0
                )
                wholesale_price = normalized_row.get("wholesale_price") or normalized_row.get("wholesale")
                retail_price = (
                    normalized_row.get("retail_price")
                    or normalized_row.get("sale_price")
                    or normalized_row.get("selling_price")
                )
                sale_price = normalized_row.get("sale_price")
                currency_title_from_row = normalized_row.get("currency")

                # Pull values from multi-header columns if present
                def _num(v):
                    return parse_number(v)

                buy_afg = _num(row.get(price_col_map.get("buy_price_afg", "")))
                buy_usd = _num(row.get(price_col_map.get("buy_price_usd", "")))
                wh_afg = _num(row.get(price_col_map.get("wholesale_price_afg", "")))
                wh_usd = _num(row.get(price_col_map.get("wholesale_price_usd", "")))
                rt_afg = _num(row.get(price_col_map.get("retail_price_afg", "")))
                rt_usd = _num(row.get(price_col_map.get("retail_price_usd", "")))

                # Fallback for flat headers (single-row format) like buy_price_usd, retail_price_afg, etc.
                if buy_afg is None:
                    buy_afg = _num(normalized_row.get("buy_price_afg"))
                if buy_usd is None:
                    buy_usd = _num(normalized_row.get("buy_price_usd"))
                if wh_afg is None:
                    wh_afg = _num(normalized_row.get("wholesale_price_afg"))
                if wh_usd is None:
                    wh_usd = _num(normalized_row.get("wholesale_price_usd"))
                if rt_afg is None:
                    rt_afg = _num(normalized_row.get("retail_price_afg"))
                if rt_usd is None:
                    rt_usd = _num(normalized_row.get("retail_price_usd"))

                # Build a single payload per row (1 row = 1 purchase item + 1 stock).
                # If both AFG/USD exist, prefer explicit currency from row; otherwise default to AFG.
                explicit_currency = (str(currency_title_from_row).strip().upper() if currency_title_from_row else "")
                chosen_currency = explicit_currency if explicit_currency in ("AFG", "USD") else "AFG"
                if chosen_currency == "USD":
                    selected_buy = buy_usd if buy_usd is not None else buy_price
                    selected_wh = wh_usd if wh_usd is not None else wholesale_price
                    selected_rt = rt_usd if rt_usd is not None else retail_price
                else:
                    selected_buy = buy_afg if buy_afg is not None else buy_price
                    selected_wh = wh_afg if wh_afg is not None else wholesale_price
                    selected_rt = rt_afg if rt_afg is not None else retail_price

                product_name = str(product_name).strip()
                product_code = str(product_code).strip()
                supplier_name = str(supplier_name).strip()
                unit_title = str(unit_title).strip()
                currency_title = str(currency_title).strip()
                branch_name = str(branch_name).strip() if branch_name not in (None, "") else ""

                row_shop = default_shop
                if branch_name:
                    matched_shop = shop_by_normalized_name.get(branch_name.lower())
                    if not matched_shop:
                        matched_shop = user_shops.filter(name__iexact=branch_name).first()
                    if not matched_shop:
                        matched_shop = Shop.objects.create(
                            owner=request.user,
                            name=branch_name,
                            created_by=request.user,
                        )
                        user_shops = user_shops | Shop.objects.filter(pk=matched_shop.pk)
                        shop_by_normalized_name[branch_name.lower()] = matched_shop
                    row_shop = matched_shop

                if not product_name:
                    skipped.append({"row": index, "reason": "Missing product name"})
                    continue

                try:
                    quantity_val = parse_number(quantity)
                    quantity = int(quantity_val) if quantity_val is not None else None
                    sale_price_val = parse_number(sale_price)
                except (TypeError, ValueError):
                    skipped.append({"row": index, "reason": "Invalid numeric value"})
                    continue

                if quantity is None:
                    skipped.append({"row": index, "reason": "Invalid numeric value"})
                    continue

                if quantity <= 0:
                    skipped.append({"row": index, "reason": "Quantity must be greater than 0"})
                    continue

                product, product_created = Product.objects.get_or_create(
                    shop=row_shop,
                    name=product_name,
                    defaults={"created_by": request.user, "code": product_code or None},
                )
                if product_code and product.code != product_code:
                    product.code = product_code
                    product.save(update_fields=["code"])
                supplier = None
                if supplier_name:
                    supplier, _ = Supplier.objects.get_or_create(
                        shop=row_shop,
                        name=supplier_name,
                        defaults={"created_by": request.user},
                    )
                unit = Unit.objects.filter(shop=row_shop, title__iexact=unit_title).first() or Unit.objects.create(
                    shop=row_shop, title=unit_title, created_by=request.user
                )
                currency_code = chosen_currency or currency_title or "AFG"
                currency = Currency.objects.filter(
                    shop=row_shop, title__iexact=currency_code
                ).first() or Currency.objects.create(
                    shop=row_shop, title=currency_code, created_by=request.user
                )

                try:
                    p_buy = parse_number(selected_buy)
                    if p_buy is None:
                        p_buy = sale_price_val or 0
                    p_wh = parse_number(selected_wh)
                    p_rt = parse_number(selected_rt)
                except (TypeError, ValueError):
                    skipped.append({"row": index, "reason": "Invalid selected currency price values"})
                    continue
                p_sale = sale_price_val if sale_price_val is not None else (p_rt or p_buy or 0)
                imported_total_amount = Decimal(str(quantity)) * Decimal(str(p_buy))

                purchase_key = (
                    row_shop.id,
                    supplier.id if supplier else None,
                    currency.id if currency else None,
                )
                group = pending_purchases.setdefault(
                    purchase_key,
                    {
                        "shop": row_shop,
                        "supplier": supplier,
                        "currency": currency,
                        "items": [],
                        "received_amount": Decimal("0"),
                    },
                )
                group["items"].append(
                    {
                        "product": product,
                        "unit": unit,
                        "quantity": quantity,
                        "buy_price": Decimal(str(p_buy)),
                        "average_buy_price": Decimal(str(p_buy)),
                        "wholesale_price": Decimal(str(p_wh)) if p_wh is not None else None,
                        "average_wholesale_price": Decimal(str(p_wh)) if p_wh is not None else None,
                        "retail_price": Decimal(str(p_rt)) if p_rt is not None else None,
                        "average_retail_price": Decimal(str(p_rt)) if p_rt is not None else None,
                        "sale_price": Decimal(str(p_sale)),
                        "comments": json.dumps({
                            "price_by_currency": {
                                "buy_price_afg": None if buy_afg is None else str(buy_afg),
                                "buy_price_usd": None if buy_usd is None else str(buy_usd),
                                "wholesale_price_afg": None if wh_afg is None else str(wh_afg),
                                "wholesale_price_usd": None if wh_usd is None else str(wh_usd),
                                "retail_price_afg": None if rt_afg is None else str(rt_afg),
                                "retail_price_usd": None if rt_usd is None else str(rt_usd),
                            }
                        }),
                        "currency": currency,
                    }
                )
                group["received_amount"] += imported_total_amount
                created += 1

            for group in pending_purchases.values():
                purchase = Purchase.objects.create(
                    shop=group["shop"],
                    supplier=group["supplier"],
                    currency=group["currency"],
                    received_amount=group["received_amount"],
                    created_by=request.user,
                )
                for item_data in group["items"]:
                    purchase_item = PurchaseItem.objects.create(
                        purchase=purchase,
                        currency=item_data["currency"],
                        product=item_data["product"],
                        unit=item_data["unit"],
                        quantity=item_data["quantity"],
                        buy_price=item_data["buy_price"],
                        wholesale_price=item_data["wholesale_price"],
                        retail_price=item_data["retail_price"],
                        sale_price=item_data["sale_price"],
                        created_by=request.user,
                    )
                    Stock.objects.create(
                        shop=group["shop"],
                        purchase_item=purchase_item,
                        quantity=item_data["quantity"],
                        sale_price=item_data["sale_price"],
                        average_buy_price=item_data["average_buy_price"],
                        average_wholesale_price=item_data["average_wholesale_price"],
                        average_retail_price=item_data["average_retail_price"],
                        wholesale_price=item_data["wholesale_price"],
                        retail_price=item_data["retail_price"],
                        comments=item_data["comments"],
                        created_by=request.user,
                    )
                PurchasePayment.objects.create(
                    purchase=purchase,
                    amount=group["received_amount"],
                    currency=group["currency"],
                    created_by=request.user,
                )

        return Response({"created": created, "skipped": skipped}, status=status.HTTP_201_CREATED)

    @action(detail=False, methods=["post"], url_path="sync_codes")
    def sync_codes(self, request):
        """
        Backend-only helper:
        Accept rows from Excel and update Product.code by matching product name.
        Expected keys per row: CODE/code + PRODUCT/product/product_name.
        """
        rows = request.data.get("rows", [])
        if not isinstance(rows, list) or not rows:
            return Response({"detail": "No rows provided."}, status=status.HTTP_400_BAD_REQUEST)

        user_shops = Shop.objects.filter(get_user_shop_filter(request.user))
        if not user_shops.exists():
            return Response({"detail": "No accessible shop found for user."}, status=status.HTTP_400_BAD_REQUEST)
        selected_shop_id = (
            request.data.get("shop")
            or request.headers.get("X-Selected-Shop")
            or request.META.get("HTTP_X_SELECTED_SHOP")
        )
        default_shop = (
            user_shops.filter(pk=selected_shop_id).first()
            if selected_shop_id
            else user_shops.first()
        ) or user_shops.first()

        updated = 0
        skipped = []

        def normalize_name(value):
            text = str(value or "").strip().lower()
            return re.sub(r"\s+", " ", text)

        shop_products = Product.objects.filter(shop=default_shop)
        product_by_normalized_name = {
            normalize_name(p.name): p for p in shop_products
        }

        for index, row in enumerate(rows, start=1):
            normalized_row = {
                (str(k).strip().lower().replace(" ", "_") if k is not None else ""): v
                for k, v in row.items()
            }
            product_name = (
                normalized_row.get("product")
                or normalized_row.get("product_name")
                or ""
            )
            product_code = (
                normalized_row.get("code")
                or normalized_row.get("product_code")
                or ""
            )

            product_name = str(product_name).strip()
            product_code = str(product_code).strip()

            if not product_name or not product_code:
                skipped.append({"row": index, "reason": "Missing product or code"})
                continue

            product = product_by_normalized_name.get(normalize_name(product_name))
            if not product:
                product = Product.objects.filter(shop=default_shop, name__iexact=product_name).first()
            if not product:
                skipped.append({"row": index, "reason": f"Product not found: {product_name}"})
                continue

            if product.code != product_code:
                product.code = product_code
                product.save(update_fields=["code"])
                updated += 1

        return Response({"updated": updated, "skipped": skipped}, status=status.HTTP_200_OK)


class StockTransferViewSet(DataRootViewSet):
    serializer_class = StockTransferSerializer
    permission_classes = [IsAuthenticated, HasTabPermission]
    required_tab = "stock_transfer"
    required_tabs = ["stock", "stock_transfer"]
    filter_backends = [DjangoFilterBackend, SearchFilter]
    filterset_fields = [
        "status",
        "product",
        "supplier_branch",
        "customer_branch",
        "transfer_date",
    ]
    search_fields = [
        "transfer_id",
        "product__name",
        "supplier_branch__name",
        "customer_branch__name",
        "created_by__username",
        "notes",
    ]
    pagination_class = CustomPagination

    def _filter_stock_by_currency(self, queryset, currency_id):
        if not currency_id:
            return queryset
        return queryset.filter(
            models.Q(purchase_item__currency_id=currency_id)
            | models.Q(
                purchase_item__currency__isnull=True,
                purchase_item__purchase__currency_id=currency_id,
            )
        )

    def _normalize_currency_code(self, value):
        return normalize_currency_code(value)

    def _resolve_shop_currency(self, shop_id, currency_or_id, user=None, create=False):
        return resolve_shop_currency(shop_id, currency_or_id, user=user, create=create)

    def _resolve_destination_unit(self, shop_id, source_unit, user=None):
        if not shop_id or not source_unit:
            return source_unit
        if source_unit.shop_id == int(shop_id):
            return source_unit
        existing = Unit.objects.filter(
            shop_id=shop_id,
            title__iexact=source_unit.title,
        ).order_by("id").first()
        if existing:
            return existing
        return Unit.objects.create(
            shop_id=shop_id,
            title=source_unit.title,
            description=source_unit.description,
            created_by=user,
        )

    def _find_source_stock(self, product_id, branch_id, currency_id=None):
        queryset = Stock.objects.select_for_update().filter(
            shop_id=branch_id,
            is_deleted=False,
            purchase_item__is_deleted=False,
            purchase_item__purchase__is_deleted=False,
        )
        if currency_id:
            resolved = self._resolve_shop_currency(branch_id, currency_id)
            currency_ids = self._matching_currency_ids(
                branch_id,
                resolved or currency_id,
            )
            if currency_ids:
                queryset = self._filter_stock_by_currency_ids(queryset, currency_ids)

        exact_stock = queryset.filter(
            purchase_item__product_id=product_id,
            quantity__gt=0,
        ).order_by("-quantity", "-id").first()
        if exact_stock:
            return exact_stock

        # A stale/cross-branch product ID can be submitted after switching the
        # source branch. Resolve the equivalent source product by its name.
        selected_product = Product.objects.filter(pk=product_id).first()
        product_name = str(getattr(selected_product, "name", "") or "").strip()
        if not product_name:
            return None
        return queryset.filter(
            purchase_item__product__name__iexact=product_name,
            quantity__gt=0,
        ).order_by("-quantity", "-id").first()

    def _purchase_item_is_listable(self, purchase_item):
        """Stock list only shows rows with a live purchase_item + purchase + product."""
        if not purchase_item:
            return False
        if getattr(purchase_item, "is_deleted", False):
            return False
        if not purchase_item.product_id:
            return False
        purchase = getattr(purchase_item, "purchase", None)
        if purchase_item.purchase_id and (purchase is None or getattr(purchase, "is_deleted", False)):
            return False
        return True

    def _find_destination_product(self, transfer):
        """Match destination Product by name first.

        Never attach a transfer to a different-named product that only shares a
        code (codes are often reused / blank / "0"). Stock search uses product
        name, so a code-only match makes Accepted transfers "disappear" from Stock.
        """
        product = transfer.product
        if not product:
            return None

        product_code = str(product.code or "").strip()
        product_name = str(product.name or "").strip()
        destination_qs = Product.objects.filter(shop_id=transfer.customer_branch_id)

        if product_code and product_name:
            destination_product = destination_qs.filter(
                code__iexact=product_code,
                name__iexact=product_name,
            ).order_by("id").first()
            if destination_product:
                return destination_product

        # Prefer name: this is what the Stock page searches on.
        if product_name:
            destination_product = destination_qs.filter(
                name__iexact=product_name,
            ).order_by("id").first()
            if destination_product:
                return destination_product

        # Code alone only when source has no usable name AND the code is unique
        # at the destination branch (avoid grabbing an unrelated shared barcode).
        if product_code and not product_name:
            matches = list(
                destination_qs.filter(code__iexact=product_code).order_by("id")[:2]
            )
            if len(matches) == 1:
                return matches[0]

        return None

    def _resolve_destination_product(self, transfer, user=None):
        existing = self._find_destination_product(transfer)
        if existing:
            return existing

        product = transfer.product
        if not product:
            return None

        # Create a local product copy so destination stock never points at
        # another branch's Product / Category rows.
        destination_category = None
        if product.category_id:
            source_category = product.category
            destination_category = Category.objects.filter(
                shop_id=transfer.customer_branch_id,
                name__iexact=source_category.name,
            ).order_by("id").first()
            if not destination_category:
                destination_category = Category.objects.create(
                    shop_id=transfer.customer_branch_id,
                    name=source_category.name,
                    description=source_category.description,
                    created_by=user or transfer.created_by,
                )

        return Product.objects.create(
            shop_id=transfer.customer_branch_id,
            category=destination_category,
            name=product.name,
            code=product.code,
            description=product.description,
            created_by=user or transfer.created_by,
        )

    def _same_product_stock_filter(self, product):
        """Match destination stock rows that represent the same sellable product.

        Product *name* is the primary key for merging. Branches often end up with
        duplicate Product rows for the same item (different codes), and requiring
        code+name left two Stock rows on the Stock page after a transfer.
        """
        product_code = str(product.code or "").strip()
        product_name = str(product.name or "").strip()

        if product_name:
            return models.Q(purchase_item__product__name__iexact=product_name)
        if product_code:
            return models.Q(purchase_item__product__code__iexact=product_code)
        return models.Q(pk__isnull=True)

    @staticmethod
    def _is_transfer_supplier(supplier_or_purchase):
        """True when the linked supplier is a synthetic 'Transfer from …' party."""
        supplier = supplier_or_purchase
        if supplier is None:
            return False
        # Allow passing a Purchase (or anything with .supplier).
        if hasattr(supplier, "supplier") and not hasattr(supplier, "name"):
            supplier = getattr(supplier, "supplier", None)
        if supplier is None:
            return False
        name = str(getattr(supplier, "name", "") or "").strip().lower()
        return name.startswith("transfer from ")

    def _matching_currency_ids(self, shop_id, currency_or_id):
        """All Currency PKs (any shop) whose title normalizes to the same AFG/USD code."""
        if not currency_or_id:
            return []

        source = currency_or_id
        if not isinstance(source, Currency):
            try:
                source = Currency.objects.filter(pk=int(currency_or_id)).first()
            except (TypeError, ValueError):
                source = None
        if not source:
            return []

        code = self._normalize_currency_code(source.title)
        if not code:
            return [source.id]

        ids = []
        for candidate in Currency.objects.all().only("id", "title", "shop_id"):
            if self._normalize_currency_code(candidate.title) == code:
                ids.append(candidate.id)
        if source.id not in ids:
            ids.append(source.id)

        # Prefer destination-shop currency first when callers resolve later.
        if shop_id:
            local = self._resolve_shop_currency(shop_id, source, create=False)
            if local and local.id in ids:
                ids = [local.id] + [i for i in ids if i != local.id]
        return ids

    def _filter_stock_by_currency_ids(self, queryset, currency_ids):
        if not currency_ids:
            return queryset
        return queryset.filter(
            models.Q(purchase_item__currency_id__in=currency_ids)
            | models.Q(
                purchase_item__currency__isnull=True,
                purchase_item__purchase__currency_id__in=currency_ids,
            )
        )

    def _find_target_stock_for_transfer(self, transfer, customer_branch_id, currency_id=None):
        product = transfer.product
        if not product:
            return None

        lookup_currency = currency_id if currency_id is not None else transfer.currency_id
        currency_ids = self._matching_currency_ids(customer_branch_id, lookup_currency)

        base_qs = Stock.objects.select_for_update().filter(
            shop_id=customer_branch_id,
            is_deleted=False,
        )
        if currency_ids:
            base_qs = self._filter_stock_by_currency_ids(base_qs, currency_ids)

        destination_product = self._find_destination_product(transfer)
        candidates = base_qs.none()
        if destination_product:
            # Include every same-name Product at the destination (duplicate SKUs).
            product_name = str(destination_product.name or "").strip()
            if product_name:
                candidates = base_qs.filter(
                    purchase_item__product__name__iexact=product_name
                )
            else:
                candidates = base_qs.filter(
                    purchase_item__product_id=destination_product.id
                )
        if not candidates.exists():
            candidates = base_qs.filter(self._same_product_stock_filter(product))

        # Prefer rows the Stock list can actually show (valid purchase_item chain).
        listable = candidates.filter(
            purchase_item__isnull=False,
            purchase_item__is_deleted=False,
            purchase_item__purchase__is_deleted=False,
            purchase_item__product__isnull=False,
        )

        # Prefer an existing positive-qty row so transfers combine into one record.
        return (
            listable.filter(quantity__gt=0).order_by("-quantity", "id").first()
            or listable.order_by("-id").first()
            or candidates.filter(quantity__gt=0).order_by("-quantity", "id").first()
            or candidates.order_by("-id").first()
        )

    def _ensure_target_stock_listable(
        self,
        target_stock,
        transfer,
        destination_product,
        transfer_currency,
        transfer_unit,
        transfer_buy_price,
        source_stock,
        user,
    ):
        """Guarantee destination stock has a purchase_item the Stock list will show.

        Accepted transfers previously could leave qty on a row with null/deleted
        purchase_item, which Stock filters out (quantity may still be > 0).
        """
        if not target_stock:
            return target_stock

        pi = target_stock.purchase_item
        if self._purchase_item_is_listable(pi):
            if destination_product and pi.product_id != destination_product.id:
                pi.product = destination_product
                pi.save(update_fields=["product", "updated_at"])
            if transfer_currency and pi.currency_id != transfer_currency.id:
                pi.currency = transfer_currency
                pi.save(update_fields=["currency", "updated_at"])
                if pi.purchase_id and pi.purchase.currency_id != transfer_currency.id:
                    pi.purchase.currency = transfer_currency
                    pi.purchase.save(update_fields=["currency", "updated_at"])
            return target_stock

        return self._attach_stock_only_purchase_item(
            target_stock,
            transfer,
            destination_product,
            transfer_currency,
            transfer_unit,
            transfer_buy_price,
            source_stock,
            user,
            quantity=int(transfer.transfer_quantity or 0),
        )

    def _attach_stock_only_purchase_item(
        self,
        target_stock,
        transfer,
        destination_product,
        transfer_currency,
        transfer_unit,
        transfer_buy_price,
        source_stock,
        user,
        quantity=None,
    ):
        """Point destination stock at a Purchase with no supplier (pure inventory move)."""
        if not target_stock:
            return target_stock

        qty = int(
            quantity
            if quantity is not None
            else (transfer.transfer_quantity or 0)
        )
        inventory_purchase = Purchase.objects.create(
            shop_id=transfer.customer_branch_id,
            supplier=None,
            currency=transfer_currency,
            amount=0,
            received_amount=0,
            purchase_date=transfer.transfer_date or timezone.localdate(),
            notes=f"Stock-only transfer {transfer.transfer_id or transfer.pk}",
            created_by=user,
        )
        purchase_item = PurchaseItem.objects.create(
            purchase=inventory_purchase,
            currency=transfer_currency,
            product=destination_product or transfer.product,
            unit=transfer_unit,
            quantity=qty,
            buy_price=transfer_buy_price,
            wholesale_price=self._transfer_wholesale_price(transfer, source_stock),
            retail_price=self._transfer_retail_price(transfer, source_stock),
            sale_price=(
                transfer.sale_price
                if transfer.sale_price is not None
                else (source_stock.sale_price if source_stock else 0)
            ),
            created_by=user,
        )
        target_stock.purchase_item = purchase_item
        target_stock.save(update_fields=["purchase_item"])
        return target_stock

    def _ensure_stock_only_no_transfer_supplier(
        self,
        target_stock,
        transfer,
        destination_product,
        transfer_currency,
        transfer_unit,
        transfer_buy_price,
        source_stock,
        user,
    ):
        """Payment OFF: keep stock listable and never show 'Transfer from …' supplier."""
        if not target_stock:
            return target_stock

        existing_pi = target_stock.purchase_item
        needs_inventory_purchase = (
            not self._purchase_item_is_listable(existing_pi)
            or (
                destination_product
                and existing_pi
                and existing_pi.product_id != destination_product.id
            )
            or (
                existing_pi
                and existing_pi.purchase_id
                and self._is_transfer_supplier(existing_pi.purchase)
            )
        )
        if needs_inventory_purchase:
            return self._attach_stock_only_purchase_item(
                target_stock,
                transfer,
                destination_product,
                transfer_currency,
                transfer_unit,
                transfer_buy_price,
                source_stock,
                user,
                quantity=int(transfer.transfer_quantity or 0),
            )

        if existing_pi and transfer_currency:
            update_fields = []
            if destination_product and existing_pi.product_id != destination_product.id:
                existing_pi.product = destination_product
                update_fields.append("product")
            if existing_pi.currency_id != transfer_currency.id:
                existing_pi.currency = transfer_currency
                update_fields.append("currency")
            if update_fields:
                update_fields.append("updated_at")
                existing_pi.save(update_fields=update_fields)
                if (
                    existing_pi.purchase_id
                    and existing_pi.purchase.currency_id != transfer_currency.id
                ):
                    existing_pi.purchase.currency = transfer_currency
                    existing_pi.purchase.save(update_fields=["currency", "updated_at"])
        return target_stock

    def _consolidate_duplicate_destination_stocks(
        self,
        primary_stock,
        transfer,
        destination_product=None,
        destination_currency=None,
        destination_unit=None,
    ):
        """Merge other same-product/same-currency rows into primary_stock."""
        if not primary_stock:
            return primary_stock

        currency_ids = self._matching_currency_ids(
            primary_stock.shop_id,
            destination_currency or transfer.currency_id or primary_stock.currency,
        )
        product = destination_product or transfer.product
        if not product:
            return primary_stock

        siblings = Stock.objects.select_for_update().filter(
            shop_id=primary_stock.shop_id,
            is_deleted=False,
        ).exclude(pk=primary_stock.pk)
        if currency_ids:
            siblings = self._filter_stock_by_currency_ids(siblings, currency_ids)
        siblings = siblings.filter(self._same_product_stock_filter(product))

        donor_unit = destination_unit
        if not donor_unit and primary_stock.purchase_item_id and primary_stock.purchase_item.unit_id:
            donor_unit = primary_stock.purchase_item.unit

        for sibling in siblings:
            sibling_pi = sibling.purchase_item
            if not donor_unit and sibling_pi and sibling_pi.unit_id:
                donor_unit = sibling_pi.unit

            old_qty = Decimal(str(primary_stock.quantity or 0))
            add_qty = Decimal(str(sibling.quantity or 0))
            if add_qty > 0:
                total_qty = old_qty + add_qty
                primary_stock.average_buy_price = (
                    (old_qty * Decimal(str(primary_stock.average_buy_price or 0)))
                    + (add_qty * Decimal(str(sibling.average_buy_price or 0)))
                ) / total_qty
                if sibling.average_wholesale_price is not None or primary_stock.average_wholesale_price is not None:
                    primary_stock.average_wholesale_price = (
                        (old_qty * Decimal(str(primary_stock.average_wholesale_price or 0)))
                        + (add_qty * Decimal(str(sibling.average_wholesale_price or 0)))
                    ) / total_qty
                if sibling.average_retail_price is not None or primary_stock.average_retail_price is not None:
                    primary_stock.average_retail_price = (
                        (old_qty * Decimal(str(primary_stock.average_retail_price or 0)))
                        + (add_qty * Decimal(str(sibling.average_retail_price or 0)))
                    ) / total_qty
                primary_stock.quantity = int(total_qty)

            StockTransfer.objects.filter(target_stock=sibling).update(target_stock=primary_stock)
            StockTransfer.objects.filter(source_stock=sibling).update(source_stock=primary_stock)
            sibling.is_deleted = True
            sibling.quantity = 0
            sibling.save(update_fields=["is_deleted", "quantity", "updated_at"])

        primary_stock.save(
            update_fields=[
                "quantity",
                "average_buy_price",
                "average_wholesale_price",
                "average_retail_price",
                "updated_at",
            ]
        )

        # Keep the surviving row on the destination product/currency/unit.
        purchase_item = primary_stock.purchase_item
        if purchase_item:
            update_fields = []
            if destination_product and purchase_item.product_id != destination_product.id:
                purchase_item.product = destination_product
                update_fields.append("product")
            if destination_currency and purchase_item.currency_id != destination_currency.id:
                purchase_item.currency = destination_currency
                update_fields.append("currency")
            if donor_unit and purchase_item.unit_id != donor_unit.id:
                purchase_item.unit = donor_unit
                update_fields.append("unit")
            if update_fields:
                update_fields.append("updated_at")
                purchase_item.save(update_fields=update_fields)
                if (
                    destination_currency
                    and purchase_item.purchase_id
                    and purchase_item.purchase.currency_id != destination_currency.id
                ):
                    purchase_item.purchase.currency = destination_currency
                    purchase_item.purchase.save(update_fields=["currency", "updated_at"])

        return primary_stock

    def get_queryset(self):
        if not self.request.user or not self.request.user.is_authenticated:
            return StockTransfer.objects.none()

        queryset = (
            StockTransfer.objects.select_related(
                "product",
                "supplier_branch",
                "customer_branch",
                "currency",
                "created_by",
            )
            .order_by("-transfer_datetime", "-id")
        )
        if user_can_see_all_shop_data(self.request.user, "stock_transfer") or user_can_see_all_shop_data(
            self.request.user, "stock"
        ):
            return queryset

        accessible_shop_ids = list(
            Shop.objects.filter(get_user_shop_filter(self.request.user)).values_list("id", flat=True)
        )
        if not accessible_shop_ids:
            return queryset.none()

        return queryset.filter(
            models.Q(supplier_branch_id__in=accessible_shop_ids)
            | models.Q(customer_branch_id__in=accessible_shop_ids)
        ).distinct()

    def filter_queryset(self, queryset):
        queryset = super().filter_queryset(queryset)
        start = self.request.query_params.get("start_date")
        end = self.request.query_params.get("end_date")
        if start:
            queryset = queryset.filter(transfer_date__gte=start)
        if end:
            queryset = queryset.filter(transfer_date__lte=end)
        return queryset

    def _next_transfer_id(self):
        last = StockTransfer.objects.order_by("-id").first()
        next_id = (last.id + 1) if last else 1
        return f"TRF-{next_id:06d}"

    def _get_accessible_shop_ids(self, user):
        if user_can_see_all_shop_data(user, "stock_transfer") or user_can_see_all_shop_data(user, "stock"):
            return None
        return list(
            Shop.objects.filter(get_user_shop_filter(user)).values_list("id", flat=True)
        )

    def _user_has_shop_access(self, user, shop_id):
        accessible_shop_ids = self._get_accessible_shop_ids(user)
        if accessible_shop_ids is None:
            return True
        try:
            shop_id = int(shop_id)
        except (TypeError, ValueError):
            return False
        return shop_id in accessible_shop_ids

    def _user_can_manage_transfer(self, user, transfer):
        if user_can_see_all_shop_data(user, "stock_transfer") or user_can_see_all_shop_data(user, "stock"):
            return True
        accessible_shop_ids = self._get_accessible_shop_ids(user) or []
        return (
            transfer.supplier_branch_id in accessible_shop_ids
            or transfer.customer_branch_id in accessible_shop_ids
        )

    def _transfer_purchase_line_amount(self, transfer):
        return Decimal(str(transfer.transfer_quantity or 0)) * Decimal(str(transfer.sale_price or 0))

    def _apply_source_stock_transfer_valuation(self, source_stock, transfer):
        """Reduce source stock value by transfer Total Amount (qty × sale_price).

        Quantity is already deducted. Remaining rows were still valued at the old
        average cost, so source Total Stock Value dropped by cost instead of the
        transfer amount. Reprice remaining qty so the value decrease matches the
        transfer Total Amount. Idempotent via source_valuation_applied.
        """
        if not source_stock:
            return source_stock
        if getattr(transfer, "source_valuation_applied", False):
            return source_stock

        transferred_qty = Decimal(str(transfer.transfer_quantity or 0))
        if transferred_qty <= 0:
            return source_stock

        remaining_qty = Decimal(str(source_stock.quantity or 0))
        current_avg = Decimal(str(source_stock.average_buy_price or 0))
        transfer_amount = self._transfer_purchase_line_amount(transfer)

        # Reconstruct pre-transfer value using current avg (unchanged until now).
        pre_qty = remaining_qty + transferred_qty
        pre_value = pre_qty * current_avg
        remaining_value = pre_value - transfer_amount
        if remaining_value < 0:
            remaining_value = Decimal("0")

        actual_removed = (pre_value - remaining_value).quantize(Decimal("0.01"))

        if remaining_qty > 0:
            source_stock.average_buy_price = (remaining_value / remaining_qty).quantize(
                Decimal("0.01")
            )
        else:
            source_stock.average_buy_price = Decimal("0.00")

        source_stock.save(update_fields=["average_buy_price", "updated_at"])
        transfer.source_valuation_applied = True
        transfer.source_value_adjustment = actual_removed
        transfer.save(
            update_fields=["source_valuation_applied", "source_value_adjustment", "updated_at"]
        )
        return source_stock

    def _restore_source_stock_transfer_valuation(self, source_stock, transfer):
        """Undo source valuation change when reversing an accepted transfer."""
        if not source_stock:
            return source_stock

        transferred_qty = Decimal(str(transfer.transfer_quantity or 0))
        if transferred_qty <= 0:
            return source_stock

        current_qty = Decimal(str(source_stock.quantity or 0))
        current_avg = Decimal(str(source_stock.average_buy_price or 0))
        if getattr(transfer, "source_valuation_applied", False):
            restore_amount = Decimal(str(transfer.source_value_adjustment or 0))
        else:
            # Legacy accepted transfers (before valuation flag): restore qty only.
            restore_amount = transferred_qty * current_avg

        restored_qty = current_qty + transferred_qty
        restored_value = current_qty * current_avg + restore_amount

        source_stock.quantity = int(restored_qty)
        if restored_qty > 0:
            source_stock.average_buy_price = (restored_value / restored_qty).quantize(
                Decimal("0.01")
            )
        else:
            source_stock.average_buy_price = Decimal("0.00")
        source_stock.save(update_fields=["quantity", "average_buy_price", "updated_at"])

        if getattr(transfer, "source_valuation_applied", False):
            transfer.source_valuation_applied = False
            transfer.source_value_adjustment = Decimal("0.00")
            transfer.save(
                update_fields=["source_valuation_applied", "source_value_adjustment", "updated_at"]
            )
        return source_stock

    def _is_source_stock_reserved(self, transfer, source_stock):
        return (
            transfer.status == StockTransfer.Status.PENDING
            and bool(getattr(transfer, "source_quantity_reserved", False))
            and source_stock is not None
            and transfer.source_stock_id == source_stock.id
        )

    def _reserve_source_stock(self, transfer, source_stock=None):
        source_stock = source_stock or transfer.source_stock
        if not source_stock:
            source_stock = self._find_source_stock(
                transfer.product_id,
                transfer.supplier_branch_id,
                transfer.currency_id,
            )
        if not source_stock:
            raise ValueError("Supplier branch stock not found for selected product.")

        quantity = int(transfer.transfer_quantity or 0)
        available_quantity = int(source_stock.quantity or 0)
        if quantity > available_quantity:
            raise ValueError("Send quantity cannot be greater than available quantity.")

        source_stock.quantity = available_quantity - quantity
        source_stock.save(update_fields=["quantity", "updated_at"])
        transfer.source_stock = source_stock
        transfer.old_quantity = available_quantity
        transfer.new_quantity = available_quantity - quantity
        transfer.source_quantity_reserved = True
        transfer.save(
            update_fields=[
                "source_stock",
                "old_quantity",
                "new_quantity",
                "source_quantity_reserved",
                "updated_at",
            ]
        )
        return source_stock

    def _release_source_stock(self, transfer):
        if transfer.status == StockTransfer.Status.ACCEPTED:
            return

        source_stock = transfer.source_stock
        if not source_stock:
            source_stock = self._find_source_stock(
                transfer.product_id,
                transfer.supplier_branch_id,
                transfer.currency_id,
            )
        if not source_stock:
            return

        if not self._is_source_stock_reserved(transfer, source_stock):
            return

        quantity = int(transfer.transfer_quantity or 0)
        if quantity <= 0:
            return

        source_stock.quantity = int(source_stock.quantity or 0) + quantity
        source_stock.save(update_fields=["quantity", "updated_at"])
        transfer.source_quantity_reserved = False
        transfer.save(update_fields=["source_quantity_reserved", "updated_at"])

    def _stock_price_average(self, quantity, old_average, old_quantity, incoming_quantity, incoming_price):
        quantity = Decimal(str(quantity or 0))
        old_average = Decimal(str(old_average or 0))
        old_quantity = Decimal(str(old_quantity or 0))
        incoming_quantity = Decimal(str(incoming_quantity or 0))
        incoming_price = Decimal(str(incoming_price or 0))

        remaining_quantity = quantity - old_quantity
        if remaining_quantity < 0:
            remaining_quantity = Decimal("0")

        remaining_value = (remaining_quantity * old_average)
        updated_total_quantity = remaining_quantity + incoming_quantity
        updated_total_value = remaining_value + (incoming_quantity * incoming_price)

        if updated_total_quantity <= 0:
            return Decimal("0")
        return updated_total_value / updated_total_quantity

    def _transfer_wholesale_price(self, transfer, source_stock=None):
        if getattr(transfer, "wholesale_price", None) is not None:
            return transfer.wholesale_price
        return source_stock.wholesale_price if source_stock else None

    def _transfer_retail_price(self, transfer, source_stock=None):
        if getattr(transfer, "retail_price", None) is not None:
            return transfer.retail_price
        return source_stock.retail_price if source_stock else None

    def _apply_transfer_prices_to_stock(self, stock, transfer, source_stock=None):
        """Update display sale/wholesale/retail prices only.

        Do not overwrite average_* fields here — callers set those with a
        weighted average so Total Stock Value stays correct after merges.
        """
        wholesale_price = self._transfer_wholesale_price(transfer, source_stock)
        retail_price = self._transfer_retail_price(transfer, source_stock)
        if transfer.sale_price is not None:
            stock.sale_price = transfer.sale_price
        if wholesale_price is not None:
            stock.wholesale_price = wholesale_price
        if retail_price is not None:
            stock.retail_price = retail_price

    def _apply_transfer_prices_to_purchase_item(self, purchase_item, transfer, source_stock=None):
        wholesale_price = self._transfer_wholesale_price(transfer, source_stock)
        retail_price = self._transfer_retail_price(transfer, source_stock)
        if transfer.sale_price is not None:
            purchase_item.buy_price = transfer.sale_price
            purchase_item.sale_price = transfer.sale_price
        if wholesale_price is not None:
            purchase_item.wholesale_price = wholesale_price
        if retail_price is not None:
            purchase_item.retail_price = retail_price

    def _sync_accepted_transfer_edit(self, original_state, updated_transfer):
        old_quantity = int(original_state["transfer_quantity"] or 0)
        new_quantity = int(updated_transfer.transfer_quantity or 0)
        quantity_delta = new_quantity - old_quantity

        old_source_stock = original_state["source_stock"]
        new_source_stock = self._find_source_stock(
            updated_transfer.product_id,
            updated_transfer.supplier_branch_id,
            updated_transfer.currency_id,
        )
        if not new_source_stock:
            raise ValueError("Supplier branch stock not found for selected product.")

        if old_source_stock and old_source_stock.pk == new_source_stock.pk:
            next_source_quantity = int(old_source_stock.quantity or 0) - quantity_delta
            if next_source_quantity < 0:
                raise ValueError("Transfer quantity is no longer available in the source branch.")
            old_source_stock.quantity = next_source_quantity
            old_source_stock.save(update_fields=["quantity", "updated_at"])
            new_source_stock.quantity = next_source_quantity
        else:
            if old_source_stock:
                old_source_stock.quantity = int(old_source_stock.quantity or 0) + old_quantity
                old_source_stock.save(update_fields=["quantity", "updated_at"])

            next_source_quantity = int(new_source_stock.quantity or 0) - new_quantity
            if next_source_quantity < 0:
                raise ValueError("Transfer quantity is no longer available in the source branch.")
            new_source_stock.quantity = next_source_quantity
            new_source_stock.save(update_fields=["quantity", "updated_at"])

        target_stock = original_state["target_stock"]
        if target_stock:
            previous_target_quantity = int(target_stock.quantity or 0)
            next_target_quantity = previous_target_quantity + quantity_delta
            if next_target_quantity < 0:
                raise ValueError("Target stock quantity cannot become negative.")

            target_stock.quantity = next_target_quantity
            incoming_cost = (
                updated_transfer.sale_price
                if updated_transfer.sale_price is not None
                else (new_source_stock.average_buy_price or 0)
            )
            target_stock.average_buy_price = self._stock_price_average(
                previous_target_quantity,
                target_stock.average_buy_price,
                old_quantity,
                new_quantity,
                incoming_cost,
            )
            incoming_wholesale = self._transfer_wholesale_price(updated_transfer, new_source_stock)
            if incoming_wholesale is not None:
                target_stock.average_wholesale_price = self._stock_price_average(
                    previous_target_quantity,
                    target_stock.average_wholesale_price,
                    old_quantity,
                    new_quantity,
                    incoming_wholesale,
                )
            incoming_retail = self._transfer_retail_price(updated_transfer, new_source_stock)
            if incoming_retail is not None:
                target_stock.average_retail_price = self._stock_price_average(
                    previous_target_quantity,
                    target_stock.average_retail_price,
                    old_quantity,
                    new_quantity,
                    incoming_retail,
                )
            self._apply_transfer_prices_to_stock(target_stock, updated_transfer, new_source_stock)
            target_stock.comments = ""
            target_stock.save(
                update_fields=[
                    "quantity",
                    "average_buy_price",
                    "average_wholesale_price",
                    "average_retail_price",
                    "wholesale_price",
                    "retail_price",
                    "sale_price",
                    "comments",
                    "updated_at",
                ]
            )

            purchase_item = target_stock.purchase_item
            destination_currency = self._resolve_shop_currency(
                updated_transfer.customer_branch_id,
                updated_transfer.currency
                or (new_source_stock.currency if new_source_stock.currency else None),
                user=updated_transfer.created_by,
                create=True,
            )
            destination_product = self._resolve_destination_product(
                updated_transfer,
                user=updated_transfer.created_by,
            )
            destination_unit = self._resolve_destination_unit(
                updated_transfer.customer_branch_id,
                new_source_stock.unit,
                user=updated_transfer.created_by,
            )
            transfer_buy_price = (
                updated_transfer.sale_price
                if updated_transfer.sale_price is not None
                else (new_source_stock.sale_price or new_source_stock.retail_price or 0)
            )

            if purchase_item:
                purchase_item.currency = destination_currency
                purchase_item.product = destination_product or updated_transfer.product
                purchase_item.quantity = new_quantity
                purchase_item.buy_price = transfer_buy_price
                purchase_item.wholesale_price = self._transfer_wholesale_price(
                    updated_transfer, new_source_stock
                )
                purchase_item.retail_price = self._transfer_retail_price(
                    updated_transfer, new_source_stock
                )
                purchase_item.sale_price = (
                    updated_transfer.sale_price
                    if updated_transfer.sale_price is not None
                    else new_source_stock.sale_price
                )
                purchase_item.unit = destination_unit
                purchase_item.save(
                    update_fields=[
                        "currency",
                        "product",
                        "quantity",
                        "buy_price",
                        "wholesale_price",
                        "retail_price",
                        "sale_price",
                        "unit",
                        "updated_at",
                    ]
                )

                purchase = purchase_item.purchase
                if purchase and updated_transfer.show_payment_button:
                    # amount is discount only — do not treat it as transfer invoice total.
                    if destination_currency and purchase.currency_id != destination_currency.id:
                        purchase.currency = destination_currency
                        purchase.save(update_fields=["currency", "updated_at"])
                    if purchase.amount not in (None, 0, Decimal("0"), Decimal("0.00")):
                        purchase.amount = 0
                        purchase.save(update_fields=["amount", "updated_at"])

            if updated_transfer.show_payment_button:
                self._ensure_transfer_sale(
                    updated_transfer,
                    source_stock=new_source_stock,
                    user=updated_transfer.accepted_by or updated_transfer.created_by,
                    replace_existing=True,
                )
            else:
                self._reverse_transfer_sale(updated_transfer)
                # Payment OFF after accept: strip Transfer supplier + merge duplicate rows.
                related_purchase = self._find_related_purchase(updated_transfer)
                if related_purchase and target_stock and target_stock.purchase_item_id:
                    linked_purchase = getattr(target_stock.purchase_item, "purchase", None)
                    if linked_purchase and linked_purchase.pk == related_purchase.pk:
                        if related_purchase.amount not in (None, 0, Decimal("0"), Decimal("0.00")):
                            related_purchase.amount = 0
                            related_purchase.save(update_fields=["amount", "updated_at"])
                        self._delete_transfer_auto_payment(updated_transfer, related_purchase)

                target_stock = self._ensure_stock_only_no_transfer_supplier(
                    target_stock,
                    updated_transfer,
                    destination_product,
                    destination_currency,
                    destination_unit,
                    transfer_buy_price,
                    new_source_stock,
                    updated_transfer.accepted_by or updated_transfer.created_by,
                )
                target_stock = self._consolidate_duplicate_destination_stocks(
                    target_stock,
                    updated_transfer,
                    destination_product=destination_product,
                    destination_currency=destination_currency,
                )
                target_stock = self._ensure_stock_only_no_transfer_supplier(
                    target_stock,
                    updated_transfer,
                    destination_product,
                    destination_currency,
                    destination_unit,
                    transfer_buy_price,
                    new_source_stock,
                    updated_transfer.accepted_by or updated_transfer.created_by,
                )
                updated_transfer.target_stock = target_stock
        else:
            if updated_transfer.show_payment_button:
                self._ensure_transfer_sale(
                    updated_transfer,
                    source_stock=new_source_stock,
                    user=updated_transfer.accepted_by or updated_transfer.created_by,
                    replace_existing=True,
                )
            else:
                self._reverse_transfer_sale(updated_transfer)

        updated_transfer.source_stock = new_source_stock
        updated_transfer.old_quantity = int(new_source_stock.quantity or 0) + new_quantity
        updated_transfer.new_quantity = int(new_source_stock.quantity or 0)
        save_fields = ["source_stock", "old_quantity", "new_quantity", "updated_at"]
        original_target = original_state.get("target_stock")
        if updated_transfer.target_stock_id != (original_target.id if original_target else None):
            save_fields.append("target_stock")
        updated_transfer.save(update_fields=save_fields)

    def _sync_pending_transfer_edit(self, updated_transfer, original_quantity=None):
        source_stock = self._find_source_stock(
            updated_transfer.product_id,
            updated_transfer.supplier_branch_id,
            updated_transfer.currency_id,
        )
        if not source_stock:
            raise ValueError("Supplier branch stock not found for selected product.")

        old_qty = int(
            original_quantity
            if original_quantity is not None
            else updated_transfer.transfer_quantity or 0
        )
        new_qty = int(updated_transfer.transfer_quantity or 0)
        quantity_delta = new_qty - old_qty
        current_available = int(source_stock.quantity or 0)

        if quantity_delta > 0 and quantity_delta > current_available:
            raise ValueError("Transfer quantity is no longer available in the source branch.")

        if quantity_delta != 0:
            source_stock.quantity = current_available - quantity_delta
            source_stock.save(update_fields=["quantity", "updated_at"])

        updated_transfer.source_stock = source_stock
        updated_transfer.old_quantity = int(source_stock.quantity or 0) + new_qty
        updated_transfer.new_quantity = int(source_stock.quantity or 0)
        if not updated_transfer.currency_id and source_stock.currency_id:
            updated_transfer.currency_id = source_stock.currency_id
        updated_transfer.save(
            update_fields=[
                "source_stock",
                "old_quantity",
                "new_quantity",
                "currency",
                "updated_at",
            ]
        )

    def partial_update(self, request, *args, **kwargs):
        transfer = self.get_object()

        try:
            with transaction.atomic():
                original_transfer = StockTransfer.objects.select_for_update().select_related(
                    "source_stock",
                    "target_stock",
                    "target_stock__purchase_item",
                    "target_stock__purchase_item__purchase",
                ).get(pk=transfer.pk)
                original_state = {
                    "status": original_transfer.status,
                    "transfer_quantity": original_transfer.transfer_quantity,
                    "discount_amount": original_transfer.discount_amount,
                    "sale_price": original_transfer.sale_price,
                    "source_stock": original_transfer.source_stock,
                    "target_stock": original_transfer.target_stock,
                }

                serializer = self.get_serializer(original_transfer, data=request.data, partial=True)
                serializer.is_valid(raise_exception=True)
                updated_transfer = serializer.save()

                if original_state["status"] == StockTransfer.Status.ACCEPTED:
                    self._sync_accepted_transfer_edit(original_state, updated_transfer)
                else:
                    self._sync_pending_transfer_edit(
                        updated_transfer,
                        original_state["transfer_quantity"],
                    )
                response = self.get_serializer(updated_transfer).data
        except ValueError as exc:
            return Response({"detail": str(exc)}, status=status.HTTP_400_BAD_REQUEST)

        return Response(response, status=status.HTTP_200_OK)

    def create(self, request, *args, **kwargs):
        product_id = request.data.get("product")
        supplier_branch_id = request.data.get("supplier_branch")
        customer_branch_id = request.data.get("customer_branch")
        currency_id = request.data.get("currency")
        quantity = request.data.get("transfer_quantity")
        buy_price = request.data.get("buy_price")
        sale_price = request.data.get("sale_price")
        wholesale_price = request.data.get("wholesale_price")
        retail_price = request.data.get("retail_price")
        discount_amount = request.data.get("discount_amount", 0)
        transfer_date = request.data.get("transfer_date")
        show_payment_button = request.data.get("show_payment_button", False)
        transfer_group_id = request.data.get("transfer_group_id") or ""
        notes = request.data.get("notes", "")
        if not all([product_id, supplier_branch_id, customer_branch_id, quantity]):
            return Response({"detail": "Missing required fields."}, status=status.HTTP_400_BAD_REQUEST)
        if str(supplier_branch_id) == str(customer_branch_id):
            return Response({"detail": "Supplier and customer branch must be different."}, status=status.HTTP_400_BAD_REQUEST)
        try:
            quantity = int(quantity)
            discount_amount = Decimal(str(discount_amount or 0))
            buy_price = Decimal(str(buy_price)) if buy_price not in (None, "") else None
            sale_price = Decimal(str(sale_price)) if sale_price not in (None, "") else None
            wholesale_price = Decimal(str(wholesale_price)) if wholesale_price not in (None, "") else None
            retail_price = Decimal(str(retail_price)) if retail_price not in (None, "") else None
            currency_id = int(currency_id) if currency_id not in (None, "") else None
        except (TypeError, ValueError):
            return Response({"detail": "Invalid transfer quantity or discount."}, status=status.HTTP_400_BAD_REQUEST)
        if quantity <= 0:
            return Response({"detail": "Transfer quantity must be greater than 0."}, status=status.HTTP_400_BAD_REQUEST)
        if not self._user_has_shop_access(request.user, supplier_branch_id):
            return Response({"detail": "You do not have access to the supplier branch."}, status=status.HTTP_403_FORBIDDEN)
        if not Shop.objects.filter(pk=customer_branch_id).exists():
            return Response({"detail": "Customer branch does not exist."}, status=status.HTTP_400_BAD_REQUEST)

        with transaction.atomic():
            resolved_currency = None
            if currency_id is not None:
                resolved_currency = self._resolve_shop_currency(supplier_branch_id, currency_id)
                if not resolved_currency:
                    return Response(
                        {"detail": "Currency does not belong to the supplier branch."},
                        status=status.HTTP_400_BAD_REQUEST,
                    )
                currency_id = resolved_currency.id

            source_stock = self._find_source_stock(
                product_id,
                supplier_branch_id,
                currency_id,
            )
            if not source_stock:
                currency = resolved_currency or (
                    Currency.objects.filter(pk=currency_id).first() if currency_id else None
                )
                product = Product.objects.filter(pk=product_id).first()
                branch = Shop.objects.filter(pk=supplier_branch_id).first()
                detail = "Supplier branch stock not found for selected product"
                if currency:
                    detail += f" in {currency.title}"
                if branch:
                    detail += f" from {branch.name}"
                if product:
                    detail += f" ({product.name})"
                return Response({"detail": f"{detail}."}, status=status.HTTP_400_BAD_REQUEST)
            available_quantity = int(source_stock.quantity or 0)
            if quantity > available_quantity:
                return Response({"detail": "Send quantity cannot be greater than available quantity."}, status=status.HTTP_400_BAD_REQUEST)

            product = source_stock.purchase_item.product
            transfer_price = (
                buy_price
                if buy_price is not None
                else (
                    sale_price
                    if sale_price is not None
                    else Decimal(str(
                        source_stock.purchase_item.buy_price
                        if source_stock.purchase_item and source_stock.purchase_item.buy_price is not None
                        else (source_stock.average_buy_price or source_stock.sale_price or source_stock.retail_price or 0)
                    ))
                )
            )
            source_currency = (
                resolved_currency
                or (source_stock.currency if source_stock.currency else None)
            )
            transfer = StockTransfer.objects.create(
                transfer_id=self._next_transfer_id(),
                transfer_group_id=transfer_group_id,
                status=StockTransfer.Status.PENDING,
                product=product,
                source_stock=source_stock,
                target_stock=None,
                supplier_branch_id=supplier_branch_id,
                customer_branch_id=customer_branch_id,
                currency=source_currency,
                transfer_quantity=quantity,
                sale_price=transfer_price,
                wholesale_price=wholesale_price if wholesale_price is not None else source_stock.wholesale_price,
                retail_price=retail_price if retail_price is not None else source_stock.retail_price,
                discount_amount=discount_amount,
                old_quantity=available_quantity,
                new_quantity=available_quantity - quantity,
                transfer_date=transfer_date or None,
                show_payment_button=str(show_payment_button).lower() in ("1", "true", "yes", "on"),
                notes=notes,
                created_by=request.user,
            )
            self._reserve_source_stock(transfer, source_stock)

        serializer = self.get_serializer(transfer)
        return Response(serializer.data, status=status.HTTP_201_CREATED)

    @action(detail=True, methods=["post"], url_path="accept")
    def accept(self, request, pk=None):
        transfer = self.get_object()
        if transfer.status == StockTransfer.Status.ACCEPTED:
            return Response({"detail": "Transfer already accepted."}, status=status.HTTP_400_BAD_REQUEST)
        if transfer.status == StockTransfer.Status.REJECTED:
            return Response({"detail": "Transfer was rejected."}, status=status.HTTP_400_BAD_REQUEST)
        if not self._user_can_manage_transfer(request.user, transfer):
            return Response({"detail": "You do not have access to accept this transfer."}, status=status.HTTP_403_FORBIDDEN)
        with transaction.atomic():
            transfer = StockTransfer.objects.select_for_update().get(pk=transfer.pk)
            if transfer.status == StockTransfer.Status.ACCEPTED:
                return Response({"detail": "Transfer already accepted."}, status=status.HTTP_400_BAD_REQUEST)

            # Honor Payment checkbox from accept payload when provided.
            if "show_payment_button" in request.data:
                transfer.show_payment_button = str(request.data.get("show_payment_button")).lower() in (
                    "1",
                    "true",
                    "yes",
                    "on",
                )
                transfer.save(update_fields=["show_payment_button", "updated_at"])

            source_stock = Stock.objects.select_for_update().filter(
                pk=transfer.source_stock_id,
                is_deleted=False,
            ).first()
            if not source_stock:
                return Response({"detail": "Source stock not found for this transfer."}, status=status.HTTP_400_BAD_REQUEST)

            if int(source_stock.quantity or 0) < 0:
                return Response({"detail": "Source stock quantity is invalid for this transfer."}, status=status.HTTP_400_BAD_REQUEST)

            reserved_on_create = self._is_source_stock_reserved(transfer, source_stock)
            if not reserved_on_create:
                if int(source_stock.quantity or 0) < int(transfer.transfer_quantity or 0):
                    return Response({"detail": "Transfer quantity is no longer available in the source branch."}, status=status.HTTP_400_BAD_REQUEST)
                source_stock.quantity = int(source_stock.quantity or 0) - int(transfer.transfer_quantity or 0)
                source_stock.save(update_fields=["quantity", "updated_at"])

            # Align Kabul (source) Total Stock Value with transfer Total Amount.
            self._apply_source_stock_transfer_valuation(source_stock, transfer)

            line_amount = self._transfer_purchase_line_amount(transfer)
            destination_product = self._resolve_destination_product(transfer, user=request.user)
            transfer_currency = self._resolve_shop_currency(
                transfer.customer_branch_id,
                transfer.currency or (source_stock.currency if source_stock else None),
                user=request.user,
                create=True,
            )
            transfer_unit = self._resolve_destination_unit(
                transfer.customer_branch_id,
                source_stock.unit,
                user=request.user,
            )

            target_stock = self._find_target_stock_for_transfer(
                transfer,
                transfer.customer_branch_id,
                transfer_currency.id if transfer_currency else transfer.currency_id,
            )
            if target_stock:
                # Combine any duplicate same-product/currency rows first.
                target_stock = self._consolidate_duplicate_destination_stocks(
                    target_stock,
                    transfer,
                    destination_product=destination_product,
                    destination_currency=transfer_currency,
                )
                old_qty = Decimal(str(target_stock.quantity or 0))
                incoming_qty = Decimal(str(transfer.transfer_quantity or 0))
                incoming_cost = Decimal(str(
                    transfer.sale_price
                    if transfer.sale_price is not None
                    else source_stock.average_buy_price
                    or 0
                ))
                total_qty = old_qty + incoming_qty
                if total_qty > 0:
                    target_stock.average_buy_price = (
                        (old_qty * Decimal(str(target_stock.average_buy_price or 0)))
                        + (incoming_qty * incoming_cost)
                    ) / total_qty
                    incoming_wholesale = self._transfer_wholesale_price(transfer, source_stock)
                    if incoming_wholesale is not None:
                        target_stock.average_wholesale_price = (
                            (old_qty * Decimal(str(target_stock.average_wholesale_price or 0)))
                            + (incoming_qty * Decimal(str(incoming_wholesale)))
                        ) / total_qty
                    incoming_retail = self._transfer_retail_price(transfer, source_stock)
                    if incoming_retail is not None:
                        target_stock.average_retail_price = (
                            (old_qty * Decimal(str(target_stock.average_retail_price or 0)))
                            + (incoming_qty * Decimal(str(incoming_retail)))
                        ) / total_qty
                target_stock.quantity = int(total_qty)
                self._apply_transfer_prices_to_stock(target_stock, transfer, source_stock)
                target_stock.save(update_fields=[
                    "quantity",
                    "average_buy_price",
                    "average_wholesale_price",
                    "average_retail_price",
                    "wholesale_price",
                    "retail_price",
                    "sale_price",
                    "updated_at",
                ])
            else:
                incoming_cost = Decimal(str(
                    transfer.sale_price
                    if transfer.sale_price is not None
                    else source_stock.average_buy_price
                    or 0
                ))
                target_stock = Stock.objects.create(
                    shop_id=transfer.customer_branch_id,
                    purchase_item=None,
                    quantity=int(transfer.transfer_quantity or 0),
                    sale_price=transfer.sale_price if transfer.sale_price is not None else source_stock.sale_price,
                    average_buy_price=incoming_cost,
                    average_wholesale_price=self._transfer_wholesale_price(transfer, source_stock),
                    average_retail_price=self._transfer_retail_price(transfer, source_stock),
                    wholesale_price=self._transfer_wholesale_price(transfer, source_stock),
                    retail_price=self._transfer_retail_price(transfer, source_stock),
                    comments="",
                    created_by=request.user,
                )

            transfer_buy_price = (
                transfer.sale_price
                if transfer.sale_price is not None
                else (source_stock.sale_price or source_stock.retail_price or 0)
            )

            if transfer.show_payment_button:
                # Full transfer: create supplier + purchase records for payment tracking.
                supplier_branch = transfer.supplier_branch
                supplier_name = f"Transfer from {supplier_branch.name}"
                receiving_supplier, _ = Supplier.objects.get_or_create(
                    shop_id=transfer.customer_branch_id,
                    name=supplier_name,
                    defaults={'created_by': request.user}
                )

                purchase = Purchase.objects.filter(
                    shop_id=transfer.customer_branch_id,
                    supplier=receiving_supplier,
                    purchase_date=transfer.transfer_date or timezone.localdate(),
                    created_by=request.user,
                    is_deleted=False
                ).order_by("-id").first()

                if purchase:
                    # Keep amount=0: on Purchase, amount means discount — not transfer invoice total.
                    # Invoice totals come from purchase_items / transfer lines.
                    if purchase.amount not in (None, 0, Decimal("0"), Decimal("0.00")):
                        purchase.amount = 0
                        if transfer_currency and purchase.currency_id != transfer_currency.id:
                            purchase.currency = transfer_currency
                            purchase.save(update_fields=["amount", "currency", "updated_at"])
                        else:
                            purchase.save(update_fields=["amount", "updated_at"])
                    elif transfer_currency and purchase.currency_id != transfer_currency.id:
                        purchase.currency = transfer_currency
                        purchase.save(update_fields=["currency", "updated_at"])
                else:
                    purchase = Purchase.objects.create(
                        shop_id=transfer.customer_branch_id,
                        supplier=receiving_supplier,
                        currency=transfer_currency,
                        amount=0,
                        received_amount=0,
                        purchase_date=transfer.transfer_date or timezone.localdate(),
                        created_by=request.user,
                    )

                purchase_item = PurchaseItem.objects.create(
                    purchase=purchase,
                    currency=transfer_currency,
                    product=destination_product or transfer.product,
                    unit=transfer_unit,
                    quantity=int(transfer.transfer_quantity or 0),
                    buy_price=transfer_buy_price,
                    wholesale_price=self._transfer_wholesale_price(transfer, source_stock),
                    retail_price=self._transfer_retail_price(transfer, source_stock),
                    sale_price=transfer.sale_price if transfer.sale_price is not None else source_stock.sale_price,
                    created_by=request.user
                )

                # Attach only. Do not add qty again — destination stock already
                # received transfer_quantity above. PurchaseViewSet must never
                # run on this PurchaseItem.

                # Always attach Transfer supplier purchase to destination stock so
                # Stock list shows "Transfer from …" when Payment is enabled.
                if target_stock:
                    target_stock.purchase_item = purchase_item
                    target_stock.save(update_fields=["purchase_item"])

                # From-branch sale so dashboard Sales / Income update.
                self._ensure_transfer_sale(
                    transfer,
                    source_stock=source_stock,
                    user=request.user,
                )
            else:
                # Stock-only transfer: inventory move with no supplier / branch debt.
                target_stock = self._ensure_stock_only_no_transfer_supplier(
                    target_stock,
                    transfer,
                    destination_product,
                    transfer_currency,
                    transfer_unit,
                    transfer_buy_price,
                    source_stock,
                    request.user,
                )

            # Final combine pass: one stock row per product + currency at destination.
            if target_stock:
                target_stock = self._consolidate_duplicate_destination_stocks(
                    target_stock,
                    transfer,
                    destination_product=destination_product,
                    destination_currency=transfer_currency,
                )
                if transfer.show_payment_button:
                    target_stock = self._ensure_target_stock_listable(
                        target_stock,
                        transfer,
                        destination_product,
                        transfer_currency,
                        transfer_unit,
                        transfer_buy_price,
                        source_stock,
                        request.user,
                    )
                else:
                    # Re-assert no Transfer supplier after merge (sibling may have had one).
                    target_stock = self._ensure_stock_only_no_transfer_supplier(
                        target_stock,
                        transfer,
                        destination_product,
                        transfer_currency,
                        transfer_unit,
                        transfer_buy_price,
                        source_stock,
                        request.user,
                    )

            transfer.status = StockTransfer.Status.ACCEPTED
            transfer.source_quantity_reserved = False
            transfer.accepted_date = timezone.localdate()
            transfer.accepted_datetime = timezone.now()
            transfer.accepted_by = request.user
            transfer.target_stock = target_stock
            transfer.save(update_fields=[
                "target_stock",
                "status",
                "source_quantity_reserved",
                "accepted_date",
                "accepted_datetime",
                "accepted_by",
                "updated_at",
            ])
        return Response(self.get_serializer(transfer).data, status=status.HTTP_200_OK)




    @action(detail=True, methods=["post"], url_path="reject")
    def reject(self, request, pk=None):
        transfer = self.get_object()
        if transfer.status == StockTransfer.Status.ACCEPTED:
            return Response({"detail": "Transfer already accepted."}, status=status.HTTP_400_BAD_REQUEST)
        if transfer.status == StockTransfer.Status.REJECTED:
            return Response({"detail": "Transfer already rejected."}, status=status.HTTP_400_BAD_REQUEST)
        if not self._user_can_manage_transfer(request.user, transfer):
            return Response({"detail": "You do not have access to reject this transfer."}, status=status.HTTP_403_FORBIDDEN)





        with transaction.atomic():
            transfer = StockTransfer.objects.select_for_update().get(pk=transfer.pk)
            if transfer.status == StockTransfer.Status.ACCEPTED:
                return Response({"detail": "Transfer already accepted."}, status=status.HTTP_400_BAD_REQUEST)
            if transfer.status == StockTransfer.Status.REJECTED:
                return Response({"detail": "Transfer already rejected."}, status=status.HTTP_400_BAD_REQUEST)

            transfer = self._mark_transfer_rejected(transfer)

        return Response(self.get_serializer(transfer).data, status=status.HTTP_200_OK)

    def _find_related_purchase(self, transfer):
        supplier_name = f"Transfer from {transfer.supplier_branch.name}" if transfer.supplier_branch else None
        if not supplier_name:
            return None

        return (
            Purchase.objects.select_for_update()
            .filter(
                shop_id=transfer.customer_branch_id,
                supplier__name=supplier_name,
                purchase_date=transfer.transfer_date or transfer.accepted_date or timezone.localdate(),
                created_by=transfer.accepted_by or transfer.created_by,
                is_deleted=False,
            )
            .order_by("-id")
            .first()
        )

    @staticmethod
    def _transfer_sale_notes(transfer):
        return f"Stock transfer sale for {transfer.transfer_id}"

    def _ensure_transfer_sale(self, transfer, source_stock=None, user=None, replace_existing=False):
        """Create/update a Sale on the from-branch when Payment is ticked.

        Dashboard Sales and Income read from Sale rows only. Without this, a
        payment-mode transfer never appears on the from-branch dashboard.
        Stock was already moved by the transfer — do not touch stock qty here.
        """
        if not transfer or not transfer.show_payment_button:
            return None
        if not transfer.supplier_branch_id or not transfer.customer_branch_id:
            return None

        notes = self._transfer_sale_notes(transfer)
        existing = (
            Sale.objects.filter(
                shop_id=transfer.supplier_branch_id,
                notes=notes,
            )
            .prefetch_related("sale_items")
            .order_by("-id")
            .first()
        )
        if existing and not replace_existing:
            return existing
        if existing and replace_existing:
            existing.sale_items.all().delete()
            existing.delete()
            existing = None

        customer_name = f"Transfer to {transfer.customer_branch.name}"
        customer, _ = Customer.objects.get_or_create(
            shop_id=transfer.supplier_branch_id,
            name=customer_name,
            defaults={"created_by": user or transfer.created_by},
        )

        sale_currency = resolve_shop_currency(
            transfer.supplier_branch_id,
            transfer.currency or (source_stock.currency if source_stock else None),
            user=user or transfer.created_by,
            create=True,
        )

        unit_price = Decimal(
            str(
                transfer.sale_price
                if transfer.sale_price is not None
                else (source_stock.sale_price if source_stock else 0) or 0
            )
        )
        buy_price = None
        if source_stock and source_stock.average_buy_price is not None:
            buy_price = source_stock.average_buy_price
        elif source_stock and source_stock.purchase_item_id:
            buy_price = source_stock.purchase_item.buy_price

        sale = Sale.objects.create(
            shop_id=transfer.supplier_branch_id,
            customer=customer,
            sale_date=transfer.transfer_date or transfer.accepted_date or timezone.localdate(),
            currency=sale_currency,
            amount=0,
            received_amount=0,
            status=PaymentStatus.PENDING,
            notes=notes,
            created_by=user or transfer.accepted_by or transfer.created_by,
        )
        SaleItem.objects.create(
            sale=sale,
            product_id=transfer.product_id,
            purchase_item_id=(
                source_stock.purchase_item_id if source_stock else None
            ),
            quantity=int(transfer.transfer_quantity or 0),
            unit=(source_stock.unit if source_stock else None),
            buy_price=buy_price,
            sale_price=unit_price,
            created_by=user or transfer.accepted_by or transfer.created_by,
        )
        return sale

    def _reverse_transfer_sale(self, transfer):
        if not transfer or not transfer.supplier_branch_id:
            return
        notes = self._transfer_sale_notes(transfer)
        sales = Sale.objects.filter(
            shop_id=transfer.supplier_branch_id,
            notes=notes,
        )
        for sale in sales:
            sale.sale_items.all().delete()
            sale.delete()

    def _find_related_purchase_item(self, transfer, purchase):
        if not purchase:
            return None

        quantity = int(transfer.transfer_quantity or 0)
        sale_price = Decimal(str(transfer.sale_price)) if transfer.sale_price is not None else None

        candidates = purchase.purchase_items.select_for_update().filter(
            product_id=transfer.product_id,
            quantity=quantity,
        )
        if sale_price is not None:
            candidates = candidates.filter(models.Q(buy_price=sale_price) | models.Q(sale_price=sale_price))

        if transfer.currency_id:
            candidates = candidates.filter(models.Q(currency_id=transfer.currency_id) | models.Q(currency__isnull=True))

        return candidates.order_by("-id").first()

    def _delete_transfer_auto_payment(self, transfer, purchase):
        if not purchase:
            return Decimal("0")

        deleted_amount = Decimal("0")
        auto_payments = purchase.payments.filter(
            notes__startswith=f"Automatic stock transfer payment for {transfer.transfer_id} "
        )
        for payment in auto_payments:
            AccountTransaction.objects.filter(
                description__startswith=f"Purchase payment #{payment.id} "
            ).delete()
            deleted_amount += Decimal(str(payment.amount or 0))
            payment.delete()
        return deleted_amount

    def _reverse_accepted_transfer(self, transfer, source_stock=None, target_stock=None):
        transfer_quantity = int(transfer.transfer_quantity or 0)
        if target_stock and int(target_stock.quantity or 0) < transfer_quantity:
            raise ValidationError(
                {
                    "detail": (
                        "This transfer cannot be deleted because some transferred "
                        "quantity has already been sold or used at the destination."
                    )
                }
            )

        restored_source_stock = source_stock
        if restored_source_stock:
            self._restore_source_stock_transfer_valuation(restored_source_stock, transfer)

        if not target_stock:
            return None

        remaining_quantity = int(target_stock.quantity or 0) - transfer_quantity
        purchase_item = target_stock.purchase_item
        purchase = purchase_item.purchase if purchase_item else None

        if purchase and transfer.show_payment_button:
            self._delete_transfer_auto_payment(transfer, purchase)
            if purchase.amount not in (None, 0, Decimal("0"), Decimal("0.00")):
                purchase.amount = 0
                purchase.save(update_fields=["amount", "updated_at"])

        self._reverse_transfer_sale(transfer)

        if purchase_item:
            purchase_item.quantity = max(int(purchase_item.quantity or 0) - int(transfer.transfer_quantity or 0), 0)
            if purchase_item.quantity <= 0:
                purchase_item.delete()
            else:
                purchase_item.save(update_fields=["quantity", "updated_at"])

        if remaining_quantity <= 0:
            transfer.target_stock = None
            transfer.save(update_fields=["target_stock", "updated_at"])
            target_stock.delete()
        else:
            target_stock.quantity = remaining_quantity
            target_stock.save(update_fields=["quantity", "updated_at"])

        if purchase and not purchase.purchase_items.exists():
            purchase.delete()

        return None

    def _mark_transfer_rejected(self, transfer):
        if transfer.status == StockTransfer.Status.PENDING:
            self._release_source_stock(transfer)
        transfer.status = StockTransfer.Status.REJECTED
        transfer.accepted_date = None
        transfer.accepted_datetime = None
        transfer.accepted_by = None
        transfer.target_stock = None
        transfer.save(
            update_fields=[
                "target_stock",
                "status",
                "accepted_date",
                "accepted_datetime",
                "accepted_by",
                "updated_at",
            ]
        )
        return transfer

    def destroy(self, request, *args, **kwargs):
        transfer = self.get_object()

        with transaction.atomic():
            transfer = (
                StockTransfer.objects.select_for_update()
                .select_related(
                    "product",
                    "supplier_branch",
                    "customer_branch",
                    "currency",
                    "created_by",
                    "accepted_by",
                    "source_stock",
                    "source_stock__purchase_item",
                    "source_stock__purchase_item__purchase",
                    "target_stock",
                    "target_stock__purchase_item",
                    "target_stock__purchase_item__purchase",
                )
                .get(pk=transfer.pk)
            )

            if transfer.status == StockTransfer.Status.ACCEPTED:
                source_stock = Stock.objects.select_for_update().filter(
                    pk=transfer.source_stock_id,
                    is_deleted=False,
                ).first()
                target_stock = (
                    Stock.objects.select_for_update()
                    .select_related("purchase_item", "purchase_item__purchase")
                    .filter(pk=transfer.target_stock_id, is_deleted=False)
                    .first()
                )
                if source_stock:
                    self._reverse_accepted_transfer(transfer, source_stock, target_stock)
                transfer.delete()
            elif transfer.status == StockTransfer.Status.REJECTED:
                transfer.delete()
            else:
                transfer = self._mark_transfer_rejected(transfer)

        return Response(status=status.HTTP_204_NO_CONTENT)
