#!/usr/bin/env python3
"""Build a local-only review dashboard. Historical input requires user's snapshot choice."""
import argparse,csv,json,re,hashlib
from sales_analysis import load_sales
from pathlib import Path
from datetime import datetime,timezone,timedelta,date
BASE=Path(__file__).resolve().parent
URL='https://doc.weixin.qq.com/smartsheet/s3_AHkAH3hlAHwCNUzm1hPTKQQGwNBft_a'

def iso_date(v):
    v=str(v or '').strip()
    if re.fullmatch(r'\d{13}',v):
        try:return datetime.fromtimestamp(int(v)/1000,timezone(timedelta(hours=8))).date().isoformat()
        except (ValueError,OverflowError):return ''
    if re.fullmatch(r'\d{4}-\d{2}-\d{2}',v):
        try:return date.fromisoformat(v).isoformat()
        except ValueError:return ''
    return ''

def percent(v):
    s=str('' if v is None else v).strip()
    if not s:return None
    if not re.fullmatch(r'\d+(?:\.\d+)?%?',s):return None
    n=float(s.rstrip('%'))
    return n if 0<=n<=100 else None

def project(r,i,anchor):
    plan=iso_date(r.get('预期上线日期'))
    owner=bool(str(r.get('交付负责人','')).strip())
    return dict(key=f'project-{i}',code=r.get('项目编号',''),name=r['项目名称'].strip(),stage=r.get('项目阶段',''),state=r.get('项目状态',''),progress=percent(r.get('项目完成度')),planRaw=r.get('预期上线日期',''),planDate=plan,planPast=bool(plan and plan<anchor and r.get('项目状态')!='已完成'),ownerRecorded=owner,ownerText='已登记 · 姓名待解析' if owner else '未填写',next=r.get('下一交付门槛',''),updated=iso_date(r.get('最近活动日期')),acceptance=r.get('验收状态',''),endRaw=iso_date(r.get('结束时间')) or r.get('结束时间',''),sourceRow=i)

def readcsv(p):
    with p.open(encoding='utf-8-sig',newline='') as f:return list(csv.DictReader(f))

def build(args):
    data=dict(connected=False,snapshotLabel='布局预览 · 数据待接入',notice='企业微信文档授权已过期，最新项目数据尚未接入。本页仅预览布局，数字留空；历史数据需选定快照后加载。',snapshotDate=None,sourceUrl=URL,projects=[],service=[])
    hashes={}
    if args.snapshot_dir:
        if not args.snapshot_date:raise SystemExit('--snapshot-date is required for a historical snapshot')
        anchor=date.fromisoformat(args.snapshot_date).isoformat()
        p=args.snapshot_dir/'2-项目管理总表.csv';s=args.snapshot_dir/'5-售后运维台账.csv'
        raw=readcsv(p)
        if raw and not {'项目名称','项目状态','项目阶段'}.issubset(raw[0]):raise SystemExit('Unexpected project schema')
        data['projects']=[project(r,i,anchor) for i,r in enumerate(raw,1) if r.get('项目名称','').strip()]
        if s.exists():
            data['service']=[dict(name=r['项目名称'].strip(),type=r.get('问题类型',''),started=iso_date(r.get('接报时间')),finished=iso_date(r.get('完成时间'))) for r in readcsv(s) if r.get('项目名称','').strip()]
        data.update(connected=True,snapshotDate=anchor,snapshotLabel=f'历史快照 · {anchor}',notice=f'当前为 {anchor} 归档数据，用于审看大屏，不代表今天的项目状态。企业微信文档权限过期，最新数据尚未同步；筛选只影响项目视图，售后单独统计。')
        hashes={str(x):hashlib.sha256(x.read_bytes()).hexdigest() for x in [p,s] if x.exists()}
    data['sales']=load_sales()
    data_json=json.dumps(data,ensure_ascii=False).replace('<','\\u003c').replace('>','\\u003e').replace('&','\\u0026')
    template=(BASE/'dashboard-template.html').read_text()
    assert template.count('__DATA__')==1
    args.output.mkdir(parents=True,exist_ok=True)
    target=args.output/'delivery-project-command-center.html'
    target.write_text(template.replace('__DATA__',data_json))
    (args.output/'snapshot.json').write_text(json.dumps(data,ensure_ascii=False,indent=2))
    receipt=dict(mode='historical' if args.snapshot_dir else 'layout-only',project_records=len(data['projects']),service_records=len(data['service']),snapshot_date=data['snapshotDate'],source_hashes=hashes,output_sha256=hashlib.sha256(target.read_bytes()).hexdigest())
    (args.output/'build-receipt.json').write_text(json.dumps(receipt,ensure_ascii=False,indent=2))
    print(json.dumps(receipt,ensure_ascii=False))

if __name__=='__main__':
    parser=argparse.ArgumentParser(description=__doc__)
    parser.add_argument('--snapshot-dir',type=Path)
    parser.add_argument('--snapshot-date')
    parser.add_argument('--output',type=Path,required=True)
    build(parser.parse_args())
