"""P1-09 数据迁移：把 ParadiseUser.favorability 写到该用户主设备 UserDevice.favorability

迁移策略：
    1. 遍历所有 favorability > 0 的用户
    2. 找该用户的主设备（is_primary=True），无主设备则取最近绑定的设备
    3. 写入 UserDevice.favorability，并同步创建一条 source='data_migration' 的
       AffinityLog 作为「已迁移」幂等标记
    4. 用户没有任何设备绑定 → 跳过（数据保留在 ParadiseUser，等用户首次绑定时由
       业务层处理）

幂等性（CR-003 修正版）：
    旧实现用 `target.favorability == 10` 做幂等判断，但 10 既是初始值也是
    衰减下限常见值，重跑迁移会覆盖合法的非默认值。新实现改用
    `AffinityLog.objects.filter(device_id=target.id, source='data_migration').exists()`
    做幂等标记，重跑安全。

backward 回滚（CR-003 修正版）：
    旧实现用 `primary.favorability != 10` 反向判断，导致衰减回 10 的数据无法回滚。
    新实现读取 forward 写入的 AffinityLog metadata 反向恢复 ParadiseUser.favorability，
    回滚后删除对应的 AffinityLog 标记（避免再次 forward 时被误判为"已迁移过"）。

⚠️ 已知风险（option B 选择说明）：
    本次 P1 收尾**直接修改了 0006 迁移内容**（而非追加新迁移 0007 做补偿）。
    选择此方案的前提：
      - dev DB 已执行过一次 0006，但 migrate_count=0 / skipped_no_device=1 /
        skipped_zero=9 — 即「成功迁移条数 = 0」，等于没动数据。
      - django_migrations 表已记录 0006 完成，Django 不会再次执行此文件。
      - 若 dev DB 出现意外脏数据（例如某用户被部分迁移），需手工
        `python manage.py migrate userapp 0005 --fake` 再 `migrate userapp 0006`
        重新执行此修正版逻辑。
    生产环境（dev → prod 同步部署前）请确认 prod 还未执行 0006，避免历史不一致。

注意：
    1. 旧的 ParadiseUser.favorability 字段保留不删，由后续版本统一清理
    2. AffinitySetting 单例和默认 8 条规则、5 个等级由 management command
       (seed_affinity, P1-10) 处理，不在数据迁移中
    3. 本迁移依赖 0007_add_affinity_check_constraints 中加入的 'data_migration'
       choice — 但 SOURCE_CHOICES 是 Python 校验，迁移内 apps.get_model 返回的
       历史模型不强制 choice 校验，直接写入字符串即可。
"""

from django.db import migrations


def migrate_favorability_forward(apps, schema_editor):
    ParadiseUser = apps.get_model('userapp', 'ParadiseUser')
    UserDevice = apps.get_model('device_interaction', 'UserDevice')
    AffinityLog = apps.get_model('userapp', 'AffinityLog')

    migrated_count = 0
    skipped_no_device = 0
    skipped_zero = 0
    skipped_already_migrated = 0

    for user in ParadiseUser.objects.iterator():
        favorability = getattr(user, 'favorability', 0) or 0
        if favorability <= 0:
            skipped_zero += 1
            continue

        # 优先取主设备，没有就取最近绑定的设备
        primary = (
            UserDevice.objects
            .filter(user_id=user.id, is_primary=True)
            .order_by('-bound_at')
            .first()
        )
        target = primary or (
            UserDevice.objects
            .filter(user_id=user.id)
            .order_by('-bound_at')
            .first()
        )
        if target is None:
            skipped_no_device += 1
            continue

        # 幂等：检查是否已迁移过（CR-003 修正 — 不再用 favorability == 10 判断）
        already = AffinityLog.objects.filter(
            device_id=target.id, source='data_migration'
        ).exists()
        if already:
            skipped_already_migrated += 1
            continue

        before = target.favorability
        target.favorability = favorability
        target.save(update_fields=['favorability'])

        # 写入幂等标记 + 审计日志
        AffinityLog.objects.create(
            user_id=user.id,
            device_id=target.id,
            rule_key='',
            change_value=favorability - before,
            before_value=before,
            after_value=favorability,
            source='data_migration',
            metadata={
                'migration': '0006_migrate_favorability_to_userdevice',
                'from_user_favorability': favorability,
                'before_device_favorability': before,
            },
        )
        migrated_count += 1

    print(
        f"\n[migration 0006_migrate_favorability] forward: "
        f"成功={migrated_count}, 无设备={skipped_no_device}, "
        f"零值={skipped_zero}, 已迁移过={skipped_already_migrated}"
    )


def migrate_favorability_backward(apps, schema_editor):
    """回滚：读取 forward 写入的 AffinityLog metadata 反向恢复 ParadiseUser.favorability

    CR-003 修正：旧实现用 `primary.favorability != 10` 反向判断，会丢失衰减回 10 的数据。
    新实现遍历 source='data_migration' 的 AffinityLog 记录，按 metadata 还原原值，
    并删除标记以保证 forward / backward 可循环。
    """
    ParadiseUser = apps.get_model('userapp', 'ParadiseUser')
    AffinityLog = apps.get_model('userapp', 'AffinityLog')

    rolled_back = 0
    skipped_no_metadata = 0

    migration_logs = AffinityLog.objects.filter(source='data_migration').iterator()
    for log in migration_logs:
        metadata = log.metadata or {}
        original_user_value = metadata.get('from_user_favorability')
        if original_user_value is None:
            skipped_no_metadata += 1
            continue

        try:
            user = ParadiseUser.objects.get(pk=log.user_id)
        except ParadiseUser.DoesNotExist:
            continue

        user.favorability = original_user_value
        user.save(update_fields=['favorability'])
        rolled_back += 1

    # 删除幂等标记（允许后续 forward 再次运行）
    deleted, _ = AffinityLog.objects.filter(source='data_migration').delete()

    print(
        f"\n[migration 0006_migrate_favorability] backward: "
        f"回滚={rolled_back}, 删除标记={deleted}, 无 metadata 跳过={skipped_no_metadata}"
    )


class Migration(migrations.Migration):

    dependencies = [
        ('userapp', '0005_affinitysetting_affinitylevel_is_deleted_and_more'),
        ('device_interaction', '0003_userdevice_affinity_level_userdevice_favorability_and_more'),
    ]

    operations = [
        migrations.RunPython(
            migrate_favorability_forward,
            migrate_favorability_backward,
        ),
    ]
