# -*- coding: utf-8 -*-
"""灌种子数据（与 demo 逐项对齐）
数据来源：db/seed_cadres.json（pad.html CADRES）、seed_meetings/depts/docs.json（admin.html）
其余模块（PAD 设备 / 数据包 / 用户 / 日志 / 登录）直接按 admin.html 里的
PKGS / PADS / USERS / LOGS / LOGINS 原样复刻，保证客户看到的行数、文案一致。

幂等：先清空本系统演示数据再写；只动 rmsys 库。
口令：PBKDF2-HMAC-SHA256（60000 次 + 随机盐），非明文存储。
"""
import hashlib
import json
import os
import secrets
from datetime import datetime, timedelta

import pymysql

BASE = '/data/www/files/rmsys-app/backend/db'
HOST, PORT, USER, PWD = '192.168.20.66', 3306, 'tanghouhu', 'admin123#@!'

cadres = json.load(open(os.path.join(BASE, 'seed_cadres.json'), encoding='utf-8'))
meetings = json.load(open(os.path.join(BASE, 'seed_meetings.json'), encoding='utf-8'))
depts = json.load(open(os.path.join(BASE, 'seed_depts.json'), encoding='utf-8'))
docs = json.load(open(os.path.join(BASE, 'seed_docs.json'), encoding='utf-8'))

NOW = datetime(2026, 10, 12, 9, 0, 0)


def hash_pwd(pwd, salt=None, iters=60000):
    salt = salt or secrets.token_hex(16)
    h = hashlib.pbkdf2_hmac('sha256', pwd.encode('utf-8'), bytes.fromhex(salt), iters)
    return '%d$%s$%s' % (iters, salt, h.hex()), salt


def dt(s):
    return datetime.strptime(s, '%Y-%m-%d %H:%M') if s and s != '—' else None


c = pymysql.connect(host=HOST, port=PORT, user=USER, password=PWD, charset='utf8mb4',
                    database='rmsys', autocommit=False, connect_timeout=10)
cur = c.cursor()

# ---------------- 0) 清理 ----------------
for t in ['packagedevices', 'packageitems', 'datapackages', 'materials', 'appointdocs',
          'meetingcadres', 'cadrenotes', 'cadrefamilies', 'cadreresumes', 'cadrerelations',
          'cadres', 'meetings', 'pwdhistories', 'sysuserroles', 'sysusers', 'sysroles',
          'auditlogs', 'paddevices', 'dictitems', 'depts']:
    cur.execute('DELETE FROM `%s`' % t)
    cur.execute('ALTER TABLE `%s` AUTO_INCREMENT = 1' % t)
print('[0] 清理 20 张表')

# ---------------- 1) 处室 ----------------
dept_id = {}
for d in sorted(depts, key=lambda x: x.get('order', 99)):
    cur.execute('INSERT INTO depts(Code,Name,Duty,Leader,Sort,Enabled,CreatedAt) '
                'VALUES(%s,%s,%s,%s,%s,%s,%s)',
                (d['k'], d['n'], d.get('desc', ''), d.get('owner', ''), d.get('order', 0),
                 1 if d.get('enabled', True) else 0, NOW))
    dept_id[d['k']] = cur.lastrowid
print('[1] 处室 %d: %s' % (len(dept_id), ' / '.join('%s=%s' % kv for kv in dept_id.items())))

# ---------------- 2) 会议 ----------------
TYPE_MAP = {'buwu': 'buwu', 'changwei': 'changwei', 'shuji': 'shuji',
            'cwh': 'changwei', 'sjzt': 'shuji'}
STATUS_MAP = {'筹备中': 'draft', '材料已审核': 'reviewing', '待上会': 'ready',
              '已上会': 'done', '已归档': 'archived'}


def only_level(s):
    s = s or 'B级'
    i = s.find('（')
    return s[:i] if i > 0 else s[:16]


meet_id = {}
for m in meetings:
    no = m.get('no')
    session_no = int(no) if str(no).isdigit() else None
    cur.execute('INSERT INTO meetings(Type,SessionNo,Name,HeldAt,Place,Host,Secret,SecretStars,'
                'PwdLevel,Status,Remark,BatchNo,CreatedAt,UpdatedAt) '
                'VALUES(%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s)',
                (TYPE_MAP.get(m.get('type'), 'buwu'), session_no, m['name'],
                 m.get('time') or m.get('date'), m.get('place'), m.get('host'),
                 m.get('secret', '机密'), '★★', only_level(m.get('level')),
                 STATUS_MAP.get(m.get('status'), 'draft'), m.get('remark'), 'PC-2026-10', NOW, NOW))
    meet_id[m['id']] = cur.lastrowid
print('[2] 会议 %d: %s' % (len(meet_id), ' / '.join('%s→%s' % kv for kv in meet_id.items())))

# ---------------- 3) 干部 + 简历 + 家庭 + 批注 ----------------
cadre_id = {}
for cd in cadres:
    ef, ep = cd.get('ef') or {}, cd.get('ep') or {}
    cur.execute('''INSERT INTO cadres(DeptId,Name,Sex,Birth,Nation,NativePlace,BirthPlace,PartyDate,
        WorkDate,Health,TechPost,Expertise,EduFullSchool,EduFullMajor,EduFullLevel,EduFullDegree,
        EduInSchool,EduInMajor,EduInLevel,EduInDegree,CurrentPost,ProposedPost,DismissPost,
        Rewards,Exams,AppointReason,Kw,Perf,Care,Suggest,PhotoPath,CreatedAt,UpdatedAt)
        VALUES(%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s)''',
                (dept_id.get(cd['dept']), cd['name'], cd.get('sex'), cd.get('birth'),
                 cd.get('nation'), cd.get('native'), cd.get('birthplace'), cd.get('party'),
                 cd.get('work'), cd.get('health'), cd.get('tech'), cd.get('expertise'),
                 ef.get('school'), ef.get('major'), ef.get('level'), ef.get('degree'),
                 ep.get('school'), ep.get('major'), ep.get('level'), ep.get('degree'),
                 cd.get('current'), cd.get('proposed'), cd.get('dismiss') or None,
                 cd.get('rewards'), cd.get('exams'), cd.get('reason'),
                 cd.get('kw'), cd.get('perf'), cd.get('care'), cd.get('suggest'),
                 'photos/%s.jpg' % cd['id'], NOW, NOW))
    cid = cur.lastrowid
    cadre_id[cd['id']] = cid
    for i, r in enumerate(cd.get('resume') or []):
        period, content = (r[0], r[1]) if isinstance(r, list) else ('', str(r))
        cur.execute('INSERT INTO cadreresumes(CadreId,Sort,Period,Content) VALUES(%s,%s,%s,%s)',
                    (cid, i + 1, period, content))
    for i, f in enumerate(cd.get('family') or []):
        cur.execute('INSERT INTO cadrefamilies(CadreId,Sort,Relation,Name,Age,Political,WorkUnit) '
                    'VALUES(%s,%s,%s,%s,%s,%s,%s)',
                    (cid, i + 1, f.get('rel'), f.get('name'), f.get('age'), f.get('pol'), f.get('org')))
    for nt in cd.get('notes') or []:
        cur.execute('INSERT INTO cadrenotes(CadreId,MeetingId,FieldName,Content,Source,Author,CreatedAt) '
                    'VALUES(%s,%s,%s,%s,%s,%s,%s)',
                    (cid, meet_id.get(1), nt.get('field'), nt.get('text', ''), 'import',
                     nt.get('by', '系统导入'), NOW))
print('[3] 干部 %d / 简历 %d / 家庭 %d / 批注 %d'
      % (len(cadre_id), sum(len(x.get('resume') or []) for x in cadres),
         sum(len(x.get('family') or []) for x in cadres),
         sum(len(x.get('notes') or []) for x in cadres)))

# ---------------- 4) 上会名单 ----------------
ISSUE = ['议题一', '议题二', '议题三']
n = 0
for mk, mid in meet_id.items():
    for cd in cadres:
        cur.execute('INSERT INTO meetingcadres(MeetingId,CadreId,IssueNo,SortNo,Result,Vote,Remark) '
                    'VALUES(%s,%s,%s,%s,%s,%s,%s)',
                    (mid, cadre_id[cd['id']], ISSUE[(cd['seq'] - 1) // 4 % 3], cd['seq'],
                     'pending', None, None))
        n += 1
print('[4] 上会名单 %d（%d 场 × %d 人）' % (n, len(meet_id), len(cadres)))

# ---------------- 5) 材料 ----------------
MAT = [('t30', '任免审批表', 4), ('exam', '考察材料', 3), ('report', '汇报材料', 2),
       ('note', '说明材料', 1), ('att', '干部档案审核表', 1)]
MISSING = {('郑海燕', 'exam'), ('刘志强', 'report')}
n = 0
for mk, mid in meet_id.items():
    for cd in cadres:
        for i, (tp, tname, pages) in enumerate(MAT):
            miss = (cd['name'], tp) in MISSING
            cur.execute('''INSERT INTO materials(MeetingId,DeptId,CadreId,Type,Title,FileName,FilePath,
                FileSize,Pages,Version,UploadedBy,UploadedAt,Status,Seq,CreatedAt)
                VALUES(%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s)''',
                        (mid, dept_id.get(cd['dept']), cadre_id[cd['id']], tp,
                         '%s_%s' % (cd['name'], tname),
                         None if miss else '%s_%s.docx' % (cd['name'], tname),
                         None if miss else 'uploads/%s/%s.docx' % (cd['id'], tp),
                         0 if miss else 1024 * (120 + cd['id'] * 7), 0 if miss else pages,
                         'v1', '李科员', NOW + timedelta(minutes=cd['seq'] * 6),
                         'missing' if miss else 'passed', i + 1, NOW))
            n += 1
print('[5] 材料 %d（2 份故意缺件）' % n)

# ---------------- 6) 任免文件（按处室各一份） ----------------
for d in docs:
    cur.execute('''INSERT INTO appointdocs(MeetingId,DeptId,DocNo,Title,Persons,Pages,FileSize,
        FilePath,Version,UploadedBy,UploadedAt,Status,IsDeleted,CreatedAt)
        VALUES(%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,0,%s)''',
                (meet_id.get(d.get('meeting', 1)), dept_id.get(d['dept']), d.get('no'),
                 d.get('title'), d.get('persons', 0), d.get('pages', 0), 1024 * 286,
                 'appointdocs/%s.docx' % d['dept'], d.get('ver', 'v1'), d.get('by'),
                 dt(d.get('at')), d.get('status', 'pending'), NOW))
print('[6] 任免文件 %d' % len(docs))

# ---------------- 7) PAD 设备（与 demo PADS 8 行一致） ----------------
# 状态口径：Status=1 且有 LastSeenAt → 在线；Status=1 无 LastSeenAt → 未加载；Status=0 → 在库
PADS = [
    ('PAD-01', '部长',     1, '2026-10-12 09:12'),
    ('PAD-02', '副部长',   1, '2026-10-12 09:11'),
    ('PAD-03', '副部长',   1, '2026-10-12 09:10'),
    ('PAD-04', '干部科科长', 1, '2026-10-12 09:09'),
    ('PAD-05', '干部一处',  1, '2026-10-12 09:08'),
    ('PAD-06', '干部二处',  1, '2026-10-12 09:07'),
    ('PAD-07', '人事处',    1, None),
    ('PAD-08', '备用',      0, '2026-10-09 17:30'),
]
pad_ids = {}
for no, owner, st, last in PADS:
    cur.execute('INSERT INTO paddevices(PadNo,Model,OwnerDeptId,OwnerLabel,Status,LastSeenAt,CreatedAt) '
                'VALUES(%s,%s,%s,%s,%s,%s,%s)',
                (no, '华为 MatePad 11', dept_id.get(owner), owner, st, dt(last), NOW))
    pad_ids[no] = cur.lastrowid
print('[7] PAD 设备 %d 台（在线6 / 未加载1 / 在库1）' % len(pad_ids))

# ---------------- 8) 数据包（2 个，与 demo PKGS 一致） ----------------
def add_pkg(meeting, pkgno, items, size_mb, exp, status, created, bind):
    cur.execute('''INSERT INTO datapackages(MeetingId,PkgNo,Encrypt,PwdHash,ExpireAt,ClearPolicy,
        Watermark,FontLevel,AllowFontChange,SwitchesJson,Status,FileSize,FilePath,CreatedBy,CreatedAt)
        VALUES(%s,%s,%s,%s,%s,%s,%s,%s,1,%s,%s,%s,%s,%s,%s)''',
                (meeting, pkgno, 'sm4', hash_pwd('Rmsys@2026')[0], exp, 'expire',
                 '中共海东市委组织部　机密★★　PAD-xx　2026-10-12', 'big',
                 json.dumps({'screenWatermark': True, 'screenshotGuard': True, 'print': False,
                             'share': False, 'copy': False, 'usb': False, 'camera': False},
                            ensure_ascii=False),
                 status, size_mb * 1024 * 1024, 'packages/%s.db' % pkgno, '王科长', dt(created)))
    pid = cur.lastrowid
    for i, tp in enumerate(['t30', 'exam', 'report', 'note', 'att']):
        cur.execute('INSERT INTO packageitems(PackageId,MaterialType,Enabled,Sort) VALUES(%s,%s,%s,%s)',
                    (pid, tp, 1 if tp in items else 0, i + 1))
    for no in bind:
        cur.execute('INSERT INTO packagedevices(PackageId,PadId) VALUES(%s,%s)', (pid, pad_ids[no]))
    return pid


# 有效期不能写死会议日期：重灌种子后包会立刻过期，PAD 一装就弹「资料已失效」。
# 改成「重灌当天 + 90 天 18:00」，任何时候重灌都能正常演示。
PKG1_EXPIRE = (datetime.now() + timedelta(days=90)).replace(hour=18, minute=0, second=0, microsecond=0)
add_pkg(meet_id.get(1), 'PK20261012-01', {'t30', 'exam'}, 218,
        PKG1_EXPIRE, 'delivered', '2026-10-10 16:20',
        ['PAD-01', 'PAD-02', 'PAD-03', 'PAD-04', 'PAD-05', 'PAD-06', 'PAD-07', 'PAD-08'])
add_pkg(meet_id.get(3), 'PK20261011-02', {'t30'}, 46,
        datetime(2026, 10, 11, 12, 0), 'destroyed', '2026-10-10 20:05',
        ['PAD-01', 'PAD-02', 'PAD-03', 'PAD-04', 'PAD-05'])
print('[8] 数据包 2 个（PK20261012-01 已下发 / PK20261011-02 已销毁）')

# ---------------- 9) 角色 / 用户 / 口令历史 ----------------
ROLES = [('admin', '系统管理员'), ('cadre_chief', '干部科科长'), ('clerk', '处室业务员'),
         ('minister', '部领导'), ('meeting_support', '会议保障'), ('auditor', '审计只读')]
role_id = {}
for i, (code, name) in enumerate(ROLES):
    cur.execute('INSERT INTO sysroles(Code,Name,Perms,Sort) VALUES(%s,%s,%s,%s)',
                (code, name, json.dumps(['*'] if code == 'admin' else []), i + 1))
    role_id[name] = cur.lastrowid

USERS = [
    ('wangkc', '王科长', '干部科科长', 'yichu', '全部处室', '启用', '2026-10-13 08:42'),
    ('lisp', '李科员', '处室业务员', 'yichu', '本处室', '启用', '2026-10-10 15:41'),
    ('limkz', '李科长', '干部科科长', 'erchu', '本处室', '启用', '2026-10-12 09:20'),
    ('chenkz', '陈科长', '干部科科长', 'rsc', '本处室', '启用', '2026-10-11 17:05'),
    ('bubz', '部领导', '部领导', 'all', '全部处室', '启用', '2026-10-12 08:55'),
    ('huiwu', '会务保障', '会议保障', 'all', '全部处室', '启用', '2026-10-12 07:30'),
    ('audit01', '审计员', '审计只读', 'all', '全部处室（只读）', '启用', '2026-10-09 16:30'),
    ('admin', '系统管理员', '系统管理员', 'all', '全部处室', '启用', '2026-10-13 07:58'),
    ('lisisi', '李思思', '处室业务员', 'erchu', '本处室', '停用', '2026-09-28 10:12'),
]
for acc, rn, role, dk, scope, st, last in USERS:
    h, salt = hash_pwd('Rmsys@2026')
    cur.execute('''INSERT INTO sysusers(Account,RealName,DeptId,Phone,DataScope,PwdHash,PwdSalt,
        PwdUpdatedAt,LastLoginAt,FailCount,Enabled,CreatedAt)
        VALUES(%s,%s,%s,%s,%s,%s,%s,%s,%s,0,%s,%s)''',
                (acc, rn, dept_id.get(dk), '1380000%04d' % (len(acc) + abs(hash(acc)) % 9000),
                 scope, h, salt, NOW, dt(last), 1 if st == '启用' else 0, NOW))
    uid = cur.lastrowid
    cur.execute('INSERT INTO sysuserroles(UserId,RoleId) VALUES(%s,%s)', (uid, role_id[role]))
    cur.execute('INSERT INTO pwdhistories(UserId,PwdHash,ChangedAt) VALUES(%s,%s,%s)', (uid, h, NOW))
print('[9] 角色 %d / 用户 %d（9 个账号，含 1 个停用）' % (len(role_id), len(USERS)))

# ---------------- 10) 登录记录（10 条）+ 操作日志（6 条） ----------------
LOGINS = [
    ('2026-10-13 08:42:11', '王科长', '192.168.99.51', 'PC-GY-021', 1, ''),
    ('2026-10-13 07:58:03', '系统管理员', '192.168.99.10', 'PC-GY-001', 1, ''),
    ('2026-10-12 09:20:44', '李科长', '192.168.99.52', 'PC-GY-052', 1, ''),
    ('2026-10-12 08:55:12', '部领导', '192.168.99.20', 'PC-GY-020', 1, ''),
    ('2026-10-12 07:30:26', '会务保障', '192.168.99.60', 'PC-GY-060', 1, ''),
    ('2026-10-11 17:05:38', '陈科长', '192.168.99.53', 'PC-GY-053', 1, ''),
    ('2026-10-10 15:41:07', '李科员', '192.168.99.35', 'PC-GY-035', 1, ''),
    ('2026-10-09 16:30:20', '审计员', '192.168.99.77', 'PC-GY-077', 1, ''),
    ('2026-10-09 14:12:55', '李思思', '192.168.99.36', 'PC-GY-036', 0, '账号已停用'),
    ('2026-10-08 11:03:41', '李科员', '192.168.99.35', 'PC-GY-035', 0, '口令错误 3 次已锁定'),
]
for t, nm, ip, dev, ok, why in LOGINS:
    cur.execute('''INSERT INTO auditlogs(At,UserId,UserName,Role,Action,Target,Detail,Ip,Device,Success)
        VALUES(%s,1,%s,'',%s,%s,%s,%s,%s,%s)''',
                (datetime.strptime(t, '%Y-%m-%d %H:%M:%S'), nm, 'login',
                 '登录成功' if ok else '登录失败', why or None, ip, dev, ok))

OPS = [
    ('2026-10-10 16:20:31', '王科长', '干部科科长', '生成数据包', 'PK20261012-01', 'PC-GY-021'),
    ('2026-10-10 15:58:12', '王科长', '干部科科长', '批量上传个人材料', '第192次部务会 / 干部一处', 'PC-GY-021'),
    ('2026-10-10 15:41:07', '李科员', '处室业务员', '导入任免表', '第192次部务会 / 干部一处', 'PC-GY-035'),
    ('2026-10-10 11:22:44', '王科长', '干部科科长', '新建会议', '中共海东市委第192次部务会', 'PC-GY-021'),
    ('2026-10-10 09:15:03', '李科员', '处室业务员', '删除任免表', '第192次部务会 / 张某某', 'PC-GY-035'),
    ('2026-10-09 17:30:20', '系统管理员', '系统管理员', '导入数据包', 'PK20261011-02', 'PC-GY-001'),
]
for t, nm, role, act, obj, dev in OPS:
    cur.execute('''INSERT INTO auditlogs(At,UserId,UserName,Role,Action,Target,Detail,Ip,Device,Success)
        VALUES(%s,1,%s,%s,%s,%s,NULL,'192.168.99.133',%s,1)''',
                (datetime.strptime(t, '%Y-%m-%d %H:%M:%S'), nm, role, act, obj, dev))
print('[10] 登录记录 %d / 操作日志 %d' % (len(LOGINS), len(OPS)))

# ---------------- 11) 字典 ----------------
DICTS = [('secret', 'jimi', '机密'), ('secret', 'mimi', '秘密'), ('secret', 'neibu', '内部'),
         ('pwdlevel', 'A', 'A级'), ('pwdlevel', 'B', 'B级'),
         ('mattype', 't30', '任免审批表'), ('mattype', 'exam', '考察材料'),
         ('mattype', 'report', '汇报材料'), ('mattype', 'note', '说明材料'), ('mattype', 'att', '附件'),
         ('matstatus', 'missing', '未上传'), ('matstatus', 'pending', '待审核'),
         ('matstatus', 'passed', '已审核'), ('matstatus', 'rejected', '退回'),
         ('result', 'pending', '待研究'), ('result', 'passed', '通过'),
         ('result', 'deferred', '暂缓'), ('result', 'rejected', '未通过'), ('result', 'withdrawn', '撤回'),
         ('fourmust', 'dangan', '干部档案必审'), ('fourmust', 'shixiang', '个人有关事项报告必核'),
         ('fourmust', 'jijian', '纪检监察机关意见必听'), ('fourmust', 'xinfang', '有关信访举报必查')]
for i, (t, code, label) in enumerate(DICTS):
    cur.execute('INSERT INTO dictitems(Type,Code,Label,Sort,Enabled) VALUES(%s,%s,%s,%s,1)',
                (t, code, label, i + 1))
print('[11] 字典 %d' % len(DICTS))

c.commit()

print('\n=== 入库汇总 ===')
for t in ['depts', 'meetings', 'cadres', 'cadreresumes', 'cadrefamilies', 'cadrenotes',
          'meetingcadres', 'materials', 'appointdocs', 'paddevices', 'datapackages',
          'packageitems', 'packagedevices', 'sysusers', 'sysroles', 'sysuserroles',
          'pwdhistories', 'dictitems', 'auditlogs']:
    cur.execute('SELECT COUNT(*) FROM `%s`' % t)
    print('  %-16s %d' % (t, cur.fetchone()[0]))

cur.execute('SELECT Name,CurrentPost FROM cadres ORDER BY Id LIMIT 3')
print('\n=== 中文抽检 ===')
for nm, post in cur.fetchall():
    print('  %s | %s' % (nm, post))
c.close()
print('SEED_DONE')
