from pathlib import Path
import re,zipfile,io,json,hashlib
from docx import Document
from docx.shared import Pt,Cm,RGBColor
from docx.oxml import OxmlElement
from docx.oxml.ns import qn
from docx.enum.text import WD_ALIGN_PARAGRAPH
from PIL import Image
ROOT=Path(__file__).resolve().parent
OUT=ROOT/'local/deliverables';OUT.mkdir(exist_ok=True)

def fontstyle(s,size,color='253448',bold=False):
 s.font.name='Arial';s.font.size=Pt(size);s.font.color.rgb=RGBColor.from_string(color);s.font.bold=bold
 rf=s.element.get_or_add_rPr().rFonts
 for k in list(rf.attrib):
  if 'Theme' in k or 'theme' in k: del rf.attrib[k]
 rf.set(qn('w:eastAsia'),'Heiti SC')
 snap=OxmlElement('w:snapToGrid');snap.set(qn('w:val'),'0');s.element.get_or_add_pPr().append(snap)

def table(doc,rows):
 rows=[r for r in rows if not all(re.fullmatch(r'[:\- ]+',x or '-') for x in r)]
 t=doc.add_table(rows=0,cols=len(rows[0]));t.autofit=False
 widths=[3.4,11.25] if len(rows[0])==2 else [3.2,2,9.45]
 for col,w in zip(t.columns,widths):col.width=Cm(w)
 for ri,row in enumerate(rows):
  cells=t.add_row().cells
  pr=t.rows[-1]._tr.get_or_add_trPr();pr.append(OxmlElement('w:cantSplit'))
  if ri==0:pr.append(OxmlElement('w:tblHeader'))
  for ci,txt in enumerate(row):
   c=cells[ci];c.width=Cm(widths[ci]);p=c.paragraphs[0];p.paragraph_format.space_after=Pt(3);p.paragraph_format.space_before=Pt(3);p.paragraph_format.line_spacing=Pt(13)
   r=p.add_run(txt);r.font.size=Pt(9.3);r.bold=ri==0
   tcpr=c._tc.get_or_add_tcPr();shade=OxmlElement('w:shd');shade.set(qn('w:fill'),'EAF0F5' if ri==0 else ('F7F9FB' if ri%2==0 else 'FFFFFF'));tcpr.append(shade)
   mar=OxmlElement('w:tcMar')
   for edge in ['top','left','bottom','right']:
    e=OxmlElement('w:'+edge);e.set(qn('w:w'),'75');e.set(qn('w:type'),'dxa');mar.append(e)
   tcpr.append(mar)
 doc.add_paragraph().paragraph_format.space_after=Pt(1)

def build(name):
 doc=Document(ROOT/'local/template.docx')
 if doc.paragraphs and not doc.paragraphs[0].text:
  el=doc.paragraphs[0]._element;el.getparent().remove(el)
 from docx.enum.style import WD_STYLE_TYPE
 for sn in ['Title','Heading 1','Heading 2','Caption']:
  if sn not in doc.styles: doc.styles.add_style(sn,WD_STYLE_TYPE.PARAGRAPH)
 fontstyle(doc.styles['Normal'],10.5)
 doc.styles['Normal'].paragraph_format.line_spacing=Pt(15.5)
 doc.styles['Normal'].paragraph_format.space_after=Pt(6)
 for nm,sz in [('Title',23),('Heading 1',16),('Heading 2',12)]:
  fontstyle(doc.styles[nm],sz,'000000' if nm=='Title' else '243A50',True)
  pf=doc.styles[nm].paragraph_format;pf.space_before=Pt(9);pf.space_after=Pt(8);pf.keep_with_next=True;pf.line_spacing=Pt(sz*1.25)
 fontstyle(doc.styles['Caption'],8,'667483')
 doc.styles['Caption'].paragraph_format.space_after=Pt(6)
 footer=doc.sections[0].footer.paragraphs[0];footer.alignment=WD_ALIGN_PARAGRAPH.RIGHT
 r=footer.add_run('进销存管理  |  '+name+'  |  ');r.font.size=Pt(8)
 r=footer.add_run();field=OxmlElement('w:fldSimple');field.set(qn('w:instr'),'PAGE');r._r.addnext(field)
 lines=(ROOT/(name+'.md')).read_text().splitlines();i=0;imgs=0;page_next=False
 while i<len(lines):
  s=lines[i].strip()
  if not s:i+=1;continue
  if s=='<!-- page -->':page_next=True
  elif s.startswith('|'):
   rows=[]
   while i<len(lines) and lines[i].strip().startswith('|'):
    rows.append([x.strip() for x in lines[i].strip().strip('|').split('|')]);i+=1
   table(doc,rows);continue
  elif s.startswith('!['):
   m=re.match(r'!\[(.*?)\]\((.*?)\)',s);path=ROOT/m[2]
   im=Image.open(path);w,h=im.size
   # Screenshot proportions preserved; height constrained for readable page flow.
   width=min(14.65,(8.5 if "warehouse-form" in str(path) else (10.5 if "-form" in str(path) else 7.0))*w/h)
   p=doc.add_paragraph();p.alignment=WD_ALIGN_PARAGRAPH.CENTER;p.paragraph_format.keep_with_next=True;p.paragraph_format.space_after=Pt(2);p.paragraph_format.line_spacing=1.0
   p.add_run().add_picture(str(path),width=Cm(width));doc.add_paragraph(m[1],style='Caption');imgs+=1
  elif s.startswith('# '):doc.add_paragraph(s[2:],style='Title')
  elif s.startswith('## '):
   p=doc.add_paragraph(s[3:],style='Heading 1');p.paragraph_format.page_break_before=page_next;page_next=False
  elif s.startswith('### '):doc.add_paragraph(s[4:],style='Heading 2')
  else:doc.add_paragraph(s)
  i+=1
 doc.core_properties.title='智慧食堂进销存管理'+name
 doc.core_properties.author='';doc.core_properties.last_modified_by='';doc.core_properties.subject='进销存管理';doc.core_properties.comments=''
 dest=OUT/('智慧食堂进销存管理'+name+'.docx');doc.save(dest)
 # Restore unchanged template package parts byte-for-byte, preserving logo/header and opaque parts.
 buf=io.BytesIO()
 with zipfile.ZipFile(ROOT/'local/template.docx') as src,zipfile.ZipFile(dest) as cur,zipfile.ZipFile(buf,'w',zipfile.ZIP_DEFLATED) as final:
  editable={'word/document.xml','word/_rels/document.xml.rels','word/styles.xml','word/footer1.xml','docProps/core.xml','[Content_Types].xml'}
  for n in set(src.namelist())|set(cur.namelist()):
   data=cur.read(n) if n in editable or n not in src.namelist() else src.read(n)
   final.writestr(n,data)
 dest.write_bytes(buf.getvalue())
 print(json.dumps({'name':name,'file':str(dest),'images':imgs,'sections':len(doc.sections)},ensure_ascii=False))
for n in ['功能介绍','用户操作手册']:build(n)
