#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""活动录入 V2（渠道+场景）与商机审批的轻量服务端存储。

- 活动主记录仍由 销售工作台服务.py 的 _act_save 落盘/同步 CloudBase；
- 本模块负责：V2 payload → 旧活动结构映射、待审商机队列、审核状态机。
"""
import json, os, re
from datetime import datetime

ROOT = '/Users/liuxinyuan/Desktop/Hermes输出-工作类'
DATA_DIR = os.path.join(ROOT, '数据')
FILE = os.path.join(DATA_DIR, 'opportunity_approvals.json')

APPROVERS = {
    '销售拓展部': '刘欣',
    '大客户部': '刘新元',
    '生命科学-浙江': '吴云',
    '生命科学-广东': '韩远怀',
    '诊断原料销售部': '刘新元',
}
ACT_TYPES = ['线上跟进', '线下拜访', '陌拜', '协同拜访', '客户来访']
CONTENT_TYPES = ['项目需求挖掘', '测试结果跟进', '客户档案完善', '订单跟进', '其他']
CHANNEL_MAP = {'线上跟进': 'online', '线下拜访': 'offline', '陌拜': 'offline',
               '协同拜访': 'offline', '客户来访': 'offline'}
LEGACY_TYPE = {'项目需求挖掘': '项目跟进', '测试结果跟进': '项目跟进',
               '客户档案完善': '客户档案完善', '订单跟进': '采购跟进', '其他': '项目跟进'}


def _now():
    return datetime.now().strftime('%Y-%m-%d %H:%M:%S')


def _load():
    try:
        with open(FILE, encoding='utf-8') as f:
            d = json.load(f)
        if isinstance(d, dict) and isinstance(d.get('items'), list):
            return d
    except Exception:
        pass
    return {'items': []}


def _save(d):
    os.makedirs(DATA_DIR, exist_ok=True)
    tmp = FILE + '.tmp'
    with open(tmp, 'w', encoding='utf-8') as f:
        json.dump(d, f, ensure_ascii=False, indent=2)
    os.replace(tmp, FILE)


def approver_for(dept):
    return APPROVERS.get((dept or '').strip(), '刘新元')


def _authorized_for(au, item):
    if not au:
        return False
    role = (au.get('role') or '').lower()
    if role in ('admin', 'gm'):
        return True
    if (au.get('name') or '') == '刘新元':
        return True
    return (au.get('name') or '') == (item.get('approver') or '')


def list_for(au, status=''):
    """按权限返回审批队列；销售只看自己，主管看本部门，总监/管理员看全部。"""
    if not au:
        return []
    role = (au.get('role') or '').lower()
    name = (au.get('name') or '').strip()
    out = []
    for x in _load()['items']:
        if status and x.get('status') != status:
            continue
        if role in ('admin', 'gm', 'readonly') or name == '刘新元':
            out.append(x)
        elif name == (x.get('approver') or ''):
            out.append(x)
        elif (x.get('sales') or '') == name:
            out.append(x)
    out.sort(key=lambda x: x.get('created') or '', reverse=True)
    return out


def get_one(oid):
    for x in _load()['items']:
        if x.get('id') == oid:
            return x
    return None


def create_from_payload(payload, au, activity_id):
    """新商机 → 待审记录（幂等：同一 activity_id 只建一次）。"""
    if (payload.get('content_type') or '') != '项目需求挖掘':
        return None
    if (payload.get('opp_mode') or '') != 'new':
        return None
    d = _load()
    for x in d['items']:
        if x.get('activity_id') == activity_id:
            return x
    no = payload.get('new_opp') or {}
    dept = (payload.get('dept') or au.get('dept') or '').strip()
    oid = 'OPP' + datetime.now().strftime('%Y%m%d%H%M%S')
    item = {
        'id': oid,
        'activity_id': activity_id,
        'status': 'pending',
        'created': _now(),
        'created_by': au.get('name') or '',
        'sales': (payload.get('sales') or au.get('name') or '').strip(),
        'dept': dept,
        'approver': approver_for(dept),
        'director_notified': True,
        'name': (no.get('name') or '').strip(),
        'customer': (payload.get('customer') or '').strip(),
        'contact': (payload.get('contact') or '').strip(),
        'project_scene': (no.get('project_scene') or '').strip(),
        'product': (no.get('product') or '').strip(),
        'testNeed': (no.get('test_need') or '').strip(),
        'volume': (no.get('volume') or '').strip(),
        'amount': (no.get('amount') or '').strip(),
        'date': (no.get('date') or '').strip(),
        'importance': (no.get('importance') or '').strip(),
        'stage': (no.get('stage') or '').strip(),
        'category': (no.get('category') or '').strip(),
        'competitor': (no.get('competitor') or '').strip(),
        'pain': (no.get('pain') or '').strip(),
        'decision': (no.get('decision') or '').strip(),
        'decisionUrl': (no.get('decision_url') or '').strip(),
        'trial': bool(no.get('trial')),
        'desc': (no.get('desc') or '').strip(),
        'history': [{'at': _now(), 'by': au.get('name') or '', 'action': 'submit', 'note': '销售提交'}],
    }
    d['items'].append(item)
    _save(d)
    return item


def review(au, oid, action, reason=''):
    """审核：approve/reject/veto；服务端判权，不靠前端。"""
    if not au:
        return False, '未登录', None
    d = _load()
    item = None
    for x in d['items']:
        if x.get('id') == oid:
            item = x
            break
    if not item:
        return False, '商机不存在', None
    role = (au.get('role') or '').lower()
    is_director = role in ('admin', 'gm') or (au.get('name') or '') == '刘新元'
    is_approver = (au.get('name') or '') == (item.get('approver') or '')
    if action == 'veto':
        if not is_director:
            return False, '仅刘新元/管理员可一票否决', None
    else:
        if not (is_approver or is_director):
            return False, '无权审核该商机', None
    if action == 'approve':
        if item['status'] not in ('pending', 'supplement'):
            return False, '当前状态不可通过', None
        item['status'] = 'approved'
        item['approved_by'] = au.get('name') or ''
        item['approved_at'] = _now()
    elif action == 'reject':
        if item['status'] not in ('pending', 'supplement'):
            return False, '当前状态不可驳回', None
        item['status'] = 'supplement'
        item['reject_reason'] = reason or ''
        item['supplement_due'] = datetime.now().strftime('%Y-%m-%d')
    elif action == 'veto':
        if item['status'] == 'vetoed':
            return False, '已否决', None
        item['status'] = 'vetoed'
        item['director_note'] = reason or ''
        item['vetoed_at'] = _now()
    else:
        return False, '未知审核动作', None
    item.setdefault('history', []).append(
        {'at': _now(), 'by': au.get('name') or '', 'action': action, 'note': reason or ''})
    _save(d)
    return True, '', item


def build_activity(payload, au):
    """V2 payload → 旧活动结构（保留旧字段，报表不用改）。"""
    at = (payload.get('activity_type') or '').strip()
    ct = (payload.get('content_type') or '').strip()
    if at not in ACT_TYPES:
        return None, '活动类型不合法'
    if ct not in CONTENT_TYPES:
        return None, '活动内容不合法'
    customer = (payload.get('customer') or '').strip()
    contact = (payload.get('contact') or '').strip()
    if not customer:
        return None, '客户必填'
    if not contact:
        return None, '联系人必填'
    block = {'opp': '', 'opp_status': '', 'opp_url': '', 'need': '', 'params': [],
             'issue': '', 'feedback': '', 'competitors': [], 'prices': [], 'price_on': False,
             'decision': [], 'biz_desc': '', 'revenues': [], 'suppliers': [],
             'self_share': '', 'main_rival': '', 'visit_goal': '', 'visit_content': '',
             'test_order': {}, 'plan': []}
    opportunities = []
    if ct == '项目需求挖掘':
        new = payload.get('new_opp') or {}
        if (payload.get('opp_mode') or '') == 'old':
            name = (payload.get('old_opp') or '').strip()
            if not name:
                return None, '请选择老商机'
            block['opp'] = name
            opportunities = [{'name': name, 'status': '', 'url': ''}]
            block['need'] = (new.get('test_need') or '').strip()
            block['issue'] = (new.get('pain') or '').strip()
        else:
            name = (new.get('name') or '').strip()
            if not name:
                return None, '商机名称必填'
            if not (new.get('test_need') or '').strip():
                return None, '测试需求必填'
            if not (new.get('volume') or '').strip():
                return None, '预计成交量必填'
            if not (new.get('amount') or '').strip():
                return None, '预计成交金额必填'
            if not (new.get('date') or '').strip():
                return None, '预计成交时间必填'
            block['opp'] = name
            opportunities = [{'name': name, 'status': '待审', 'url': ''}]
            block['need'] = (new.get('test_need') or '').strip()
            block['issue'] = (new.get('pain') or '').strip()
    elif ct == '测试结果跟进':
        t = payload.get('test') or {}
        if not (t.get('order_no') or '').strip():
            return None, '测试单号必填'
        if not (t.get('opp_id') or '').strip():
            return None, '请关联商机'
        block['opp'] = (t.get('opp_id') or '').strip()
        opportunities = [{'name': block['opp'], 'status': '', 'url': ''}]
        items = [{'sku': (x.get('sku') or '').strip(), 'name': '', 'result': (x.get('result') or '').strip(),
                  'attachments': []} for x in (t.get('sku_rows') or []) if (x.get('sku') or '').strip()]
        if not items:
            return None, '至少一条货号结果'
        block['test_order'] = {'no': (t.get('order_no') or '').strip(), 'customer': customer,
                               'sales': payload.get('sales') or au.get('name') or '', 'items': items}
        block['feedback'] = '\n'.join('%s：%s' % (x['sku'], x.get('result') or '') for x in items)
        block['competitors'] = [{'brand': (x.get('brand') or '').strip(), 'sku': (x.get('sku') or '').strip(),
                                 'perf': (x.get('result') or '').strip(), 'feedback': ''}
                                for x in (t.get('comp_rows') or []) if (x.get('brand') or '').strip()]
        block['plan'].append({'item': '测试结果跟进：' + ((t.get('progress') or '不调整')), 'owner': au.get('name') or '',
                              'due': ''})
    elif ct == '客户档案完善':
        p = payload.get('profile') or {}
        block['decision'] = [{'name': (x.get('name') or '').strip(), 'title': (x.get('title') or '').strip(),
                              'role': (x.get('role') or '').strip(), 'power': (x.get('power') or '').strip(),
                              'attitude': (x.get('attitude') or '').strip()}
                             for x in (p.get('decision_rows') or []) if (x.get('name') or '').strip()]
        block['biz_desc'] = (p.get('biz_desc') or '').strip()
        block['revenues'] = [{'year': (x.get('year') or '').strip(), 'amt': (x.get('amt') or '').strip(),
                              'source': (x.get('source') or '').strip()} for x in (p.get('revenue_rows') or [])]
        block['suppliers'] = [{'name': (x.get('name') or '').strip(), 'project': (x.get('project') or '').strip(),
                               'amt': (x.get('amt') or '').strip(), 'pct': (x.get('pct') or '').strip()}
                              for x in (p.get('supplier_rows') or [])]
        block['self_share'] = (p.get('share') or '').strip()
        block['main_rival'] = (p.get('rival') or '').strip()
        if not (block['decision'] or block['biz_desc'] or block['revenues'] or block['suppliers'] or block['self_share']):
            return None, '客户档案至少填一项'
    elif ct == '订单跟进':
        o = payload.get('order') or {}
        if not (o.get('opp_id') or '').strip():
            return None, '请关联商机'
        if not (o.get('sku') or '').strip():
            return None, '请选择货号'
        if not (o.get('qty') or '').strip():
            return None, '数量必填'
        block['opp'] = (o.get('opp_id') or '').strip()
        opportunities = [{'name': block['opp'], 'status': '', 'url': ''}]
        block['visit_goal'] = '订单跟进：%s × %s' % ((o.get('sku') or '').strip(), (o.get('qty') or '').strip())
        block['visit_content'] = ('规格：%s；%s；竞对价格：%s；报价：%s；预计拿货：%s' % (
            o.get('spec_mode') or '', o.get('special_reason') or '', o.get('competitor_price') or '',
            o.get('quote_strategy') or '', o.get('pickup') or ''))
    else:
        block['visit_content'] = (payload.get('other') or '').strip()
        if not block['visit_content']:
            return None, '其他内容必填'
    action = payload.get('action') or {}
    atype = (action.get('type') or '').strip()
    due = (action.get('due') or action.get('data_due') or action.get('collab_time') or '').strip()
    owner = (action.get('tech') or action.get('data_assignee') or action.get('rnd') or '').strip()
    req = (action.get('requirement') or action.get('data_need') or action.get('collab_target') or action.get('other') or '').strip()
    if atype:
        block['plan'] = [{'item': atype + ('：' + req if req else ''), 'owner': owner, 'due': due}]
    if not block['plan']:
        return None, '下一步动作必填'
    ch = CHANNEL_MAP.get(at)
    typ = LEGACY_TYPE.get(ct, '项目跟进')
    rec = {
        'id': 'ACT' + datetime.now().strftime('%Y%m%d%H%M%S'),
        'created': _now(),
        'submitted_by': au.get('name') or '',
        'sales': (payload.get('sales') or au.get('name') or '').strip(),
        'type': typ,
        'subtype': ct,
        'activity_type': at,
        'content_type': ct,
        'project_scene': (new.get('project_scene') or '').strip() if ct == '项目需求挖掘' else '',
        'customer': customer,
        'contact': contact,
        'date': (payload.get('date') or '').strip() or datetime.now().strftime('%Y-%m-%d'),
        'channel': ch,
        'order_refs': [],
        'order_ref_nos': [],
        'opportunities': opportunities,
        'opp_names': [o['name'] for o in opportunities],
        'opp_count': len(opportunities),
        'blocks': [block],
        'block_count': 1,
        'order_no': (block.get('test_order') or {}).get('no', ''),
        'sku_count': len((block.get('test_order') or {}).get('items') or []),
        'att_count': 0,
        'attachments': [],
        'need': block['need'], 'params': block['params'], 'test_order': block['test_order'],
        'issue': block['issue'], 'feedback': block['feedback'], 'competitors': block['competitors'],
        'prices': [], 'price_count': 0, 'decision': block['decision'], 'biz_desc': block['biz_desc'],
        'revenues': block['revenues'], 'suppliers': block['suppliers'], 'self_share': block['self_share'],
        'main_rival': block['main_rival'], 'decision_count': len(block['decision']),
        'visit_goal': block['visit_goal'], 'visit_content': block['visit_content'],
        'plan': block['plan'],
        'v2': True,
        # 活动录入同时产生的工单线索（技术测试单等）：保留结构化字段，客户首页/工单看板直接读取，不靠猜关键词。
        'action_type': atype,
        'action_owner': owner,
        'action_assignee': (action.get('task_owner') or action.get('tech') or '').strip(),
        'action_due': due,
        'action_requirement': req,
        'action_status': (action.get('task_status') or '待承接').strip(),
    }
    return rec, ''
