"""投诉工单 API：用户提交/列表/升级 + 图片上传 + 客服处理。"""
import logging
import uuid

from django.utils import timezone
from rest_framework.parsers import FormParser, JSONParser, MultiPartParser
from rest_framework.permissions import IsAuthenticated
from rest_framework.response import Response
from rest_framework.views import APIView
from rest_framework import status

from gongdan.constants import (
    MAX_IMAGES, OSS_PREFIX, PERM_VIEW, PERM_HANDLE, TYPE_LABELS, STATUS_LABELS,
    TYPE_GUANSHI, TYPE_ZUZHANG,
)
from gongdan.models import Gongdan
from gongdan.services import (
    GongdanError,
    create_gongdan,
    escalate_gongdan,
    handle_gongdan,
    serialize_gongdan,
    user_mark_dissatisfied,
    list_gongdan_for_user,
    user_can_view_gongdan,
    user_resolve_gongdan,
    user_start_negotiate,
    user_apply_intervene,
    check_complaint_blocks_withdraw,
    search_complaint_targets,
    get_club_gongdan_config,
    set_complaint_withdraw_block,
    update_club_gongdan_config,
    peek_inviter_complaint_target,
)
from utils.oss_utils import upload_to_oss, validate_image

logger = logging.getLogger(__name__)


def _ok(data=None, message='ok', code=200):
    body = {'code': code, 'message': message, 'msg': message}
    if data is not None:
        body['data'] = data
    return Response(body, status=status.HTTP_200_OK)


def _err(message, http=400, code=None):
    c = code if code is not None else http
    return Response(
        {'code': c, 'message': message, 'msg': message},
        status=http,
    )


def _resolve_club(request):
    try:
        from jituan.services.club_write import resolve_club_id_for_write
        return resolve_club_id_for_write(request, request.user)
    except Exception:
        return getattr(request.user, 'club_id', None) or None


class GongdanUploadImageView(APIView):
    """上传证据图，返回 URL。路径：POST /gongdan/upload-image/"""
    permission_classes = [IsAuthenticated]
    parser_classes = (MultiPartParser, FormParser)

    def post(self, request):
        f = request.FILES.get('file') or request.FILES.get('image') or request.FILES.get('tupian')
        if not f:
            return _err('请选择图片')
        ok, msg = validate_image(f)
        if not ok:
            return _err(msg)
        ext = (f.name.rsplit('.', 1)[-1] if '.' in f.name else 'jpg').lower()
        if ext not in ('jpg', 'jpeg', 'png', 'gif', 'webp', 'bmp'):
            ext = 'jpg'
        uid = getattr(request.user, 'UserUID', 'anon')
        day = timezone.now().strftime('%Y%m%d')
        path = f'{OSS_PREFIX}/{day}/{uid}/{uuid.uuid4().hex}.{ext}'
        url = upload_to_oss(f, path)
        if not url:
            return _err('上传失败，请稍后重试', http=500)
        return _ok({'url': url}, '上传成功')


class GongdanCreateView(APIView):
    """创建投诉工单。POST /gongdan/create/"""
    permission_classes = [IsAuthenticated]
    parser_classes = (JSONParser, FormParser, MultiPartParser)

    def post(self, request):
        d = request.data
        images = d.get('images') or d.get('image_urls') or []
        if isinstance(images, str):
            images = [x.strip() for x in images.split(',') if x.strip()]
        try:
            gd = create_gongdan(
                yonghuid=request.user.UserUID,
                leixing=d.get('leixing'),
                shuoming=d.get('shuoming') or d.get('content') or '',
                club_id=_resolve_club(request),
                order_id=d.get('order_id') or d.get('OrderID'),
                chongzhi_id=d.get('chongzhi_id') or d.get('dingdanid') or d.get('dingdan_id'),
                fadan_id=d.get('fadan_id') or d.get('penalty_id'),
                guanshi_id=d.get('guanshi_id'),
                target_role=d.get('target_role'),
                target_user_id=d.get('target_user_id') or d.get('zuzhang_id') or d.get('guanshi_id'),
                image_urls=images,
                contact_qq=d.get('contact_qq') or d.get('qq') or '',
                contact_wechat=d.get('contact_wechat') or d.get('wechat') or d.get('wx') or '',
                contact_phone=d.get('contact_phone') or d.get('phone') or d.get('mobile') or '',
            )
            return _ok(
                serialize_gongdan(gd, include_images=True, include_shengji=True, viewer_uid=request.user.UserUID),
                '提交成功',
            )
        except GongdanError as e:
            return _err(str(e))
        except Exception as e:
            logger.error('创建工单失败: %s', e, exc_info=True)
            return _err('提交失败，请稍后重试', http=500)


class GongdanMyListView(APIView):
    """我的投诉列表。POST/GET /gongdan/my-list/"""
    permission_classes = [IsAuthenticated]

    def get(self, request):
        return self._list(request)

    def post(self, request):
        return self._list(request)

    def _list(self, request):
        scope = (
            request.data.get('scope')
            or request.data.get('tab')
            or request.query_params.get('scope')
            or request.query_params.get('tab')
            or 'created'
        )
        page = int(request.data.get('page') or request.query_params.get('page') or 1)
        page_size = min(int(request.data.get('page_size') or request.query_params.get('page_size') or 20), 50)
        club_id = _resolve_club(request)
        data = list_gongdan_for_user(
            request.user.UserUID,
            scope=scope,
            club_id=club_id,
            page=page,
            page_size=page_size,
        )
        return _ok(data)


class GongdanDetailView(APIView):
    """工单详情。POST /gongdan/detail/  {gongdan_id}"""
    permission_classes = [IsAuthenticated]

    def post(self, request):
        gid = (request.data.get('gongdan_id') or '').strip()
        if not gid:
            return _err('工单号不能为空')
        gd = Gongdan.query.filter(gongdan_id=gid).first()
        if not gd:
            return _err('工单不存在', http=404)
        # 申请人 / 被投诉人 / 牵连组长 / 客服
        if user_can_view_gongdan(gd, request.user.UserUID):
            return _ok(serialize_gongdan(
                gd, include_images=True, include_shengji=True, viewer_uid=request.user.UserUID,
            ))
        from backend.utils import verify_kefu_permission
        from gongdan.constants import PERM_VIEW, PERM_HANDLE
        kefu, perms_or_resp = verify_kefu_permission(
            request, request.data.get('username') or getattr(request.user, 'Phone', None),
        )
        if not kefu:
            return perms_or_resp
        perms = perms_or_resp if isinstance(perms_or_resp, list) else []
        if PERM_VIEW not in perms and PERM_HANDLE not in perms and '000001' not in perms:
            is_admin = getattr(request.user, 'UserType', '') == 'admin' or getattr(request.user, 'IsSuperuser', False)
            if not is_admin:
                return _err('无权查看', http=403)
        return _ok(serialize_gongdan(
            gd, include_images=True, include_shengji=True, for_admin=True,
        ))


class GongdanEscalateView(APIView):
    """继续投诉/升级。POST /gongdan/escalate/"""
    permission_classes = [IsAuthenticated]

    def post(self, request):
        d = request.data
        images = d.get('images')
        if images is not None and isinstance(images, str):
            images = [x.strip() for x in images.split(',') if x.strip()]
        try:
            gd = escalate_gongdan(
                gongdan_id=(d.get('gongdan_id') or '').strip(),
                yonghuid=request.user.UserUID,
                shuoming=d.get('shuoming') or '',
                image_urls=images,
            )
            return _ok(
                serialize_gongdan(gd, include_images=True, include_shengji=True, viewer_uid=request.user.UserUID),
                '已提交补充投诉',
            )
        except GongdanError as e:
            return _err(str(e))
        except Exception as e:
            logger.error('升级工单失败: %s', e, exc_info=True)
            return _err('提交失败', http=500)


class GongdanDissatisfiedView(APIView):
    """标记不满意。POST /gongdan/dissatisfied/"""
    permission_classes = [IsAuthenticated]

    def post(self, request):
        try:
            gd = user_mark_dissatisfied(
                gongdan_id=(request.data.get('gongdan_id') or '').strip(),
                yonghuid=request.user.UserUID,
            )
            return _ok(serialize_gongdan(gd, viewer_uid=request.user.UserUID), '已标记不满意，可继续投诉')
        except GongdanError as e:
            return _err(str(e))


class GongdanResolveView(APIView):
    """投诉人确认已解决。POST /gongdan/resolve/"""
    permission_classes = [IsAuthenticated]

    def post(self, request):
        try:
            gd = user_resolve_gongdan(
                gongdan_id=(request.data.get('gongdan_id') or '').strip(),
                yonghuid=request.user.UserUID,
            )
            return _ok(serialize_gongdan(gd, viewer_uid=request.user.UserUID), '已确认解决')
        except GongdanError as e:
            return _err(str(e))


class GongdanNegotiateView(APIView):
    """转协商中。POST /gongdan/negotiate/"""
    permission_classes = [IsAuthenticated]

    def post(self, request):
        try:
            gd = user_start_negotiate(
                gongdan_id=(request.data.get('gongdan_id') or '').strip(),
                yonghuid=request.user.UserUID,
            )
            return _ok(serialize_gongdan(gd, viewer_uid=request.user.UserUID), '已进入协商')
        except GongdanError as e:
            return _err(str(e))


class GongdanApplyInterveneView(APIView):
    """申请平台介入。POST /gongdan/apply-intervene/"""
    permission_classes = [IsAuthenticated]

    def post(self, request):
        try:
            gd = user_apply_intervene(
                gongdan_id=(request.data.get('gongdan_id') or '').strip(),
                yonghuid=request.user.UserUID,
            )
            return _ok(serialize_gongdan(gd, viewer_uid=request.user.UserUID), '已申请平台介入')
        except GongdanError as e:
            return _err(str(e))


class GongdanWithdrawCheckView(APIView):
    """提现前投诉拦截检查。POST /gongdan/withdraw-block-check/"""
    permission_classes = [IsAuthenticated]

    def post(self, request):
        ok, msg, extra = check_complaint_blocks_withdraw(request.user, club_id=_resolve_club(request))
        return _ok({
            'blocked': not ok,
            'msg': msg,
            'reason': extra.get('reason') or '',
            'tab': extra.get('tab') or '',
            'count': extra.get('count') or 0,
        })


class GongdanSearchTargetView(APIView):
    """本人邀请人（已禁止全库搜索）。POST /gongdan/search-target/"""
    permission_classes = [IsAuthenticated]

    def post(self, request):
        d = request.data
        try:
            data = search_complaint_targets(
                role=d.get('role') or d.get('target_role') or '',
                keyword=d.get('keyword') or d.get('q') or '',
                club_id=_resolve_club(request),
                page=d.get('page') or 1,
                page_size=d.get('page_size') or 20,
                yonghuid=request.user.UserUID,
            )
            return _ok(data)
        except GongdanError as e:
            return _err(str(e))


class GongdanMyInviterTargetView(APIView):
    """查询本人可投诉的邀请人。POST /gongdan/my-inviter-target/  body: {leixing:4|6}"""
    permission_classes = [IsAuthenticated]

    def post(self, request):
        try:
            leixing = int(request.data.get('leixing') or 0)
        except (TypeError, ValueError):
            return _err('投诉类型无效')
        if leixing not in (TYPE_GUANSHI, TYPE_ZUZHANG):
            return _err('仅管事/组长投诉需要邀请人')
        return _ok(peek_inviter_complaint_target(request.user.UserUID, leixing))


class GongdanMetaView(APIView):
    """类型/状态枚举 + 人工客服联系方式。GET /gongdan/meta/"""
    permission_classes = [IsAuthenticated]

    def get(self, request):
        club_id = _resolve_club(request)
        cfg = get_club_gongdan_config(club_id) if club_id else get_club_gongdan_config('')
        return _ok({
            'types': [{'value': k, 'label': v} for k, v in TYPE_LABELS.items()],
            'statuses': [{'value': k, 'label': v} for k, v in STATUS_LABELS.items()],
            'max_images': MAX_IMAGES,
            'human_kefu': {
                'wechat': cfg.get('human_kefu_wechat') or '',
                'link': cfg.get('human_kefu_link') or '',
                'tip': cfg.get('human_kefu_tip') or '',
                'available': bool(cfg.get('human_kefu_available')),
            },
            'inviter_targets': {
                'guanshi': peek_inviter_complaint_target(request.user.UserUID, TYPE_GUANSHI),
                'zuzhang': peek_inviter_complaint_target(request.user.UserUID, TYPE_ZUZHANG),
            },
        })


class ChongzhiRecordListView(APIView):
    """当前用户充值记录（Czjilu），供投诉选 ID / 充值记录页。POST /gongdan/chongzhi-list/"""
    permission_classes = [IsAuthenticated]

    def post(self, request):
        from products.models import Czjilu
        from products.czjilu_types import (
            CZJILU_LEIXING_HUIYUAN, CZJILU_LEIXING_YAJIN, CZJILU_LEIXING_JIFEN,
            CZJILU_LEIXING_SHANGJIA,
        )
        page = int(request.data.get('page') or 1)
        page_size = min(int(request.data.get('page_size') or 20), 50)
        qs = Czjilu.query.filter(yonghuid=request.user.UserUID).order_by('-CreateTime')
        leixing = request.data.get('leixing')
        if leixing not in (None, ''):
            qs = qs.filter(leixing=int(leixing))
        # 投诉选单：仅已支付；普通充值记录页不传此参数则展示全部状态
        only_paid = str(
            request.data.get('only_paid')
            or request.data.get('for_complaint')
            or ''
        ).lower() in ('1', 'true', 'yes')
        if only_paid:
            from products.czjilu_types import CZJILU_PAID_STATUS
            qs = qs.filter(zhuangtai=CZJILU_PAID_STATUS)
        total = qs.count()
        start = max(page - 1, 0) * page_size
        type_map = {
            CZJILU_LEIXING_HUIYUAN: '会员',
            CZJILU_LEIXING_YAJIN: '押金',
            CZJILU_LEIXING_JIFEN: '积分',
            CZJILU_LEIXING_SHANGJIA: '商家充值',
            5: '考核',
            6: '罚款',
            7: '转盘',
        }
        # 与 czjilu 实际状态一致：待支付/已取消/已支付等全量展示，不筛选
        status_map = {
            9: '待支付',
            3: '已支付',
            2: '已取消',
            1: '处理中',
            8: '履约失败',
            10: '业务失败',
            0: '未知',
        }
        rows = []
        for o in qs[start:start + page_size]:
            z = o.zhuangtai
            try:
                z_int = int(z) if z is not None else None
            except (TypeError, ValueError):
                z_int = None
            ct = ''
            if getattr(o, 'CreateTime', None):
                try:
                    ct = o.CreateTime.strftime('%Y-%m-%d %H:%M:%S')
                except Exception:
                    ct = o.CreateTime.isoformat()
            rows.append({
                'dingdan_id': o.dingdan_id,
                'leixing': o.leixing,
                'leixing_label': type_map.get(o.leixing, f'类型{o.leixing}'),
                'jine': str(o.jine),
                'zhuangtai': z_int if z_int is not None else z,
                'zhuangtai_label': status_map.get(z_int, f'状态{z}' if z is not None else '未知'),
                'shuoming': o.shuoming or '',
                'club_id': getattr(o, 'club_id', '') or '',
                'CreateTime': ct,
            })
        return _ok({'list': rows, 'total': total, 'page': page, 'page_size': page_size})


# ---------- 客服后台 ----------

def _require_kefu_perm(request, *need_codes):
    from backend.utils import verify_kefu_permission
    username = request.data.get('username') or request.query_params.get('username') or getattr(request.user, 'Phone', None)
    kefu, perms_or_resp = verify_kefu_permission(request, username)
    if not kefu:
        return None, None, perms_or_resp
    perms = perms_or_resp if isinstance(perms_or_resp, list) else []
    is_admin = (
        getattr(request.user, 'UserType', '') == 'admin'
        or getattr(request.user, 'IsSuperuser', False)
        or '000001' in perms
    )
    if is_admin:
        return kefu, perms, None
    if not any(c in perms for c in need_codes):
        return None, None, _err('无工单权限', http=403)
    return kefu, perms, None


class GongdanAdminListView(APIView):
    """客服工单列表。POST /gongdan/admin/list/"""
    permission_classes = [IsAuthenticated]

    def post(self, request):
        kefu, perms, err = _require_kefu_perm(request, PERM_VIEW, PERM_HANDLE)
        if err:
            return err
        page = int(request.data.get('page') or 1)
        page_size = min(int(request.data.get('page_size') or 20), 100)
        from django.db.models import Q
        qs = Gongdan.query.all().order_by('-CreateTime')
        try:
            from jituan.services.club_context import filter_club_char_field
            qs = filter_club_char_field(qs, request, field='club_id')
        except Exception:
            pass
        zhuangtai = request.data.get('zhuangtai')
        leixing = request.data.get('leixing')
        keyword = (request.data.get('keyword') or '').strip()
        only_escalated = str(request.data.get('only_escalated') or '').lower() in ('1', 'true', 'yes')
        need_handle = str(request.data.get('need_handle') or '').lower() in ('1', 'true', 'yes')
        if need_handle:
            qs = qs.filter(Q(zhuangtai=1) | Q(zhuangtai=3) | Q(zhuangtai=5))
        elif zhuangtai not in (None, ''):
            qs = qs.filter(zhuangtai=int(zhuangtai))
        if only_escalated:
            qs = qs.filter(shengji_cishu__gt=0).filter(Q(zhuangtai=1) | Q(zhuangtai=3) | Q(zhuangtai=5))
        if leixing not in (None, ''):
            qs = qs.filter(leixing=int(leixing))
        if keyword:
            qs = qs.filter(
                Q(gongdan_id__icontains=keyword)
                | Q(shenqingren_id__icontains=keyword)
                | Q(target_user_id__icontains=keyword)
                | Q(guanshi_id__icontains=keyword)
                | Q(order_id__icontains=keyword)
                | Q(chongzhi_id__icontains=keyword)
                | Q(fadan_id__icontains=keyword)
            )
        total = qs.count()
        start = max(page - 1, 0) * page_size
        rows = [
            serialize_gongdan(g, include_images=False, for_admin=True)
            for g in qs[start:start + page_size]
        ]

        # 统计基于俱乐部范围全量（不受当前筛选影响），方便客服一眼看到待办量
        stats_qs = Gongdan.query.all()
        try:
            from jituan.services.club_context import filter_club_char_field
            stats_qs = filter_club_char_field(stats_qs, request, field='club_id')
        except Exception:
            pass
        stats = {
            'total': stats_qs.count(),
            'pending': stats_qs.filter(zhuangtai=1).count(),
            'done': stats_qs.filter(zhuangtai=2).count(),
            'dissatisfied': stats_qs.filter(zhuangtai=3).count(),
            'closed': stats_qs.filter(zhuangtai=4).count(),
            'negotiating': stats_qs.filter(zhuangtai=5).count(),
            'escalated': stats_qs.filter(shengji_cishu__gt=0).filter(
                Q(zhuangtai=1) | Q(zhuangtai=3) | Q(zhuangtai=5)
            ).count(),
            'need_handle': stats_qs.filter(Q(zhuangtai=1) | Q(zhuangtai=3) | Q(zhuangtai=5)).count(),
        }
        return _ok({
            'list': rows,
            'total': total,
            'page': page,
            'page_size': page_size,
            'stats': stats,
        })


class GongdanAdminHandleView(APIView):
    """客服处理工单。POST /gongdan/admin/handle/"""
    permission_classes = [IsAuthenticated]

    def post(self, request):
        kefu, perms, err = _require_kefu_perm(request, PERM_HANDLE)
        if err:
            return err
        d = request.data
        handler_uid = getattr(request.user, 'UserUID', '') or getattr(request.user, 'Phone', '')
        handler_name = getattr(request.user, 'NickName', None) or getattr(request.user, 'Phone', '') or handler_uid
        try:
            gd = handle_gongdan(
                gongdan_id=(d.get('gongdan_id') or '').strip(),
                handler_uid=handler_uid,
                handler_name=handler_name,
                action=d.get('action') or 'done',
                chuli_jieguo=d.get('chuli_jieguo') or d.get('jieguo') or '',
                intervene_reason=d.get('intervene_reason') or '',
            )
            return _ok(
                serialize_gongdan(gd, include_images=True, include_shengji=True, for_admin=True),
                '处理成功',
            )
        except GongdanError as e:
            return _err(str(e))
        except Exception as e:
            logger.error('处理工单失败: %s', e, exc_info=True)
            return _err('处理失败', http=500)


class GongdanAdminClubConfigView(APIView):
    """
    当前俱乐部工单开关 + 人工客服联系方式。
    GET/POST /gongdan/admin/club-config/
    字段：complaint_withdraw_block / human_kefu_wechat / human_kefu_link / human_kefu_tip
    """
    permission_classes = [IsAuthenticated]

    def _club_id(self, request):
        try:
            from jituan.services.club_context import resolve_club_id_from_request
            return (resolve_club_id_from_request(request) or '').strip()
        except Exception:
            return (getattr(request.user, 'ClubID', None) or getattr(request.user, 'club_id', None) or '').strip()

    def get(self, request):
        kefu, perms, err = _require_kefu_perm(request, PERM_VIEW, PERM_HANDLE)
        if err:
            return err
        club_id = self._club_id(request)
        if not club_id:
            return _err('请先选择俱乐部')
        return _ok(get_club_gongdan_config(club_id))

    def post(self, request):
        kefu, perms, err = _require_kefu_perm(request, PERM_HANDLE)
        if err:
            return err
        club_id = self._club_id(request)
        if not club_id:
            return _err('请先选择俱乐部')
        d = request.data or {}
        patch = {}
        if 'complaint_withdraw_block' in d:
            raw = d.get('complaint_withdraw_block')
            if isinstance(raw, str):
                patch['complaint_withdraw_block'] = raw.strip().lower() in ('1', 'true', 'yes', 'on')
            else:
                patch['complaint_withdraw_block'] = bool(raw)
        for key in ('human_kefu_wechat', 'human_kefu_link', 'human_kefu_tip'):
            if key in d:
                patch[key] = d.get(key)
        if not patch:
            return _err('缺少可保存字段')
        try:
            # 兼容旧前端只传 withdraw 开关
            if list(patch.keys()) == ['complaint_withdraw_block']:
                data = set_complaint_withdraw_block(club_id, patch['complaint_withdraw_block'])
            else:
                data = update_club_gongdan_config(club_id, **patch)
            return _ok(data, '已保存')
        except GongdanError as e:
            return _err(str(e))
