# -*- coding: utf-8 -*-
"""
龙先生(lxs)审核版目录重置：清空本俱乐部三个审核类型下的专区/商品，再从星阙(xq)原样复制。

范围（必须全部满足）：
  - 目标俱乐部固定 lxs（name 含「龙先生」）
  - 源俱乐部固定 xq（name 含「星阙」）
  - 仅全局 ShangpinLeixing.shenhezhuangtai=2 的类型（通常 3 个）
  - 仅公共目录 dianpu IS NULL
  - 专区/商品均要求 shenhezhuangtai=2

步骤：
  1) 删除 lxs 下上述类型的专区+商品（只删 DB，不删 OSS，避免误伤星阙共用图）
  2) 从 xq 复制同类型专区+商品到 lxs（图片复用相对路径；销量归零）

不碰：正常商品(shenhezhuangtai=1)、其它俱乐部、店铺商品、全局类型表、订单等。

用法:
  python manage.py reset_lxs_shenhe_catalog_from_xq
  python manage.py reset_lxs_shenhe_catalog_from_xq --list
  python manage.py reset_lxs_shenhe_catalog_from_xq --apply --i-confirm=lxs-from-xq
"""
from __future__ import annotations

from django.core.management.base import BaseCommand, CommandError
from django.db import transaction

TARGET_CLUB = 'lxs'
TARGET_NAME_HINT = '龙先生'
SOURCE_CLUB = 'xq'
SOURCE_NAME_HINT = '星阙'
SHENHE = 2
CONFIRM_TOKEN = 'lxs-from-xq'


class Command(BaseCommand):
    help = '清空龙先生审核专区/商品，并从星阙复制三个审核类型目录（默认 dry-run）'

    def add_arguments(self, parser):
        parser.add_argument('--apply', action='store_true', help='真正删+写')
        parser.add_argument(
            '--i-confirm',
            type=str,
            default='',
            help=f'必须等于 {CONFIRM_TOKEN} 才允许 --apply',
        )
        parser.add_argument(
            '--list',
            action='store_true',
            help='只列出审核类型及 xq/lxs 下专区商品数量',
        )

    def handle(self, *args, **options):
        from jituan.models import Club
        from products.models import Shangpin, ShangpinLeixing, ShangpinZhuanqu

        apply = bool(options.get('apply'))
        confirm = (options.get('i_confirm') or '').strip()

        src_club = Club.query.filter(club_id=SOURCE_CLUB).first()
        if not src_club:
            raise CommandError(f'源俱乐部 {SOURCE_CLUB} 不存在')
        if SOURCE_NAME_HINT not in (src_club.name or ''):
            raise CommandError(
                f'安全拦截：{SOURCE_CLUB} 的 name={src_club.name!r} 不含「{SOURCE_NAME_HINT}」'
            )

        dst_club = Club.query.filter(club_id=TARGET_CLUB).first()
        if not dst_club:
            raise CommandError(f'目标俱乐部 {TARGET_CLUB} 不存在')
        if TARGET_NAME_HINT not in (dst_club.name or ''):
            raise CommandError(
                f'安全拦截：{TARGET_CLUB} 的 name={dst_club.name!r} 不含「{TARGET_NAME_HINT}」'
            )

        audit_types = list(
            ShangpinLeixing.query.filter(shenhezhuangtai=SHENHE).order_by('id')
        )
        if not audit_types:
            raise CommandError('没有任何审核专用商品类型(shenhezhuangtai=2)')
        if len(audit_types) != 3:
            self.stdout.write(self.style.WARNING(
                f'注意：当前审核类型数量={len(audit_types)}（预期常为 3），将按实际全部处理。'
            ))

        leixing_ids = [lx.id for lx in audit_types]

        def counts(club_id):
            z = ShangpinZhuanqu.query.filter(
                club_id=club_id,
                leixing_id__in=leixing_ids,
                shenhezhuangtai=SHENHE,
                dianpu__isnull=True,
            ).count()
            p = Shangpin.query.filter(
                club_id=club_id,
                leixing_id__in=leixing_ids,
                shenhezhuangtai=SHENHE,
                dianpu__isnull=True,
            ).count()
            return z, p

        def per_type(club_id):
            rows = []
            for lx in audit_types:
                z = ShangpinZhuanqu.query.filter(
                    club_id=club_id,
                    leixing_id=lx.id,
                    shenhezhuangtai=SHENHE,
                    dianpu__isnull=True,
                ).count()
                p = Shangpin.query.filter(
                    club_id=club_id,
                    leixing_id=lx.id,
                    shenhezhuangtai=SHENHE,
                    dianpu__isnull=True,
                ).count()
                rows.append((lx, z, p))
            return rows

        if options.get('list'):
            self.stdout.write(self.style.NOTICE('审核类型及两侧存量：'))
            for lx in audit_types:
                sz, sp = (
                    ShangpinZhuanqu.query.filter(
                        club_id=SOURCE_CLUB, leixing_id=lx.id,
                        shenhezhuangtai=SHENHE, dianpu__isnull=True,
                    ).count(),
                    Shangpin.query.filter(
                        club_id=SOURCE_CLUB, leixing_id=lx.id,
                        shenhezhuangtai=SHENHE, dianpu__isnull=True,
                    ).count(),
                )
                dz, dp = (
                    ShangpinZhuanqu.query.filter(
                        club_id=TARGET_CLUB, leixing_id=lx.id,
                        shenhezhuangtai=SHENHE, dianpu__isnull=True,
                    ).count(),
                    Shangpin.query.filter(
                        club_id=TARGET_CLUB, leixing_id=lx.id,
                        shenhezhuangtai=SHENHE, dianpu__isnull=True,
                    ).count(),
                )
                self.stdout.write(
                    f'  id={lx.id}「{lx.jieshao}」  '
                    f'xq: zones={sz} products={sp}  |  '
                    f'lxs: zones={dz} products={dp}'
                )
            return

        if apply and confirm != CONFIRM_TOKEN:
            raise CommandError(
                f'安全拦截：--apply 必须带 --i-confirm={CONFIRM_TOKEN}\n'
                f'  python manage.py reset_lxs_shenhe_catalog_from_xq '
                f'--apply --i-confirm={CONFIRM_TOKEN}'
            )

        src_z_total, src_p_total = counts(SOURCE_CLUB)
        dst_z_total, dst_p_total = counts(TARGET_CLUB)

        src_zones = list(
            ShangpinZhuanqu.query.filter(
                club_id=SOURCE_CLUB,
                leixing_id__in=leixing_ids,
                shenhezhuangtai=SHENHE,
                dianpu__isnull=True,
            ).order_by('leixing_id', 'paixu', 'id')
        )
        src_products = list(
            Shangpin.query.filter(
                club_id=SOURCE_CLUB,
                leixing_id__in=leixing_ids,
                shenhezhuangtai=SHENHE,
                dianpu__isnull=True,
            ).order_by('leixing_id', 'paixu', 'id')
        )

        # 源商品的专区必须落在本批源专区内，否则拒绝（防串数据）
        src_zone_ids = {z.id for z in src_zones}
        orphan = [p for p in src_products if p.zhuanqu_id not in src_zone_ids]
        if orphan:
            sample = ', '.join(f'{p.id}:{p.biaoqian}' for p in orphan[:5])
            raise CommandError(
                f'源商品有 {len(orphan)} 条专区不在本批审核专区内，已中止。样例: {sample}'
            )

        mode = 'APPLY' if apply else 'DRY-RUN'
        self.stdout.write(self.style.NOTICE(
            f'\n===== {mode} 龙先生审核目录 ← 星阙 =====\n'
            f'源: {SOURCE_CLUB} ({src_club.name})  '
            f'专区={src_z_total} 商品={src_p_total}\n'
            f'目标: {TARGET_CLUB} ({dst_club.name})  '
            f'将先删除 专区={dst_z_total} 商品={dst_p_total}，再复制源数据\n'
            f'审核类型数={len(audit_types)} ids={leixing_ids}\n'
            f'OSS: 不删文件；复制时复用星阙相对路径\n'
        ))

        self.stdout.write('—— 分类型存量 ——')
        for lx, sz, sp in per_type(SOURCE_CLUB):
            self.stdout.write(f'  [xq] id={lx.id}「{lx.jieshao}」 zones={sz} products={sp}')
        for lx, dz, dp in per_type(TARGET_CLUB):
            self.stdout.write(f'  [lxs] id={lx.id}「{lx.jieshao}」 zones={dz} products={dp}')

        self.stdout.write('—— 将从 xq 复制的专区 ——')
        for z in src_zones[:40]:
            self.stdout.write(
                f'  zq#{z.id} leixing={z.leixing_id} {z.mingzi!r}'
            )
        if len(src_zones) > 40:
            self.stdout.write(f'  ... 其余 {len(src_zones) - 40} 个省略')

        self.stdout.write('—— 将从 xq 复制的商品样例 ——')
        for p in src_products[:15]:
            self.stdout.write(
                f'  sp#{p.id} leixing={p.leixing_id} {p.biaoqian!r} ¥{p.jiage}'
            )
        if len(src_products) > 15:
            self.stdout.write(f'  ... 其余 {len(src_products) - 15} 个省略')

        if not apply:
            self.stdout.write(self.style.WARNING(
                '\n以上仅计划：未删库、未写库。确认无误后：\n'
                f'  python manage.py reset_lxs_shenhe_catalog_from_xq '
                f'--apply --i-confirm={CONFIRM_TOKEN}\n'
            ))
            return

        stats = {
            'deleted_products': 0,
            'deleted_zones': 0,
            'zones_created': 0,
            'products_created': 0,
        }

        with transaction.atomic():
            # 1) 只删 lxs + 审核类型 + 公共
            n_prod, _ = Shangpin.query.filter(
                club_id=TARGET_CLUB,
                leixing_id__in=leixing_ids,
                shenhezhuangtai=SHENHE,
                dianpu__isnull=True,
            ).delete()
            n_zone, _ = ShangpinZhuanqu.query.filter(
                club_id=TARGET_CLUB,
                leixing_id__in=leixing_ids,
                shenhezhuangtai=SHENHE,
                dianpu__isnull=True,
            ).delete()
            stats['deleted_products'] = n_prod
            stats['deleted_zones'] = n_zone

            # 2) 按源专区插入，建立 id 映射
            zone_id_map = {}
            for z in src_zones:
                if z.leixing_id not in leixing_ids:
                    raise CommandError(f'专区 {z.id} 类型 {z.leixing_id} 不在审核列表')
                row = ShangpinZhuanqu.query.create(
                    mingzi=z.mingzi,
                    leixing_id=z.leixing_id,
                    club_id=TARGET_CLUB,
                    tupian_url=z.tupian_url or '',
                    shenhezhuangtai=SHENHE,
                    paixu=int(z.paixu or 0),
                    shangjia_zhuangtai=bool(getattr(z, 'shangjia_zhuangtai', True)),
                    fengjin_zhuangtai=bool(getattr(z, 'fengjin_zhuangtai', True)),
                    dianpu=None,
                    dianpu_leixing=None,
                )
                zone_id_map[z.id] = row.id
                stats['zones_created'] += 1

            for p in src_products:
                if p.leixing_id not in leixing_ids:
                    raise CommandError(f'商品 {p.id} 类型 {p.leixing_id} 不在审核列表')
                dst_zq = zone_id_map.get(p.zhuanqu_id)
                if not dst_zq:
                    raise CommandError(
                        f'商品「{p.biaoqian}」id={p.id} 无法映射 zhuanqu_id={p.zhuanqu_id}'
                    )
                Shangpin.query.create(
                    biaoqian=p.biaoqian,
                    jiage=p.jiage,
                    kucun=p.kucun,
                    leixing_id=p.leixing_id,
                    zhuanqu_id=dst_zq,
                    zhenshi_xiaoliang=0,
                    duiwai_xiaoliang=0,
                    jieshao=p.jieshao,
                    xiadan_xuzhi=p.xiadan_xuzhi,
                    guize_tupian=p.guize_tupian or '',
                    tupian_url=p.tupian_url or '',
                    yaoqiuleixing=p.yaoqiuleixing,
                    huiyuan_id=p.huiyuan_id,
                    yongjin=p.yongjin,
                    kaioi_ewai_dashou_fencheng=bool(p.kaioi_ewai_dashou_fencheng),
                    ewai_dashou_fencheng=p.ewai_dashou_fencheng,
                    paixu=int(p.paixu or 0),
                    shenhezhuangtai=SHENHE,
                    shangjia_zhuangtai=bool(p.shangjia_zhuangtai),
                    fengjin_zhuangtai=bool(p.fengjin_zhuangtai),
                    shenhe_zhuangtai=bool(getattr(p, 'shenhe_zhuangtai', True)),
                    club_id=TARGET_CLUB,
                    shi_zhuanpan=False,
                    dianpu=None,
                    dianpu_leixing=None,
                )
                stats['products_created'] += 1

        self.stdout.write(self.style.SUCCESS(
            f'\n完成: {stats}\n'
            f'星阙(xq)未改动；龙先生(lxs)审核目录已与星阙对齐。'
        ))
