#!/usr/bin/env python
"""Marketplace catalog cleanup and normalization.

What this script does:
1) Activates all bundle-linked products for marketplace visibility.
2) Ensures consistent names (removes hidden zero-width chars).
3) Generates unique slugs and SKUs where missing (or optionally refreshes all).
4) Generates consistent descriptions (optionally for all products).
5) Trims ACTIVE non-bundle marketplace items down to a target count (default: 100).

Usage examples:
  python scripts/marketplace_catalog_cleanup.py
  python scripts/marketplace_catalog_cleanup.py --keep-retail-active 100 --overwrite-descriptions
"""

import argparse
import os
import re
import sys
from decimal import Decimal

import django
from django.db import transaction
from django.utils.text import slugify


BACKEND_ROOT = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
if BACKEND_ROOT not in sys.path:
    sys.path.insert(0, BACKEND_ROOT)


ZERO_WIDTH_RE = re.compile(r"[\u200b\u200c\u200d\ufeff]")
TEST_NAME_RE = re.compile(r"\b(test|demo|dummy|sample|load)\b", re.IGNORECASE)


def clean_name(name: str) -> str:
    cleaned = ZERO_WIDTH_RE.sub("", name or "")
    cleaned = re.sub(r"\s+", " ", cleaned).strip()
    return cleaned


def build_description(product) -> str:
    category = (product.category or "PRODUCT").replace("_", " ").title()
    product_type = (product.product_type or "STANDARD").replace("_", " ").title()
    base_cost = Decimal(product.base_cost or 0)
    mrp = Decimal(product.mrp or product.base_cost or 0)
    gst = Decimal(product.gst_percentage or 0)

    points = [
        f"{product.name} is a {category.lower()} offering under the TrueWave catalog.",
        f"Product type: {product_type}.",
        f"Base price: INR {base_cost:.2f}; MRP: INR {mrp:.2f}; GST: {gst:.3f}%.",
        "Suitable for structured marketplace purchase and invoice-ready order processing.",
    ]

    if product.opportunity_bundle_id:
        points.append(
            "This item is linked to an opportunity bundle and is configured for single-item bundle purchase flow."
        )
    else:
        points.append(
            "This item is configured for standard retail marketplace flow, including cart and checkout compatibility."
        )

    return " ".join(points)


def is_testish_name(name: str) -> bool:
    return bool(TEST_NAME_RE.search(name or ""))


def unique_slug_for(product, used_slugs, refresh=False):
    current = (product.slug or "").strip() if product.slug else ""
    if current and not refresh:
        return current

    base = slugify(clean_name(product.name)) or f"product-{str(product.id)[:8]}"
    candidate = base
    idx = 2
    while candidate in used_slugs:
        candidate = f"{base}-{idx}"
        idx += 1
    used_slugs.add(candidate)
    return candidate


def unique_sku_for(product, used_skus, refresh=False):
    current = (product.sku or "").strip() if product.sku else ""
    if current and not refresh:
        return current

    base_name = re.sub(r"[^A-Za-z0-9]+", "", clean_name(product.name).upper())[:8] or "PRD"
    base = f"TWM-{base_name}-{str(product.id)[:6].upper()}"
    candidate = base
    idx = 2
    while candidate in used_skus:
        candidate = f"{base}-{idx}"
        idx += 1
    used_skus.add(candidate)
    return candidate


def main():
    parser = argparse.ArgumentParser(description="Normalize marketplace catalog data")
    parser.add_argument("--settings", default="config.settings.dev", help="Django settings module")
    parser.add_argument("--keep-retail-active", type=int, default=100, help="How many non-bundle ACTIVE marketplace items to keep")
    parser.add_argument("--overwrite-descriptions", action="store_true", help="Regenerate descriptions for all products (not only missing)")
    parser.add_argument("--refresh-slug-sku", action="store_true", help="Regenerate slug/sku for all products")
    args = parser.parse_args()

    os.environ.setdefault("DJANGO_SETTINGS_MODULE", args.settings)
    django.setup()

    from apps.business.products.models import Product

    qs_all = Product.objects.all().order_by("created_at", "id")

    active_total_before = Product.objects.filter(is_active=True).count()
    active_market_before = Product.objects.filter(is_active=True, marketplace_status="ACTIVE").count()
    active_bundle_before = Product.objects.filter(is_active=True, opportunity_bundle__isnull=False).count()

    used_slugs = set(
        Product.objects.exclude(slug__isnull=True).exclude(slug__exact="").values_list("slug", flat=True)
    )
    used_skus = set(
        Product.objects.exclude(sku__isnull=True).exclude(sku__exact="").values_list("sku", flat=True)
    )

    updated_count = 0
    bundle_activated = 0
    names_cleaned = 0
    slug_updated = 0
    sku_updated = 0
    desc_updated = 0

    with transaction.atomic():
        # 1) Normalize every product for consistency.
        for p in qs_all.iterator(chunk_size=500):
            changed = False

            # Name cleanup
            new_name = clean_name(p.name)
            if new_name and new_name != p.name:
                p.name = new_name
                changed = True
                names_cleaned += 1

            # Bundle-linked products should be marketplace ACTIVE.
            if p.opportunity_bundle_id and p.marketplace_status != "ACTIVE":
                p.marketplace_status = "ACTIVE"
                changed = True
                bundle_activated += 1

            # Slug/SKU consistency
            new_slug = unique_slug_for(p, used_slugs, refresh=args.refresh_slug_sku)
            if new_slug != (p.slug or ""):
                p.slug = new_slug
                changed = True
                slug_updated += 1

            new_sku = unique_sku_for(p, used_skus, refresh=args.refresh_slug_sku)
            if new_sku != (p.sku or ""):
                p.sku = new_sku
                changed = True
                sku_updated += 1

            # Description consistency
            should_set_desc = args.overwrite_descriptions or not (p.description and p.description.strip())
            if should_set_desc:
                new_desc = build_description(p)
                if new_desc != (p.description or ""):
                    p.description = new_desc
                    changed = True
                    desc_updated += 1

            if changed:
                p.save(update_fields=[
                    "name", "marketplace_status", "slug", "sku", "description", "updated_at"
                ])
                updated_count += 1

        # 2) Reduce ACTIVE retail marketplace items to target size.
        keep = max(0, args.keep_retail_active)

        active_retail_qs = Product.objects.filter(
            is_active=True,
            opportunity_bundle__isnull=True,
            marketplace_status="ACTIVE",
        )

        active_retail = list(
            active_retail_qs.values("id", "name", "updated_at", "created_at")
        )

        # Keep non-test-like products first, then most recently updated.
        ranked = sorted(
            active_retail,
            key=lambda row: (
                is_testish_name(row["name"]),
                -(row["updated_at"].timestamp() if row["updated_at"] else 0),
                -(row["created_at"].timestamp() if row["created_at"] else 0),
            ),
        )

        keep_ids = {row["id"] for row in ranked[:keep]}
        drop_ids = [row["id"] for row in ranked[keep:]]

        dropped = 0
        if drop_ids:
            dropped = Product.objects.filter(id__in=drop_ids).update(marketplace_status="INACTIVE")

    active_total_after = Product.objects.filter(is_active=True).count()
    active_market_after = Product.objects.filter(is_active=True, marketplace_status="ACTIVE").count()
    active_bundle_after = Product.objects.filter(is_active=True, opportunity_bundle__isnull=False).count()
    active_retail_after = Product.objects.filter(
        is_active=True,
        opportunity_bundle__isnull=True,
        marketplace_status="ACTIVE",
    ).count()

    print("=== MARKETPLACE CATALOG CLEANUP SUMMARY ===")
    print(f"Products updated: {updated_count}")
    print(f"Bundle products set to ACTIVE: {bundle_activated}")
    print(f"Names cleaned: {names_cleaned}")
    print(f"Slugs updated: {slug_updated}")
    print(f"SKUs updated: {sku_updated}")
    print(f"Descriptions updated: {desc_updated}")
    print("---")
    print(f"ACTIVE total: {active_total_before} -> {active_total_after}")
    print(f"ACTIVE marketplace: {active_market_before} -> {active_market_after}")
    print(f"ACTIVE bundle: {active_bundle_before} -> {active_bundle_after}")
    print(f"ACTIVE retail marketplace after trim: {active_retail_after}")
    print(f"Retail keep target: {args.keep_retail_active}")


if __name__ == "__main__":
    main()
