# -*- coding: utf-8 -*-
"""
将龙先生(lxs)审核类型「娱乐护航」下的专区+商品，复制到其它审核专用商品类型。

背景：全局通常有 3 个 shenhezhuangtai=2 的商品类型；其中「娱乐护航」已有专区/商品，
另外两个往往为空。本命令只增不减，把源类型目录镜像到目标类型。

范围：
  - 仅 club_id=lxs（Club.name 须含「龙先生」）
  - 仅 shenhezhuangtai=2（审核专用）
  - 仅公共目录 dianpu IS NULL
  - 源类型：名称精确「娱乐护航」（可用 --source-name 覆盖）
  - 目标：除源以外的全部审核类型（或 --target-id 指定）

不碰：正常类型/商品、其它俱乐部、店铺商品、源数据、全局类型表本身。

图片：复用源相对路径（同俱乐部审核素材，不另拷 OSS）。

用法:
  python manage.py copy_lxs_shenhe_huhang_to_other_types
  python manage.py copy_lxs_shenhe_huhang_to_other_types --list-leixing
  python manage.py copy_lxs_shenhe_huhang_to_other_types --apply --i-confirm-club=lxs
"""
from __future__ import annotations

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

ALLOWED_CLUB_ID = 'lxs'
NAME_HINT = '龙先生'
SHENHE = 2
DEFAULT_SOURCE_NAME = '娱乐护航'


class Command(BaseCommand):
    help = '龙先生：把审核类型「娱乐护航」的专区/商品复制到其它审核类型（默认 dry-run）'

    def add_arguments(self, parser):
        parser.add_argument('--apply', action='store_true', help='真正写库')
        parser.add_argument(
            '--i-confirm-club',
            type=str,
            default='',
            help='必须等于 lxs 才允许 --apply',
        )
        parser.add_argument(
            '--club-id',
            type=str,
            default=ALLOWED_CLUB_ID,
            help='仅允许 lxs',
        )
        parser.add_argument(
            '--source-name',
            type=str,
            default=DEFAULT_SOURCE_NAME,
            help=f'源审核类型全名精确匹配，默认「{DEFAULT_SOURCE_NAME}」',
        )
        parser.add_argument(
            '--source-id',
            type=int,
            default=0,
            help='可选：直接指定源 ShangpinLeixing.id（须为审核专用且名称校验）',
        )
        parser.add_argument(
            '--target-id',
            type=int,
            action='append',
            default=[],
            help='可选：只复制到指定审核类型 id，可多次传入；默认=除源外全部审核类型',
        )
        parser.add_argument(
            '--list-leixing',
            action='store_true',
            help='只列出审核类型及 lxs 下专区/商品数量',
        )

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

        club_id = (options.get('club_id') or '').strip()
        apply = bool(options.get('apply'))
        confirm = (options.get('i_confirm_club') or '').strip()
        source_name = (options.get('source_name') or DEFAULT_SOURCE_NAME).strip()

        if club_id != ALLOWED_CLUB_ID:
            raise CommandError(
                f'安全拦截：只允许 club_id={ALLOWED_CLUB_ID!r}，你传的是 {club_id!r}'
            )

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

        audit_types = list(
            ShangpinLeixing.query.filter(shenhezhuangtai=SHENHE).order_by('id')
        )
        if not audit_types:
            raise CommandError('库中没有任何审核专用商品类型(shenhezhuangtai=2)')

        def count_for(leixing_id):
            z = ShangpinZhuanqu.query.filter(
                club_id=ALLOWED_CLUB_ID,
                leixing_id=leixing_id,
                shenhezhuangtai=SHENHE,
                dianpu__isnull=True,
            ).count()
            p = Shangpin.query.filter(
                club_id=ALLOWED_CLUB_ID,
                leixing_id=leixing_id,
                shenhezhuangtai=SHENHE,
                dianpu__isnull=True,
            ).count()
            return z, p

        if options.get('list_leixing'):
            self.stdout.write(self.style.NOTICE(
                f'审核类型清单（club={ALLOWED_CLUB_ID} 下专区/商品数）：'
            ))
            for lx in audit_types:
                zc, pc = count_for(lx.id)
                mark = '  <<-- 源候选' if (lx.jieshao or '') == source_name else ''
                self.stdout.write(
                    f'  id={lx.id}  name={lx.jieshao!r}  zones={zc}  products={pc}{mark}'
                )
            return

        if apply and confirm != ALLOWED_CLUB_ID:
            raise CommandError(
                '安全拦截：--apply 必须带 --i-confirm-club=lxs\n'
                '  python manage.py copy_lxs_shenhe_huhang_to_other_types '
                '--apply --i-confirm-club=lxs'
            )

        # 解析源类型
        source_id = int(options.get('source_id') or 0)
        if source_id:
            source = ShangpinLeixing.query.filter(id=source_id).first()
            if not source:
                raise CommandError(f'找不到类型 id={source_id}')
            if source.shenhezhuangtai != SHENHE:
                raise CommandError(f'类型 id={source_id} 不是审核专用')
            if (source.jieshao or '') != source_name:
                raise CommandError(
                    f'安全拦截：id={source_id} 名称是「{source.jieshao}」，'
                    f'与要求的「{source_name}」不一致'
                )
        else:
            matches = [lx for lx in audit_types if (lx.jieshao or '') == source_name]
            if not matches:
                # 宽松：名称包含「娱乐护」且含「航」或等于常见别名
                soft = [
                    lx for lx in audit_types
                    if '娱乐护' in (lx.jieshao or '')
                ]
                hint = '\n'.join(
                    f'  id={lx.id} name={lx.jieshao!r}' for lx in audit_types
                )
                raise CommandError(
                    f'找不到审核类型全名「{source_name}」。当前审核类型：\n{hint}\n'
                    f'可用 --source-id=... 或 --source-name=... 指定。'
                    + (f'\n名称含「娱乐护」的有: {[s.jieshao for s in soft]}' if soft else '')
                )
            if len(matches) > 1:
                raise CommandError(
                    f'「{source_name}」匹配到多条 {[m.id for m in matches]}，请 --source-id='
                )
            source = matches[0]

        src_zones_n, src_prods_n = count_for(source.id)
        if src_zones_n == 0 and src_prods_n == 0:
            raise CommandError(
                f'源类型 id={source.id}「{source.jieshao}」在 {ALLOWED_CLUB_ID} 下'
                f'没有任何审核专区/商品，无需复制'
            )

        # 目标类型
        target_ids = [int(x) for x in (options.get('target_id') or [])]
        if target_ids:
            targets = []
            for tid in target_ids:
                lx = ShangpinLeixing.query.filter(id=tid).first()
                if not lx:
                    raise CommandError(f'目标类型 id={tid} 不存在')
                if lx.shenhezhuangtai != SHENHE:
                    raise CommandError(f'目标类型 id={tid} 不是审核专用，拒绝')
                if lx.id == source.id:
                    raise CommandError('目标不能与源相同')
                targets.append(lx)
        else:
            targets = [lx for lx in audit_types if lx.id != source.id]

        if not targets:
            raise CommandError('没有可复制的目标审核类型（除源外为空）')

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

        mode = 'APPLY' if apply else 'DRY-RUN'
        self.stdout.write(self.style.NOTICE(
            f'\n===== {mode} 复制审核目录 =====\n'
            f'club={ALLOWED_CLUB_ID} ({club.name})\n'
            f'源类型: id={source.id}「{source.jieshao}」 '
            f'专区={len(src_zones)} 商品={len(src_products)}\n'
            f'目标类型({len(targets)}): '
            + ', '.join(f'id={t.id}「{t.jieshao}」' for t in targets)
            + '\n'
        ))

        for t in targets:
            zc, pc = count_for(t.id)
            self.stdout.write(f'  目标当前存量 id={t.id}「{t.jieshao}」 zones={zc} products={pc}')

        self.stdout.write('—— 源专区 ——')
        for z in src_zones:
            self.stdout.write(f'  id={z.id}  {z.mingzi!r}  paixu={z.paixu}')
        self.stdout.write('—— 源商品样例 ——')
        for p in src_products[:8]:
            self.stdout.write(f'  id={p.id}  {p.biaoqian!r}  ¥{p.jiage}  zq={p.zhuanqu_id}')
        if len(src_products) > 8:
            self.stdout.write(f'  ... 其余 {len(src_products) - 8} 个省略')

        # 预计算每个目标的 create/skip
        preview = []
        for t in targets:
            z_create = z_skip = p_create = p_skip = 0
            for z in src_zones:
                exist_z = ShangpinZhuanqu.query.filter(
                    club_id=ALLOWED_CLUB_ID,
                    leixing_id=t.id,
                    mingzi=z.mingzi,
                    shenhezhuangtai=SHENHE,
                    dianpu__isnull=True,
                ).first()
                if exist_z:
                    z_skip += 1
                else:
                    z_create += 1
            # 商品去重按：目标类型下 同标题+同专区名（专区可能尚未建，用名推断）
            for p in src_products:
                src_z = next((x for x in src_zones if x.id == p.zhuanqu_id), None)
                zname = src_z.mingzi if src_z else None
                exist_zq = None
                if zname:
                    exist_zq = ShangpinZhuanqu.query.filter(
                        club_id=ALLOWED_CLUB_ID,
                        leixing_id=t.id,
                        mingzi=zname,
                        shenhezhuangtai=SHENHE,
                        dianpu__isnull=True,
                    ).first()
                if exist_zq:
                    exist_p = Shangpin.query.filter(
                        club_id=ALLOWED_CLUB_ID,
                        leixing_id=t.id,
                        zhuanqu_id=exist_zq.id,
                        biaoqian=p.biaoqian,
                        shenhezhuangtai=SHENHE,
                        dianpu__isnull=True,
                    ).first()
                    if exist_p:
                        p_skip += 1
                    else:
                        p_create += 1
                else:
                    p_create += 1
            preview.append((t, z_create, z_skip, p_create, p_skip))
            self.stdout.write(
                f'  计划 →「{t.jieshao}」: zones +{z_create}/skip{z_skip}, '
                f'products +{p_create}/skip{p_skip}'
            )

        if not apply:
            self.stdout.write(self.style.WARNING(
                '\n以上仅计划，未写库。确认后：\n'
                '  python manage.py copy_lxs_shenhe_huhang_to_other_types '
                '--apply --i-confirm-club=lxs\n'
            ))
            return

        stats = {
            'targets': 0,
            'zones_created': 0,
            'zones_skipped': 0,
            'products_created': 0,
            'products_skipped': 0,
            'products_skip_no_zone': 0,
        }

        with transaction.atomic():
            for t in targets:
                stats['targets'] += 1
                zone_map = {}  # src_zone_id -> dst_zone_id

                for z in src_zones:
                    exist_z = ShangpinZhuanqu.query.filter(
                        club_id=ALLOWED_CLUB_ID,
                        leixing_id=t.id,
                        mingzi=z.mingzi,
                        shenhezhuangtai=SHENHE,
                        dianpu__isnull=True,
                    ).first()
                    if exist_z:
                        zone_map[z.id] = exist_z.id
                        stats['zones_skipped'] += 1
                        continue
                    row = ShangpinZhuanqu.query.create(
                        mingzi=z.mingzi,
                        leixing_id=t.id,
                        club_id=ALLOWED_CLUB_ID,
                        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_map[z.id] = row.id
                    stats['zones_created'] += 1

                for p in src_products:
                    dst_zq = zone_map.get(p.zhuanqu_id)
                    if not dst_zq:
                        stats['products_skip_no_zone'] += 1
                        self.stderr.write(
                            f'跳过无专区映射商品: {p.biaoqian!r} src_zq={p.zhuanqu_id} →「{t.jieshao}」'
                        )
                        continue
                    exist_p = Shangpin.query.filter(
                        club_id=ALLOWED_CLUB_ID,
                        leixing_id=t.id,
                        zhuanqu_id=dst_zq,
                        biaoqian=p.biaoqian,
                        shenhezhuangtai=SHENHE,
                        dianpu__isnull=True,
                    ).first()
                    if exist_p:
                        stats['products_skipped'] += 1
                        continue
                    Shangpin.query.create(
                        biaoqian=p.biaoqian,
                        jiage=p.jiage,
                        kucun=p.kucun,
                        leixing_id=t.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=ALLOWED_CLUB_ID,
                        shi_zhuanpan=False,
                        dianpu=None,
                        dianpu_leixing=None,
                    )
                    stats['products_created'] += 1

        self.stdout.write(self.style.SUCCESS(
            f'\n完成: {stats}\n'
            f'源「{source.jieshao}」未改动；目标均为审核专用且仅 lxs。'
        ))
