#!/usr/bin/env python3
"""Conservative, auditable lower bound; this is not an exhaustive productivity census."""
from pathlib import Path
import json,hashlib,csv,subprocess,re,collections
O=Path(__file__).resolve().parent;ROOT=O.parents[1]
candidates=json.loads((O/'candidate-groups.json').read_text())
exclusions={
 ('技术方案','work/2026-06-13-presales-bidding-history-ingest'):'Imported historical tender documents, not newly authored technical plans.',
 ('技术方案','work/2026-08-21-elderly-care-1000-person-decision-solution'):'A plan for generating a Word report, not the business technical solution itself.',
 ('需求分析','work/2026-06-13-presales-bidding-history-ingest'):'Imported historical requirement text, not AI requirement analysis.',
 ('原型设计','work/2026-07-30-ui-image-to-editable-prototype-assessment'):'Method/repository assessment page, not a business prototype.',
 ('交付说明','work/2026-06-13-presales-bidding-history-ingest'):'Imported third-party manual.',
 ('交付说明','work_wecom_customer_service/inbox'):'A request for a manual, not the manual.',
 ('交付说明','work_wecom_customer_service/solutions'):'A chatbot analysis record responding to a manual request, not an authored delivery manual.',
 ('交付说明','modules/product/products/smart-canteen/product-solutions/2026-07-06-ai-nutrition-solution-product-manual'):'Sales/product brochure; excluded from delivery and operating guides.',
 ('交付说明','modules/product/products/smart-canteen/product-solutions/2026-07-30-nutrition-health-external-product-manual'):'Sales/product brochure; excluded from delivery and operating guides.'
}
aliases={
 'modules/product/products/smart-canteen/requirements/2026-07-13-recharge-gift-account-mvp':'recharge-gift-account',
 'work_store/recharge-gift-account-mvp':'recharge-gift-account',
 'work_school_food_safety_regulatory_platform/10_原型':'school-food-safety-prototype',
 'work_school_food_safety_regulatory_platform/prototype':'school-food-safety-prototype',
 'product_airport_card_system_adjustment/product-design':'airport-card-adjustment',
 'work_store/2026-06-04-airport-card-system-adjustment':'airport-card-adjustment',
 'work_store/20260813-week-copy-ai-dish-import-prototype':'menu-ai-prototype',
 'VWCG-1232':'menu-ai-prototype',
 'work_sifangda/deployment-packages':'sifangda-deployment-manual',
 'work_sifangda/deployment-records':'sifangda-deployment-manual',
 'modules/product/products/smart-canteen/requirements/2026-06-15-smart-menu-android-client':'smart-menu-android',
 'work_android/ManageDishTeminal':'smart-menu-android'
}
# The school-nutrition parent and its child implementation records are counted once.
dev_aliases={'VWCG-1366':'school-nutrition-development','VWCG-1367':'school-nutrition-development','VWCG-1369':'school-nutrition-development'}
selected=[];decisions=[]
for category,groups in candidates.items():
 for group,files in groups.items():
  reason=exclusions.get((category,group))
  if reason:decisions.append({'category':category,'group':group,'decision':'exclude','reason':reason});continue
  key=dev_aliases.get(group,aliases.get(group,group)) if category=='软件开发' else aliases.get(group,group)
  decisions.append({'category':category,'group':group,'canonical_group':key,'decision':'include','reason':'Substantive recorded artifact; broad same-project/feature groups deliberately count once.'})
  for f in files:
   p=ROOT/f['path'];assert p.is_file() and p.stat().st_size>0
   selected.append({'category':category,'canonical_group':key,'source_group':group,'title':f['title'],'path':f['path'],'sha256':hashlib.sha256(p.read_bytes()).hexdigest(),'characters':f['characters']})
# Exact same document stored under different groups should never multiply the count.
for cat in candidates:
 by_hash=collections.defaultdict(list)
 for row in selected:
  if row['category']==cat:by_hash[row['sha256']].append(row)
 for rows in by_hash.values():
  keys=sorted({r['canonical_group'] for r in rows})
  if len(keys)>1:
   for r in selected:
    if r['category']==cat and r['canonical_group'] in keys:r['canonical_group']=keys[0]
counts={c:len({r['canonical_group'] for r in selected if r['category']==c}) for c in candidates}
with (O/'counted-artifacts.csv').open('w') as f:
 w=csv.DictWriter(f,fieldnames=selected[0].keys(),lineterminator='\n');w.writeheader();w.writerows(selected)
(O/'review-decisions.json').write_text(json.dumps(decisions,ensure_ascii=False,indent=2)+'\n')
sha=subprocess.check_output(['git','rev-parse','HEAD'],cwd=ROOT,text=True).strip()
report={'period_start':'2026-04-28','as_of':'2026-09-26','source_sha':sha,'counts':counts,'measure':'Distinct conservative work-package groups with retained substantive artifacts; cross-category overlap allowed. Do not add categories into a project total.','coverage':'Lower-bound inventory based on recognizable retained artifact paths; does not establish exhaustive totals or independent AI authorship. Skill/import/archive-only paths excluded; some drafts and review-stage designs included.','delivery_status':'Document/prototype existence and implementation records, not proof of customer acceptance or production release.','candidate_group_counts':{c:len(g) for c,g in candidates.items()},'retained_artifact_files':len(selected)}
(O/'metrics.json').write_text(json.dumps(report,ensure_ascii=False,indent=2)+'\n')
print(json.dumps(report,ensure_ascii=False,indent=2))
