"""
Audit (and optionally heal) one branch: stock, purchases, sales, transfers.

Example (Kabul Main Branch):

  python manage.py audit_branch_inventory --shop-name "Kabul Main Branch"
  python manage.py audit_branch_inventory --shop-name "Kabul Main Branch" --heal-prices
  python manage.py audit_branch_inventory --shop-name "Kabul Main Branch" --heal-prices --dry-run
"""

from decimal import Decimal, ROUND_HALF_UP

from django.core.management.base import BaseCommand
from django.db import transaction
from django.db.models import Q, Sum

from shop_management.models.data.purchase import Purchase, PurchaseItem
from shop_management.models.data.sale import Sale, SaleItem
from shop_management.models.data.shop import Shop
from shop_management.models.data.stock import Stock
from shop_management.models.data.stock_transfer import StockTransfer
from shop_management.views.data.stock import normalize_currency_code


TWOPLACES = Decimal("0.01")


def _money(value):
    if value is None:
        return None
    return Decimal(str(value)).quantize(TWOPLACES, rounding=ROUND_HALF_UP)


class Command(BaseCommand):
    help = (
        "Audit branch inventory: stock totals, missing prices, "
        "transfer-sales vs customer sales, purchase/transfer counts. "
        "Optional --heal-prices fills blank stock buy/wholesale/retail from purchase_item."
    )

    def add_arguments(self, parser):
        parser.add_argument("--shop", type=int, help="Shop id")
        parser.add_argument(
            "--shop-name",
            type=str,
            help='Shop name, e.g. "Kabul Main Branch"',
        )
        parser.add_argument(
            "--heal-prices",
            action="store_true",
            help="Copy purchase_item prices onto stock when average/buy prices are blank/zero.",
        )
        parser.add_argument(
            "--dry-run",
            action="store_true",
            help="With --heal-prices, show changes only.",
        )
        parser.add_argument(
            "--list-missing",
            action="store_true",
            help="Print every stock row with missing buy price.",
        )

    def handle(self, *args, **options):
        shop = self._resolve_shop(options)
        if not shop:
            return

        self.stdout.write(self.style.MIGRATE_HEADING(f"=== {shop.name} (id={shop.id}) ==="))
        self._print_counts(shop)
        missing = self._print_stock_value_and_missing(shop, list_missing=options["list_missing"])
        self._print_sales_breakdown(shop)
        self._print_transfer_breakdown(shop)

        if options["heal_prices"]:
            self._heal_prices(shop, missing, dry_run=options["dry_run"])
        else:
            self.stdout.write("")
            self.stdout.write(
                "Next (optional heal missing prices):\n"
                f'  python manage.py audit_branch_inventory --shop-name "{shop.name}" --heal-prices --dry-run\n'
                f'  python manage.py audit_branch_inventory --shop-name "{shop.name}" --heal-prices\n'
                "\n"
                "Next (merge duplicate products/stock + repair transfer sales):\n"
                f"  python manage.py fix_branch_inventory --shop={shop.id} --dry-run\n"
                f"  python manage.py fix_branch_inventory --shop={shop.id}"
            )

    def _resolve_shop(self, options):
        shop_id = options.get("shop")
        shop_name = (options.get("shop_name") or "").strip()
        if shop_id:
            shop = Shop.objects.filter(pk=shop_id).first()
        elif shop_name:
            shop = Shop.objects.filter(name__iexact=shop_name).first()
            if not shop:
                shop = Shop.objects.filter(name__icontains=shop_name).first()
        else:
            self.stderr.write(self.style.ERROR("Use --shop=<id> or --shop-name=\"...\""))
            return None
        if not shop:
            self.stderr.write(self.style.ERROR("Shop not found."))
            return None
        return shop

    def _listable_stocks(self, shop):
        return (
            Stock.objects.filter(
                shop=shop,
                is_deleted=False,
                quantity__gt=0,
                purchase_item__isnull=False,
                purchase_item__is_deleted=False,
                purchase_item__purchase__is_deleted=False,
            )
            .select_related(
                "purchase_item",
                "purchase_item__product",
                "purchase_item__currency",
                "purchase_item__purchase__currency",
                "purchase_item__purchase__supplier",
            )
            .order_by("-id")
        )

    def _print_counts(self, shop):
        stocks = self._listable_stocks(shop)
        products = set()
        qty = 0
        for s in stocks:
            products.add(s.purchase_item.product_id)
            qty += int(s.quantity or 0)

        purchases = Purchase.objects.filter(shop=shop, is_deleted=False).count()
        purchase_items = PurchaseItem.objects.filter(
            purchase__shop=shop, purchase__is_deleted=False, is_deleted=False
        ).count()
        sales = Sale.objects.filter(shop=shop).count()
        sale_items = SaleItem.objects.filter(sale__shop=shop).count()
        transfers_out = StockTransfer.objects.filter(
            supplier_branch=shop, status=StockTransfer.Status.ACCEPTED
        ).count()
        transfers_in = StockTransfer.objects.filter(
            customer_branch=shop, status=StockTransfer.Status.ACCEPTED
        ).count()

        self.stdout.write(f"Stock rows (listable):     {stocks.count()}")
        self.stdout.write(f"Available products:        {len(products)}")
        self.stdout.write(f"Total available qty:       {qty}")
        self.stdout.write(f"Purchases:                 {purchases} (items={purchase_items})")
        self.stdout.write(f"Sales:                     {sales} (items={sale_items})")
        self.stdout.write(f"Accepted transfers OUT:    {transfers_out}")
        self.stdout.write(f"Accepted transfers IN:     {transfers_in}")

    def _effective_buy(self, stock):
        if stock.average_buy_price is not None and Decimal(str(stock.average_buy_price)) > 0:
            return Decimal(str(stock.average_buy_price))
        pi = stock.purchase_item
        if pi and pi.buy_price is not None and Decimal(str(pi.buy_price)) > 0:
            return Decimal(str(pi.buy_price))
        return None

    def _print_stock_value_and_missing(self, shop, list_missing=False):
        totals = {}
        missing = []
        for stock in self._listable_stocks(shop):
            qty = Decimal(str(stock.quantity or 0))
            code = normalize_currency_code(stock.currency.title if stock.currency else "")
            buy = self._effective_buy(stock)
            if buy is None:
                missing.append(stock)
            elif code:
                totals[code] = totals.get(code, Decimal("0")) + qty * buy

        self.stdout.write("")
        self.stdout.write("Total Stock Value:")
        if not totals:
            self.stdout.write("  (none)")
        else:
            for code, amount in sorted(totals.items()):
                self.stdout.write(f"  {code}: {_money(amount)}")

        self.stdout.write("")
        self.stdout.write(
            self.style.WARNING(f"Stock rows missing buy price: {len(missing)}")
        )
        if list_missing or len(missing) <= 40:
            for stock in missing:
                name = stock.purchase_item.product.name if stock.purchase_item and stock.purchase_item.product else "?"
                code = (stock.purchase_item.product.code if stock.purchase_item and stock.purchase_item.product else "") or "-"
                self.stdout.write(
                    f"  stock#{stock.id} qty={stock.quantity} code={code} name={name}"
                )
        elif missing:
            self.stdout.write("  (use --list-missing to print all)")

        return missing

    def _print_sales_breakdown(self, shop):
        sales = Sale.objects.filter(shop=shop)
        transfer_q = Q(notes__startswith="Stock transfer sale for ") | Q(
            customer__name__startswith="Transfer to "
        )
        transfer_sales = sales.filter(transfer_q).distinct()
        customer_sales = sales.exclude(pk__in=transfer_sales.values("pk"))

        self.stdout.write("")
        self.stdout.write(self.style.NOTICE("Sales breakdown (why Sale List shows transfers):"))
        self.stdout.write(
            "  When Stock Transfer has Payment ON, the system creates a Sale on the "
            "from-branch (Kabul) so Dashboard income shows it."
        )
        self.stdout.write(
            "  Stock qty is reduced ONLY by the transfer accept — the transfer Sale "
            "does NOT reduce stock again."
        )
        self.stdout.write(f"  Customer sales:   {customer_sales.count()}")
        self.stdout.write(f"  Transfer sales:   {transfer_sales.count()}")
        recent = transfer_sales.order_by("-id")[:8]
        for sale in recent:
            self.stdout.write(
                f"    sale#{sale.id} date={sale.sale_date} "
                f"customer={sale.customer.name if sale.customer else '-'} "
                f"notes={sale.notes}"
            )

    def _print_transfer_breakdown(self, shop):
        qs = StockTransfer.objects.filter(
            supplier_branch=shop,
            status=StockTransfer.Status.ACCEPTED,
        )
        payment_on = qs.filter(show_payment_button=True).count()
        payment_off = qs.filter(show_payment_button=False).count()
        qty = qs.aggregate(total=Sum("transfer_quantity"))["total"] or 0
        self.stdout.write("")
        self.stdout.write("Transfers OUT from this branch:")
        self.stdout.write(f"  Accepted:     {qs.count()} (qty={qty})")
        self.stdout.write(f"  Payment ON:   {payment_on}  → creates Sale + supplier on destination")
        self.stdout.write(f"  Payment OFF:  {payment_off}  → stock move only, no Sale")

    def _heal_prices(self, shop, missing_stocks, dry_run=False):
        healed = 0
        skipped = 0
        self.stdout.write("")
        self.stdout.write(
            self.style.MIGRATE_HEADING(
                f"{'DRY-RUN ' if dry_run else ''}Healing missing stock prices..."
            )
        )
        for stock in missing_stocks:
            pi = stock.purchase_item
            if not pi:
                skipped += 1
                continue
            buy = pi.buy_price
            wholesale = pi.wholesale_price
            retail = pi.retail_price
            if buy is None or Decimal(str(buy)) <= 0:
                skipped += 1
                name = pi.product.name if pi.product else "?"
                self.stdout.write(
                    f"  SKIP stock#{stock.id} '{name}' — purchase_item also has no buy_price"
                )
                continue

            updates = {
                "average_buy_price": buy,
            }
            if wholesale is not None:
                updates["average_wholesale_price"] = wholesale
                updates["wholesale_price"] = wholesale
            if retail is not None:
                updates["average_retail_price"] = retail
                updates["retail_price"] = retail
                updates["sale_price"] = retail

            name = pi.product.name if pi.product else "?"
            self.stdout.write(
                f"  {'Would heal' if dry_run else 'Heal'} stock#{stock.id} '{name}' "
                f"buy={buy} wholesale={wholesale} retail={retail}"
            )
            healed += 1
            if dry_run:
                continue
            with transaction.atomic():
                for field, value in updates.items():
                    setattr(stock, field, value)
                stock.save(update_fields=list(updates.keys()) + ["updated_at"])

        self.stdout.write(
            self.style.SUCCESS(
                f"{'Would heal' if dry_run else 'Healed'} {healed}; skipped {skipped}."
            )
        )
