"""跨俱乐部资产迁移 API（用户端 + 客服后台）。全新接口，旧逻辑零侵入。"""
from __future__ import annotations

import logging
from datetime import datetime

from django.db.models import Q
from django.utils import timezone
from django.utils.dateparse import parse_datetime
from rest_framework.parsers import JSONParser
from rest_framework.permissions import IsAuthenticated
from rest_framework.response import Response
from rest_framework.views import APIView

from backend.utils import verify_kefu_permission
from jituan.models import ClubMigrateLedger, ClubMigratePlan, ClubMigrateUserQuota
from jituan.services import club_migrate as migrate_svc
from jituan.services.admin_context import can_manage_admin_assignments
from jituan.services.club_migrate_assets import catalog_for_admin, normalize_whitelist

logger = logging.getLogger(__name__)


def _auth_group_admin(request):
    """仅集团超管（系统超管 / GROUP_OWNER / GROUP_SUPER_ADMIN）。俱乐部 000001 不可见不可调。"""
    username = (request.data.get('username') or request.data.get('phone') or '').strip()
    if not username:
        return None, None, Response({'code': 400, 'msg': '缺少username'})
    kefu, permissions = verify_kefu_permission(request, username)
    if kefu is None:
        return None, None, permissions
    if not can_manage_admin_assignments(request.user, permissions):
        return None, None, Response({'code': 403, 'msg': '仅集团超管可管理跨店资产迁移'})
    return kefu, permissions, None


def _user_club(user) -> str:
    return str(getattr(user, 'ClubID', '') or '').strip()


def _resolve_club_id(request) -> str:
    """小程序 club 优先：body.club_id → X-Club-Id → 用户 ClubID。"""
    club_id = str(request.data.get('club_id') or '').strip()
    if not club_id:
        club_id = (
            request.META.get('HTTP_X_CLUB_ID')
            or request.headers.get('X-Club-Id')
            or ''
        ).strip()
    if not club_id:
        club_id = _user_club(request.user)
    return club_id


def _no_source_plan_response(club_id: str):
    """无源店计划时给可区分文案（避免前端笼统「暂无迁移计划」）。"""
    cid = (club_id or '').strip() or '(空)'
    target = migrate_svc.find_active_plan_for_target(club_id) if club_id else None
    if target:
        msg = (
            f'本店({cid})是迁入目标店，请到源店小程序'
            f'({target.from_club_id})完成迁出'
        )
    else:
        msg = f'当前俱乐部({cid})无有效迁出计划，请确认后台源店/启用/时间窗'
    return Response({'code': 400, 'msg': msg, 'data': {'club_id': cid, 'is_target': bool(target)}})


def _user_roles(user) -> list:
    roles = []
    for role, attr in (
        ('dashou', 'DashouProfile'),
        ('shangjia', 'ShopProfile'),
        ('guanshi', 'GuanshiProfile'),
        ('zuzhang', 'ZuzhangProfile'),
        ('shenheguan', 'ShenheguanProfile'),
    ):
        try:
            getattr(user, attr)
            roles.append(role)
        except Exception:
            pass
    return roles


def _parse_dt(v):
    if not v:
        return None
    if isinstance(v, datetime):
        return v if timezone.is_aware(v) else timezone.make_aware(v)
    dt = parse_datetime(str(v).replace('Z', '+00:00'))
    if dt is None:
        return None
    if timezone.is_naive(dt):
        dt = timezone.make_aware(dt, timezone.get_current_timezone())
    return dt


# ==================== 小程序用户端 ====================

class ClubMigrateGateView(APIView):
    """POST /jituan/migrate/gate"""
    permission_classes = [IsAuthenticated]
    parser_classes = [JSONParser]

    def post(self, request):
        club_id = _resolve_club_id(request)
        data = migrate_svc.gate_for_user(
            club_id=club_id,
            user_uid=str(request.user.UserUID),
            roles=_user_roles(request.user),
        )
        data['resolved_club_id'] = club_id or ''
        return Response({'code': 0, 'msg': 'ok', 'data': data})


class ClubMigratePreviewView(APIView):
    """POST /jituan/migrate/preview"""
    permission_classes = [IsAuthenticated]
    parser_classes = [JSONParser]

    def post(self, request):
        club_id = _resolve_club_id(request)
        plan = migrate_svc.find_active_plan_for_source(club_id)
        if not plan:
            return _no_source_plan_response(club_id)
        uid = str(request.user.UserUID)
        try:
            ok, msg = migrate_svc.check_conditions(plan, uid)
            assets = migrate_svc.preview_assets(plan, uid)
        except Exception as e:
            logger.exception('migrate preview failed club=%s uid=%s: %s', club_id, uid, e)
            return Response({'code': 500, 'msg': f'预览加载失败：{e}'})
        return Response({
            'code': 0,
            'msg': 'ok',
            'data': {
                'plan': migrate_svc.serialize_plan(plan),
                'assets': assets,
                'condition_ok': ok,
                'condition_msg': msg,
                'can_migrate': migrate_svc.can_migrate_again(plan, uid),
                'success_count': migrate_svc.success_count(plan.plan_id, uid),
                'max_allowed': migrate_svc.max_allowed_success(plan, uid),
                'resolved_club_id': club_id or '',
            },
        })


class ClubMigrateSetPasswordView(APIView):
    """POST /jituan/migrate/set_password — 设密并发令牌+目标店码"""
    permission_classes = [IsAuthenticated]
    parser_classes = [JSONParser]

    def post(self, request):
        club_id = _resolve_club_id(request)
        password = str(request.data.get('password') or '').strip()
        password2 = str(request.data.get('password2') or '').strip()
        if password != password2:
            return Response({'code': 400, 'msg': '两次密码不一致'})
        plan = migrate_svc.find_active_plan_for_source(club_id)
        if not plan:
            return _no_source_plan_response(club_id)

        token_row, err = migrate_svc.set_password_and_issue(
            plan=plan,
            from_uid=str(request.user.UserUID),
            password=password,
        )
        if err:
            return Response({'code': 400, 'msg': err})

        from jituan.services.club_migrate_qr import generate_migrate_qrcode
        qr_url, qr_err = generate_migrate_qrcode(to_club_id=plan.to_club_id, token=token_row.token)
        if qr_err:
            logger.warning('migrate qr gen fail: %s', qr_err)
            # 令牌已签发，前端仍可用 scene；码失败单独提示
        else:
            token_row.qrcode_url = qr_url
            token_row.save(update_fields=['qrcode_url', 'UpdateTime'])

        data = migrate_svc.serialize_token(token_row)
        if qr_err:
            data['qr_error'] = qr_err
        return Response({'code': 0, 'msg': '已设密并发码', 'data': data})


class ClubMigrateIssueQrView(APIView):
    """POST /jituan/migrate/issue_qr — 对已签发令牌重出码"""
    permission_classes = [IsAuthenticated]
    parser_classes = [JSONParser]

    def post(self, request):
        from jituan.models import ClubMigrateToken
        from jituan.services.club_migrate_qr import generate_migrate_qrcode

        club_id = _resolve_club_id(request)
        uid = str(request.user.UserUID)
        plan = migrate_svc.find_active_plan_for_source(club_id)
        if not plan:
            return _no_source_plan_response(club_id)
        token_row = (
            ClubMigrateToken.query.filter(
                plan_id=plan.plan_id,
                from_uid=uid,
                status=ClubMigrateToken.STATUS_ISSUED,
            )
            .order_by('-id')
            .first()
        )
        if not token_row:
            return Response({'code': 400, 'msg': '请先设置转移密码'})
        qr_url, qr_err = generate_migrate_qrcode(to_club_id=plan.to_club_id, token=token_row.token)
        if qr_err:
            return Response({'code': 400, 'msg': qr_err})
        token_row.qrcode_url = qr_url
        token_row.save(update_fields=['qrcode_url', 'UpdateTime'])
        return Response({'code': 0, 'msg': 'ok', 'data': migrate_svc.serialize_token(token_row)})


class ClubMigrateConfirmView(APIView):
    """POST /jituan/migrate/confirm — 目标店确认过户"""
    permission_classes = [IsAuthenticated]
    parser_classes = [JSONParser]

    def post(self, request):
        token = str(request.data.get('token') or '').strip()
        scene = str(request.data.get('scene') or '').strip()
        if not token and scene:
            token = migrate_svc.parse_migrate_scene(scene)
        password = str(request.data.get('password') or '').strip()
        if not token:
            return Response({'code': 400, 'msg': '缺少令牌'})
        # 必须在目标店小程序确认（防止源店误点确认）
        club_id = _resolve_club_id(request)
        from jituan.models import ClubMigrateToken
        row = ClubMigrateToken.query.filter(token=token).first()
        if row and club_id and str(row.to_club_id) != club_id:
            return Response({
                'code': 400,
                'msg': f'请用目标店小程序({row.to_club_id})扫码确认，当前店={club_id}',
            })
        data, err = migrate_svc.confirm_migrate(
            token=token,
            password=password,
            to_uid=str(request.user.UserUID),
            client_meta={
                'club_id': club_id or _user_club(request.user),
                'ip': request.META.get('REMOTE_ADDR', ''),
            },
        )
        if err:
            return Response({'code': 400, 'msg': err})
        return Response({'code': 0, 'msg': '迁移成功', 'data': data})


class ClubMigrateMyRecordsView(APIView):
    """POST /jituan/migrate/my_records"""
    permission_classes = [IsAuthenticated]
    parser_classes = [JSONParser]

    def post(self, request):
        limit = request.data.get('limit') or 50
        try:
            limit = int(limit)
        except (TypeError, ValueError):
            limit = 50
        rows = migrate_svc.list_user_records(str(request.user.UserUID), limit=limit)
        return Response({'code': 0, 'msg': 'ok', 'data': {'list': rows}})


class ClubMigrateTokenInfoView(APIView):
    """POST /jituan/migrate/token_info — 目标店扫码后预览（不验密）"""
    permission_classes = [IsAuthenticated]
    parser_classes = [JSONParser]

    def post(self, request):
        from jituan.models import ClubMigrateToken

        token = str(request.data.get('token') or '').strip()
        scene = str(request.data.get('scene') or '').strip()
        if not token and scene:
            token = migrate_svc.parse_migrate_scene(scene)
        if not token:
            return Response({'code': 400, 'msg': '缺少令牌'})
        row = ClubMigrateToken.query.filter(token=token).first()
        if not row:
            return Response({'code': 400, 'msg': '令牌无效'})
        plan = ClubMigratePlan.query.filter(plan_id=row.plan_id).first()
        return Response({
            'code': 0,
            'msg': 'ok',
            'data': {
                'token': migrate_svc.serialize_token(row),
                'plan': migrate_svc.serialize_plan(plan) if plan else None,
                'can_confirm': row.status == ClubMigrateToken.STATUS_ISSUED,
            },
        })


# ==================== 客服后台 ====================

class ClubMigrateAdminView(APIView):
    """
    POST /jituan/houtai/club-migrate
    action: catalog | list | get | save | set_enabled | set_quota | ledger_list | ledger_detail
    """
    permission_classes = [IsAuthenticated]
    parser_classes = [JSONParser]

    def post(self, request):
        kefu, permissions, err = _auth_group_admin(request)
        if err:
            return err
        action = (request.data.get('action') or 'list').strip().lower()
        operator = str(getattr(request.user, 'UserUID', '') or '')

        if action == 'catalog':
            return Response({'code': 0, 'msg': 'ok', 'data': {'assets': catalog_for_admin()}})

        if action == 'list':
            qs = ClubMigratePlan.query.all().order_by('-id')
            from_club = (request.data.get('from_club_id') or '').strip()
            to_club = (request.data.get('to_club_id') or '').strip()
            if from_club:
                qs = qs.filter(from_club_id=from_club)
            if to_club:
                qs = qs.filter(to_club_id=to_club)
            return Response({
                'code': 0,
                'msg': 'ok',
                'data': {'list': [migrate_svc.serialize_plan(p) for p in qs[:200]]},
            })

        if action == 'get':
            plan_id = (request.data.get('plan_id') or '').strip()
            plan = ClubMigratePlan.query.filter(plan_id=plan_id).first()
            if not plan:
                return Response({'code': 404, 'msg': '计划不存在'})
            return Response({'code': 0, 'msg': 'ok', 'data': migrate_svc.serialize_plan(plan)})

        if action == 'save':
            return self._save_plan(request, operator)

        if action == 'set_enabled':
            plan_id = (request.data.get('plan_id') or '').strip()
            plan = ClubMigratePlan.query.filter(plan_id=plan_id).first()
            if not plan:
                return Response({'code': 404, 'msg': '计划不存在'})
            enabled = request.data.get('enabled')
            plan.enabled = bool(enabled in (True, 1, '1', 'true', 'True'))
            plan.updated_by = operator
            plan.save(update_fields=['enabled', 'updated_by', 'UpdateTime'])
            return Response({'code': 0, 'msg': '已更新', 'data': migrate_svc.serialize_plan(plan)})

        if action == 'set_quota':
            plan_id = (request.data.get('plan_id') or '').strip()
            from_uid = (request.data.get('from_uid') or '').strip()
            if not plan_id or not from_uid:
                return Response({'code': 400, 'msg': '缺少 plan_id/from_uid'})
            plan = ClubMigratePlan.query.filter(plan_id=plan_id).first()
            if not plan:
                return Response({'code': 404, 'msg': '计划不存在'})
            try:
                extra = int(request.data.get('max_extra_times') or 1)
            except (TypeError, ValueError):
                return Response({'code': 400, 'msg': 'max_extra_times 无效'})
            user_info = migrate_svc.lookup_migrate_user_brief(from_uid)
            if not user_info.get('exists'):
                return Response({'code': 404, 'msg': f'用户不存在：{from_uid}'})
            row, _ = ClubMigrateUserQuota.query.get_or_create(
                plan_id=plan_id,
                from_uid=from_uid,
                defaults={'max_extra_times': max(0, extra), 'updated_by': operator},
            )
            row.max_extra_times = max(0, extra)
            row.reason = str(request.data.get('reason') or '')[:255]
            row.updated_by = operator
            row.save(update_fields=['max_extra_times', 'reason', 'updated_by', 'UpdateTime'])
            return Response({
                'code': 0,
                'msg': '已设置二次名额',
                'data': migrate_svc.serialize_user_quota(row, plan=plan, user_info=user_info),
            })

        if action == 'list_quota':
            plan_id = (request.data.get('plan_id') or '').strip()
            if not plan_id:
                return Response({'code': 400, 'msg': '缺少 plan_id'})
            plan = ClubMigratePlan.query.filter(plan_id=plan_id).first()
            if not plan:
                return Response({'code': 404, 'msg': '计划不存在'})
            rows = list(
                ClubMigrateUserQuota.query.filter(plan_id=plan_id).order_by('-id')[:200]
            )
            return Response({
                'code': 0,
                'msg': 'ok',
                'data': {
                    'list': [
                        migrate_svc.serialize_user_quota(r, plan=plan)
                        for r in rows
                    ],
                },
            })

        if action == 'ledger_list':
            return self._ledger_list(request)

        if action == 'ledger_detail':
            try:
                lid = int(request.data.get('ledger_id') or 0)
            except (TypeError, ValueError):
                return Response({'code': 400, 'msg': 'ledger_id 无效'})
            led = ClubMigrateLedger.query.filter(id=lid).first()
            if not led:
                return Response({'code': 404, 'msg': '流水不存在'})
            return Response({'code': 0, 'msg': 'ok', 'data': migrate_svc.serialize_ledger(led)})

        return Response({'code': 400, 'msg': '无效 action'})

    def _save_plan(self, request, operator: str):
        data = request.data
        plan_id = (data.get('plan_id') or '').strip()
        from_club = (data.get('from_club_id') or '').strip()
        to_club = (data.get('to_club_id') or '').strip()
        if not from_club or not to_club:
            return Response({'code': 400, 'msg': '缺少 from_club_id/to_club_id'})
        if from_club == to_club:
            return Response({'code': 400, 'msg': '源与目标俱乐部不能相同'})
        whitelist = normalize_whitelist(data.get('asset_whitelist') or [])
        if not whitelist:
            return Response({'code': 400, 'msg': '请至少勾选一项资产白名单'})

        force_roles = data.get('force_roles') or ['dashou']
        if not isinstance(force_roles, list):
            force_roles = ['dashou']
        condition = data.get('condition_json') or {}
        if not isinstance(condition, dict):
            condition = {}

        if plan_id:
            plan = ClubMigratePlan.query.filter(plan_id=plan_id).first()
            if not plan:
                return Response({'code': 404, 'msg': '计划不存在'})
        else:
            plan = ClubMigratePlan(
                plan_id=migrate_svc.new_plan_id(),
                from_club_id=from_club,
                to_club_id=to_club,
            )

        plan.name = str(data.get('name') or '')[:64]
        plan.from_club_id = from_club
        plan.to_club_id = to_club
        if 'enabled' in data:
            plan.enabled = bool(data.get('enabled') in (True, 1, '1', 'true', 'True'))
        plan.start_at = _parse_dt(data.get('start_at'))
        plan.end_at = _parse_dt(data.get('end_at'))
        plan.asset_whitelist = whitelist
        plan.condition_json = condition
        if 'force_intercept' in data:
            plan.force_intercept = bool(data.get('force_intercept') in (True, 1, '1', 'true', 'True'))
        plan.force_roles = force_roles
        if 'default_once_per_user' in data:
            plan.default_once_per_user = bool(
                data.get('default_once_per_user') in (True, 1, '1', 'true', 'True')
            )
        plan.remark = str(data.get('remark') or '')[:255]
        plan.updated_by = operator
        plan.save()
        return Response({'code': 0, 'msg': '已保存', 'data': migrate_svc.serialize_plan(plan)})

    def _ledger_list(self, request):
        qs = ClubMigrateLedger.query.all().order_by('-id')
        plan_id = (request.data.get('plan_id') or '').strip()
        uid = (request.data.get('uid') or '').strip()
        from_club = (request.data.get('from_club_id') or '').strip()
        to_club = (request.data.get('to_club_id') or '').strip()
        if plan_id:
            qs = qs.filter(plan_id=plan_id)
        if uid:
            qs = qs.filter(Q(from_uid=uid) | Q(to_uid=uid))
        if from_club:
            qs = qs.filter(from_club_id=from_club)
        if to_club:
            qs = qs.filter(to_club_id=to_club)
        try:
            page = max(1, int(request.data.get('page') or 1))
            page_size = min(100, max(1, int(request.data.get('page_size') or 20)))
        except (TypeError, ValueError):
            page, page_size = 1, 20
        total = qs.count()
        start = (page - 1) * page_size
        rows = list(qs[start:start + page_size])
        return Response({
            'code': 0,
            'msg': 'ok',
            'data': {
                'total': total,
                'page': page,
                'page_size': page_size,
                'list': [migrate_svc.serialize_ledger(x) for x in rows],
            },
        })
