from __future__ import annotations
import argparse, csv, json
from pathlib import Path
from reportlab import rl_config
rl_config.invariant=1
from reportlab.lib import colors
from reportlab.lib.enums import TA_CENTER, TA_LEFT
from reportlab.lib.pagesizes import A4
from reportlab.lib.styles import ParagraphStyle, getSampleStyleSheet
from reportlab.lib.units import mm
from reportlab.pdfbase import pdfmetrics
from reportlab.pdfbase.ttfonts import TTFont
from reportlab.pdfgen.canvas import Canvas
from reportlab.platypus import BaseDocTemplate, Frame, PageTemplate, Paragraph, Spacer, Table, TableStyle, PageBreak, KeepTogether, Flowable
from xml.sax.saxutils import escape

ROOT=Path(__file__).resolve().parent
FONT=ROOT/'assets'/'fonts'/'NotoSansTC-Regular.ttf'
DOI='10.5281/zenodo.21904782'
DOI_URL=f'https://doi.org/{DOI}'
pdfmetrics.registerFont(TTFont('NotoSansTC',str(FONT),subfontIndex=0))

class DeterministicCanvas(Canvas):
    def __init__(self,*a,**k): k['invariant']=1; super().__init__(*a,**k)

class VectorChart(Flowable):
    def __init__(self,kind,rows,caption): Flowable.__init__(self); self.kind=kind; self.rows=rows; self.caption=caption; self.width=165*mm; self.height=78*mm
    def draw(self):
        c=self.canv; w=self.width; h=self.height; left=18*mm; bottom=14*mm; top=h-8*mm; right=w-5*mm
        c.setFillColor(colors.HexColor('#fffdf7')); c.rect(0,0,w,h,fill=1,stroke=0); c.setStrokeColor(colors.HexColor('#d6d3d1'))
        if self.kind=='revision':
            vals=[float(r['cpi_delta_pp']) for r in self.rows]+[float(r['ppi_delta_pp']) for r in self.rows]; lim=max(1,max(abs(v) for v in vals)); y=lambda v: bottom+(top-bottom)*(v+lim)/(2*lim); zero=y(0); c.line(left,zero,right,zero); group=(right-left)/len(self.rows)
            for i,r in enumerate(self.rows):
                for j,(field,color) in enumerate((('cpi_delta_pp','#0f766e'),('ppi_delta_pp','#d97706'))):
                    v=float(r[field]); yy=y(v); bw=group*.25; x=left+group*(i+.5)+(j-.5)*bw-bw/2; c.setFillColor(colors.HexColor(color)); c.rect(x,min(zero,yy),bw,max(.7,abs(yy-zero)),fill=1,stroke=0)
        else:
            fields=(('cpi_yoy','#0f766e'),('ppi_yoy','#d97706'),('ipi_usd_yoy','#2563eb'),('epi_usd_yoy','#9333ea')) if self.kind=='levels' else (('ppi_minus_cpi_pp','#d97706'),('ipi_minus_cpi_pp','#2563eb'),('epi_minus_cpi_pp','#9333ea'))
            ymin,ymax=-10,25; x=lambda i:left+(right-left)*i/(len(self.rows)-1); y=lambda v:bottom+(top-bottom)*(v-ymin)/(ymax-ymin)
            c.setStrokeColor(colors.HexColor('#57534e')); c.line(left,y(0),right,y(0))
            for field,color in fields:
                pts=[(x(i),y(float(r[field]))) for i,r in enumerate(self.rows)]; c.setStrokeColor(colors.HexColor(color)); c.setLineWidth(1.6)
                p=c.beginPath(); p.moveTo(*pts[0]); [p.lineTo(*pt) for pt in pts[1:]]; c.drawPath(p)
        c.setFont('NotoSansTC',7); c.setFillColor(colors.HexColor('#44403c')); c.drawString(left,3*mm,self.caption[:88])

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

def build(output,analysis_root=ROOT):
    content=json.loads((ROOT/'paper-content.json').read_text(encoding='utf-8')); rows=read_csv(analysis_root/'outputs'/'monthly-price-spreads.csv'); audit=read_csv(analysis_root/'outputs'/'revision-audit.csv')
    styles=getSampleStyleSheet(); body=ParagraphStyle('Body',fontName='NotoSansTC',fontSize=9.4,leading=15,textColor=colors.HexColor('#292524'),spaceAfter=7,wordWrap='CJK');
    h1=ParagraphStyle('H1',parent=body,fontSize=19,leading=28,alignment=TA_CENTER,textColor=colors.HexColor('#134e4a'),spaceAfter=10)
    h2=ParagraphStyle('H2',parent=body,fontSize=14,leading=21,textColor=colors.HexColor('#115e59'),spaceBefore=8,spaceAfter=7,keepWithNext=True)
    h3=ParagraphStyle('H3',parent=body,fontSize=11.5,leading=17,textColor=colors.HexColor('#92400e'),spaceBefore=5,spaceAfter=4,keepWithNext=True)
    callout=ParagraphStyle('Callout',parent=body,backColor=colors.HexColor('#ecfdf5'),borderColor=colors.HexColor('#0f766e'),borderWidth=.7,borderPadding=7,spaceBefore=5,spaceAfter=8)
    small=ParagraphStyle('Small',parent=body,fontSize=8,leading=12,textColor=colors.HexColor('#57534e'))
    def footer(canvas,doc):
        canvas.saveState(); canvas.setFont('NotoSansTC',7.5); canvas.setFillColor(colors.HexColor('#57534e')); canvas.drawString(20*mm,11*mm,f'SHWRP-2026-039 v1.0｜預留DOI {DOI}'); canvas.drawRightString(190*mm,11*mm,f'{doc.page}'); canvas.restoreState()
    doc=BaseDocTemplate(str(output),pagesize=A4,leftMargin=20*mm,rightMargin=20*mm,topMargin=18*mm,bottomMargin=18*mm,title=content['title'],author='周秉漢 / CHOU, BING-HAN',subject='Taiwan price index vintage audit')
    frame=Frame(doc.leftMargin,doc.bottomMargin,doc.width,doc.height,id='normal'); doc.addPageTemplates(PageTemplate(id='paper',frames=[frame],onPage=footer))
    story=[Spacer(1,12*mm),Paragraph(escape(content['title']),h1),Paragraph('Why Faster Upstream Inflation Does Not Directly Imply CPI Pass-Through',ParagraphStyle('En',parent=small,fontSize=10,leading=15,alignment=TA_CENTER)),Spacer(1,7*mm),Paragraph('周秉漢 / CHOU, BING-HAN',ParagraphStyle('Author',parent=body,alignment=TA_CENTER,fontSize=11)),Paragraph('ORCID 0009-0000-7018-5096',ParagraphStyle('Orcid',parent=small,alignment=TA_CENTER)),Spacer(1,5*mm),Paragraph('上好味研究工作論文系列｜SHWRP-2026-039｜版本1.0｜2026-08-12｜未經外部同儕審查',ParagraphStyle('Meta',parent=small,alignment=TA_CENTER)),Spacer(1,8*mm),Paragraph(f'預留DOI：{DOI}<br/>{DOI_URL}<br/>本地回填不代表Zenodo已發布；發布與網站部署僅由外層機器閘門執行。',callout),PageBreak()]
    for section in content['sections']:
        story.append(Paragraph(escape(section['title']),h2))
        for block in section['blocks']:
            t=block['type']; v=block['value']
            if t=='paragraph': story.append(Paragraph(escape(v),body))
            elif t=='callout': story.append(Paragraph(escape(v),callout))
            elif t=='subheading': story.append(Paragraph(escape(v),h3))
            elif t=='list':
                for i,item in enumerate(v['items']): story.append(Paragraph(('• ' if not v['ordered'] else f'{i+1}. ')+escape(item),ParagraphStyle('List',parent=body,leftIndent=5*mm,firstLineIndent=-3*mm)))
            elif t=='table':
                data=[[Paragraph(escape(str(cell)),small) for cell in row] for row in v]; widths=[doc.width/len(data[0])]*len(data[0]); tab=Table(data,colWidths=widths,repeatRows=1,hAlign='LEFT')
                tab.setStyle(TableStyle([('BACKGROUND',(0,0),(-1,0),colors.HexColor('#ccfbf1')),('GRID',(0,0),(-1,-1),.4,colors.HexColor('#a8a29e')),('VALIGN',(0,0),(-1,-1),'TOP'),('LEFTPADDING',(0,0),(-1,-1),4),('RIGHTPADDING',(0,0),(-1,-1),4),('TOPPADDING',(0,0),(-1,-1),4),('BOTTOMPADDING',(0,0),(-1,-1),4)])); story += [tab,Spacer(1,4*mm)]
            elif t=='figure':
                kind='levels' if 'figure-1' in v['path'] else ('gaps' if 'figure-2' in v['path'] else 'revision'); story += [KeepTogether([VectorChart(kind,audit if kind=='revision' else rows,v['caption']),Spacer(1,4*mm)])]
    story += [Paragraph('權利與引用',h2),Paragraph('建議引用：周秉漢（2026）。〈'+escape(content['title'])+'〉。《上好味研究工作論文系列》，SHWRP-2026-039，版本1.0。'+DOI_URL,body),Paragraph(f'預留DOI {DOI} 已完成本地回填；本地回填不代表Zenodo已發布。CC BY 4.0僅適用於作者有權授權的原創分析、文字、研究程式及原創圖表；第三方權利不因收錄而改變。',callout)]
    doc.build(story,canvasmaker=DeterministicCanvas)
    print(json.dumps({'status':'PASSED','pdf':str(output),'bytes':output.stat().st_size},ensure_ascii=False))
if __name__=='__main__':
    ap=argparse.ArgumentParser(); ap.add_argument('--output',type=Path,default=ROOT/'paper.pdf'); ap.add_argument('--analysis-root',type=Path,default=ROOT); a=ap.parse_args(); build(a.output.resolve(),a.analysis_root.resolve())
