import random
from decimal import Decimal

from django.conf import settings
from django.contrib.auth import get_user_model
from django.core.management.base import BaseCommand, CommandError
from django.db import transaction

from basic_info import models as basic_models
from printing import models as printing_models
from shipment import models as shipment_models


class Command(BaseCommand):
    help = (
        "为开发环境生成一批假的 customer / printing_order / printing_job / sales_item 数据，"
        "用于前端联调 shipment 与 printing 相关页面。"
    )

    def add_arguments(self, parser):
        parser.add_argument("--merchant-id", type=int, default=1, help="目标商户 ID，默认 1")
        parser.add_argument("--user-id", type=int, default=None, help="创建人用户 ID，可选")
        parser.add_argument("--customers", type=int, default=10, help="客户数量，默认 10")
        parser.add_argument("--orders", type=int, default=50, help="订单数量，默认 50")
        parser.add_argument(
            "--jobs-min", type=int, default=3, help="每个订单最少生成多少个 PrintingJob，默认 3"
        )
        parser.add_argument(
            "--jobs-max", type=int, default=30, help="每个订单最多生成多少个 PrintingJob，默认 30"
        )
        parser.add_argument(
            "--items-min", type=int, default=2, help="每个 job 最少生成多少个 SalesItem，默认 2"
        )
        parser.add_argument(
            "--items-max", type=int, default=20, help="每个 job 最多生成多少个 SalesItem，默认 20"
        )
        parser.add_argument(
            "--products", type=int, default=24, help="测试产品数量，默认 24"
        )
        parser.add_argument(
            "--tag",
            type=str,
            default="DEVSHIP",
            help="生成数据的标签前缀，默认 DEVSHIP",
        )
        parser.add_argument(
            "--seed",
            type=int,
            default=20260331,
            help="随机种子，默认 20260331",
        )
        parser.add_argument(
            "--yes",
            action="store_true",
            help="跳过交互确认，直接执行（谨慎使用）",
        )
        parser.add_argument(
            "--dry-run",
            action="store_true",
            help="仅输出执行前检查与预计写入量，不实际写入数据",
        )

    def handle(self, *args, **options):
        random.seed(options["seed"])
        self._validate_options(options)

        merchant = basic_models.Merchant.objects.filter(id=options["merchant_id"]).first()
        if merchant is None:
            raise CommandError(f"merchant_id={options['merchant_id']} 不存在")

        created_by = self._resolve_user(merchant=merchant, user_id=options["user_id"])
        preview = self._collect_preview(merchant=merchant, created_by=created_by, options=options)

        self._print_preview(preview)

        if options["dry_run"]:
            self.stdout.write(self.style.WARNING("dry-run 模式：未写入任何数据"))
            return

        if not options["yes"]:
            self._confirm_or_abort()

        with transaction.atomic():
            counts = self._generate_data(
                merchant=merchant,
                created_by=created_by,
                options=options,
            )

        self._print_summary(
            merchant=merchant,
            created_by=created_by,
            tag=options["tag"],
            counts=counts,
        )

    def _generate_data(self, *, merchant, created_by, options):
        category = self._ensure_category(merchant=merchant, tag=options["tag"])
        products = self._ensure_products(
            merchant=merchant,
            category=category,
            count=options["products"],
            tag=options["tag"],
        )
        customers = self._create_customers(
            merchant=merchant,
            count=options["customers"],
            tag=options["tag"],
        )

        order_count = 0
        job_count = 0
        sales_item_count = 0

        self.stdout.write(self.style.NOTICE("开始生成测试数据..."))
        self.stdout.write("步骤 1/3: 准备客户与产品")
        self.stdout.write(f"  客户数: {len(customers)}")
        self.stdout.write(f"  产品数: {len(products)}")
        self.stdout.write("步骤 2/3: 创建 PrintingOrder / PrintingJob / SalesItem")

        for index in range(1, options["orders"] + 1):
            customer = customers[(index - 1) % len(customers)]
            order = printing_models.PrintingOrder.objects.create(
                merchant=merchant,
                customer=customer,
                fabric=random.choice(["全棉", "涤纶", "尼龙", "人棉", "TC"]),
                width=random.choice(["150cm", "160cm", "170cm", "180cm"]),
                is_urgent=random.choice([False, False, False, True]),
                area=customer.area or random.choice(["广州", "佛山", "绍兴", "杭州"]),
                address=f"{customer.area or '广州'}-{index}号测试地址",
                fabric_source=random.choice(["自带布", "仓库", "客户送布"]),
                is_fabric_received=random.choice([True, False]),
                craft=random.choice(["活性印花", "数码印花", "涂料印花"]),
                description=f"{options['tag']} 测试印染订单 #{index}",
                position=random.choice(["A区", "B区", "C区", "待排产"]),
                printing_warn=random.choice(["", "注意色差", "先确认花型"]),
                rolling_warn=random.choice(["", "注意卷边", "慢速滚筒"]),
                production_warn=random.choice(["", "先打样", "优先安排"]),
                created_by=created_by,
                external_order_id=self._build_external_order_id(index=index),
                external_customer_id=f"CUST-{customer.id}",
                external_customer_name=customer.name,
                external_employee_name=self._display_user(created_by),
            )
            order_count += 1

            jobs_for_order = random.randint(options["jobs_min"], options["jobs_max"])
            for job_index in range(1, jobs_for_order + 1):
                product = random.choice(products)
                job = printing_models.PrintingJob.objects.create(
                    merchant=merchant,
                    printing_order=order,
                    product=product,
                    work_state=random.choice(
                        [
                            printing_models.PrintingJobWorkStateEnum.PRODUCING,
                            printing_models.PrintingJobWorkStateEnum.WAITING_FOR_DELIVERY,
                            printing_models.PrintingJobWorkStateEnum.WAITING_FOR_INVOICE,
                        ]
                    ),
                    quantity=random.randint(80, 3000),
                    unit=random.choice(["米", "码", "件"]),
                    size=random.choice(["", "120x120", "150x150", "200x200"]) or None,
                    pieces=random.choice([None, 1, 2, 4, 6, 8]),
                    description=f"{options['tag']} Job {index}-{job_index}",
                    created_by=created_by,
                    external_product_name=product.name,
                )
                job_count += 1

                sales_items = []
                items_for_job = random.randint(options["items_min"], options["items_max"])
                for item_index in range(1, items_for_job + 1):
                    sales_items.append(
                        shipment_models.SalesItem(
                            shipment=None,
                            merchant=merchant,
                            name=f"{product.name}-销售品-{index}-{job_index}-{item_index}",
                            quantity=Decimal(
                                f"{random.randint(20, 3000)}.{random.randint(0, 99):02d}"
                            ),
                            unit=random.choice(
                                [
                                    shipment_models.UnitChoices.METER,
                                    shipment_models.UnitChoices.PIECE,
                                    shipment_models.UnitChoices.YARD,
                                    shipment_models.UnitChoices.UNIT,
                                ]
                            ),
                            created_by=created_by,
                            printing_job_id=job.id,
                            customer_id=customer.id,
                            position=random.choice(
                                [
                                    "A1-01",
                                    "A1-02",
                                    "B2-03",
                                    "C3-05",
                                    "待分区",
                                    "待发货区",
                                ]
                            ),
                            remark=random.choice(["", "测试数据", "扫码联调用", "优先出货"]),
                        )
                    )
                shipment_models.SalesItem.objects.bulk_create(sales_items, batch_size=500)
                sales_item_count += len(sales_items)

            if index % 10 == 0 or index == options["orders"]:
                self.stdout.write(
                    f"  已完成订单 {index}/{options['orders']}，"
                    f"累计 jobs={job_count}，sales_items={sales_item_count}"
                )

        self.stdout.write("步骤 3/3: 数据写入完成")
        return {
            "customers_created": len(customers),
            "printing_orders_created": order_count,
            "printing_jobs_created": job_count,
            "sales_items_created": sales_item_count,
        }

    def _resolve_user(self, *, merchant, user_id):
        User = get_user_model()
        if user_id is not None:
            user = User.objects.filter(id=user_id).first()
            if user is None:
                raise CommandError(f"user_id={user_id} 不存在")
            employee = getattr(user, "employee", None)
            if employee is None or employee.merchant_id != merchant.id:
                raise CommandError("指定 user 未绑定到目标商户的 employee")
            return user

        employee = (
            basic_models.Employee.objects.select_related("sys_user")
            .filter(merchant=merchant, sys_user__isnull=False)
            .order_by("id")
            .first()
        )
        if employee and employee.sys_user:
            return employee.sys_user

        username = f"seed_{merchant.id}_user"
        user = User.objects.create_user(username=username, password="devpass123")
        basic_models.Employee.objects.create(
            merchant=merchant,
            sys_user=user,
            name=f"Seed User {merchant.id}",
            mobile=f"1390000{merchant.id:04d}",
            status=basic_models.EmployeeStatusEnum.ACTIVE,
        )
        return user

    def _ensure_category(self, *, merchant, tag):
        category, _ = basic_models.ProductCategory.objects.get_or_create(
            merchant=merchant,
            name=f"{tag}-测试分类",
            defaults={"product_prefix": "TS"},
        )
        return category

    def _ensure_products(self, *, merchant, category, count, tag):
        products = list(
            basic_models.Product.objects.filter(
                merchant=merchant,
                category=category,
                name__startswith=f"{tag}-测试产品-",
            ).order_by("id")
        )
        missing = count - len(products)
        for index in range(1, missing + 1):
            number = len(products) + index
            products.append(
                basic_models.Product.objects.create(
                    merchant=merchant,
                    category=category,
                    name=f"{tag}-测试产品-{number:03d}",
                    human_id=f"{tag[:4]}P{number:03d}",
                    unit=random.choice(
                        [
                            basic_models.ProductUnitEnum.METER,
                            basic_models.ProductUnitEnum.YARD,
                            basic_models.ProductUnitEnum.SEGMENT,
                        ]
                    ),
                    color=random.choice(["红", "蓝", "黑", "白", "灰"]),
                    width_size=Decimal(random.choice(["150.00", "160.00", "170.00"])),
                    description="开发环境自动生成的测试产品",
                    from_mdy=False,
                )
            )
        return list(
            basic_models.Product.objects.filter(
                merchant=merchant,
                category=category,
                name__startswith=f"{tag}-测试产品-",
            ).order_by("id")[:count]
        )

    def _create_customers(self, *, merchant, count, tag):
        customers = []
        employee = (
            basic_models.Employee.objects.filter(merchant=merchant).order_by("id").first()
        )
        if employee is None:
            raise CommandError("目标商户下没有 employee，无法创建 customer")

        for index in range(1, count + 1):
            name = f"{tag}-测试客户-{index:02d}"
            customer, _ = basic_models.Customer.objects.get_or_create(
                merchant=merchant,
                name=name,
                defaults={
                    "created_by": employee,
                    "mobile": f"138{merchant.id:02d}{index:06d}"[:11],
                    "area": random.choice(["广州", "佛山", "绍兴", "杭州", "苏州"]),
                    "contact": f"联系人{index:02d}",
                    "description": "开发环境自动生成的测试客户",
                },
            )
            customers.append(customer)
        return customers

    def _build_external_order_id(self, *, index):
        return f"KD{20135142 + index:08d}"

    def _validate_options(self, options):
        if options["customers"] <= 0:
            raise CommandError("--customers 必须大于 0")
        if options["orders"] <= 0:
            raise CommandError("--orders 必须大于 0")
        if options["products"] <= 0:
            raise CommandError("--products 必须大于 0")
        if options["jobs_min"] <= 0 or options["jobs_max"] <= 0:
            raise CommandError("--jobs-min / --jobs-max 必须大于 0")
        if options["items_min"] <= 0 or options["items_max"] <= 0:
            raise CommandError("--items-min / --items-max 必须大于 0")
        if options["jobs_min"] > options["jobs_max"]:
            raise CommandError("--jobs-min 不能大于 --jobs-max")
        if options["items_min"] > options["items_max"]:
            raise CommandError("--items-min 不能大于 --items-max")

    def _collect_preview(self, *, merchant, created_by, options):
        db = settings.DATABASES["default"]
        tag = options["tag"]

        existing_customer_count = basic_models.Customer.objects.filter(
            merchant=merchant,
            name__startswith=f"{tag}-测试客户-",
        ).count()
        existing_order_count = printing_models.PrintingOrder.objects.filter(
            merchant=merchant,
            description__startswith=f"{tag} 测试印染订单 #",
        ).count()
        existing_job_count = printing_models.PrintingJob.objects.filter(
            merchant=merchant,
            description__startswith=f"{tag} Job ",
        ).count()
        existing_sales_item_count = shipment_models.SalesItem.objects.filter(
            merchant=merchant,
            name__startswith=f"{tag}-测试产品-",
        ).count()

        jobs_min_total = options["orders"] * options["jobs_min"]
        jobs_max_total = options["orders"] * options["jobs_max"]
        sales_items_min_total = jobs_min_total * options["items_min"]
        sales_items_max_total = jobs_max_total * options["items_max"]

        return {
            "debug": getattr(settings, "DEBUG", None),
            "db_host": db.get("HOST"),
            "db_port": db.get("PORT"),
            "db_name": db.get("NAME"),
            "merchant_id": merchant.id,
            "merchant_name": merchant.name,
            "created_by_id": created_by.id,
            "created_by_username": created_by.username,
            "tag": tag,
            "seed": options["seed"],
            "customers": options["customers"],
            "orders": options["orders"],
            "jobs_min": options["jobs_min"],
            "jobs_max": options["jobs_max"],
            "items_min": options["items_min"],
            "items_max": options["items_max"],
            "products": options["products"],
            "existing_customer_count": existing_customer_count,
            "existing_order_count": existing_order_count,
            "existing_job_count": existing_job_count,
            "existing_sales_item_count": existing_sales_item_count,
            "jobs_min_total": jobs_min_total,
            "jobs_max_total": jobs_max_total,
            "sales_items_min_total": sales_items_min_total,
            "sales_items_max_total": sales_items_max_total,
        }

    def _print_preview(self, preview):
        self.stdout.write(self.style.WARNING("即将生成开发环境测试数据，请先确认以下状态"))
        self.stdout.write("=" * 72)
        self.stdout.write(f"DEBUG               : {preview['debug']}")
        self.stdout.write(
            f"数据库              : {preview['db_name']} @ {preview['db_host']}:{preview['db_port']}"
        )
        self.stdout.write(
            f"目标商户            : {preview['merchant_id']} / {preview['merchant_name']}"
        )
        self.stdout.write(
            f"创建人用户          : {preview['created_by_id']} / {preview['created_by_username']}"
        )
        self.stdout.write(f"数据标签            : {preview['tag']}")
        self.stdout.write(f"随机种子            : {preview['seed']}")
        self.stdout.write("-" * 72)
        self.stdout.write(f"客户数              : {preview['customers']}")
        self.stdout.write(f"订单数              : {preview['orders']}")
        self.stdout.write(
            f"每单 job 数         : {preview['jobs_min']} ~ {preview['jobs_max']}"
        )
        self.stdout.write(
            f"每 job sales item 数: {preview['items_min']} ~ {preview['items_max']}"
        )
        self.stdout.write(f"测试产品数          : {preview['products']}")
        self.stdout.write("-" * 72)
        self.stdout.write(
            f"预计新增 PrintingJob: {preview['jobs_min_total']} ~ {preview['jobs_max_total']}"
        )
        self.stdout.write(
            "预计新增 SalesItem  : "
            f"{preview['sales_items_min_total']} ~ {preview['sales_items_max_total']}"
        )
        self.stdout.write("-" * 72)
        self.stdout.write(
            f"当前同 tag 客户数    : {preview['existing_customer_count']}"
        )
        self.stdout.write(
            f"当前同 tag 订单数    : {preview['existing_order_count']}"
        )
        self.stdout.write(
            f"当前同 tag job 数    : {preview['existing_job_count']}"
        )
        self.stdout.write(
            f"当前同 tag sales item: {preview['existing_sales_item_count']}"
        )
        self.stdout.write("=" * 72)

    def _confirm_or_abort(self):
        answer = input("确认要继续写入这批测试数据吗？请输入 YES 继续: ").strip()
        if answer != "YES":
            raise CommandError("已取消执行，未写入任何数据")

    def _print_summary(self, *, merchant, created_by, tag, counts):
        self.stdout.write(self.style.SUCCESS("本次执行摘要"))
        self.stdout.write("=" * 72)
        self.stdout.write(f"merchant_id         : {merchant.id}")
        self.stdout.write(f"merchant_name       : {merchant.name}")
        self.stdout.write(f"created_by          : {created_by.id}:{created_by.username}")
        self.stdout.write(f"tag                 : {tag}")
        self.stdout.write(f"customers_created   : {counts['customers_created']}")
        self.stdout.write(f"printing_orders     : {counts['printing_orders_created']}")
        self.stdout.write(f"printing_jobs       : {counts['printing_jobs_created']}")
        self.stdout.write(f"sales_items         : {counts['sales_items_created']}")
        self.stdout.write("=" * 72)
        self.stdout.write(self.style.SUCCESS("假数据生成完成"))

    def _display_user(self, user):
        employee = getattr(user, "employee", None)
        if employee:
            return employee.name
        return user.username
