#!/usr/bin/env python3
"""Synthetic invoices with known ground truth. Three layouts, three fonts,
half of them degraded like a phone scan (rotation, blur, JPEG, noise)."""
import json, random, os, math
from PIL import Image, ImageDraw, ImageFont, ImageFilter
OUT=os.environ.get("OUT","img"); N=int(os.environ.get("N","60")); rnd=random.Random(7)
FONTS={"sans":"/usr/share/fonts/truetype/dejavu/DejaVuSans.ttf","serif":"/usr/share/fonts/truetype/dejavu/DejaVuSerif.ttf",
       "mono":"/usr/share/fonts/truetype/dejavu/DejaVuSansMono.ttf","lib":"/usr/share/fonts/truetype/liberation/LiberationSans-Regular.ttf"}
VENDORS=["Northwind Traders Ltd","Aurora Studio SRL","Kestrel Components GmbH","Bluefin Logistics BV","Marlowe & Finch LLP",
 "Tessera Software AB","Copperline Tools Inc","Halcyon Media OÜ","Oakridge Supply Co","Vantage Print S.A.","Lumen Labs Pty Ltd","Sable Consulting AG"]
CUSTOMERS=["Redwood Bakery","Ionut Popescu","Greenfield Clinic","Nakamura Design","Ada Lovelace Institute","Harbor Fitness","Elena Marin","Quartz Analytics"]
ITEMS=["Consulting hours","Server hosting (monthly)","Design revision","Cable, Cat6, 3 m","Licence renewal","On-site support","Printing, 500 sheets","Shipping",
 "Steel bracket M8","Training session","Domain registration","Coffee beans 1 kg","Maintenance visit","SSD 2 TB","Translation, per page"]
CUR=["EUR","USD","GBP","RON"]
def money(x): return f"{x:,.2f}"
def make(i):
    layout=i%3; fk=rnd.choice(list(FONTS)); f=FONTS[fk]
    vendor=rnd.choice(VENDORS); cust=rnd.choice(CUSTOMERS); cur=rnd.choice(CUR)
    num=f"{rnd.choice(['INV','F','2026-','NR'])}{rnd.randint(1000,99999)}"
    y=2026; m=rnd.randint(1,9); d=rnd.randint(1,28); date=f"{y}-{m:02d}-{d:02d}"; due=f"{y}-{min(12,m+1):02d}-{d:02d}"
    n_items=rnd.randint(2,6); items=[]
    for _ in range(n_items):
        q=rnd.choice([1,1,2,3,5,10,12]); p=round(rnd.uniform(4,900),2); items.append({"description":rnd.choice(ITEMS),"quantity":q,"unit_price":p,"amount":round(q*p,2)})
    sub=round(sum(it["amount"] for it in items),2); rate=rnd.choice([0,5,9,19,20,21]); tax=round(sub*rate/100,2); total=round(sub+tax,2)
    gt={"vendor_name":vendor,"customer_name":cust,"invoice_number":num,"invoice_date":date,"due_date":due,"currency":cur,
        "subtotal":sub,"tax_rate_percent":rate,"tax_amount":tax,"total":total,"line_items":items}
    W,H=1000,1400; im=Image.new("RGB",(W,H),"white"); dr=ImageDraw.Draw(im)
    big=ImageFont.truetype(f,40); med=ImageFont.truetype(f,24); small=ImageFont.truetype(f,20)
    datefmt=rnd.choice(["iso","eu","us"])
    def fd(s):
        yy,mm,dd=s.split("-"); return s if datefmt=="iso" else (f"{dd}.{mm}.{yy}" if datefmt=="eu" else f"{mm}/{dd}/{yy}")
    if layout==0:
        dr.text((60,60),"INVOICE",font=big,fill="black"); dr.text((60,120),vendor,font=med,fill="black"); dr.text((60,150),"12 Market Street, Springfield",font=small,fill="gray")
        dr.text((620,120),f"Invoice no: {num}",font=small,fill="black"); dr.text((620,150),f"Date: {fd(date)}",font=small,fill="black"); dr.text((620,180),f"Due: {fd(due)}",font=small,fill="black")
        dr.text((60,240),"Bill to:",font=small,fill="gray"); dr.text((60,265),cust,font=med,fill="black"); top=360
    elif layout==1:
        dr.rectangle((0,0,W,110),fill=(30,60,120)); dr.text((60,35),vendor,font=big,fill="white")
        dr.text((60,140),f"Invoice {num}",font=med,fill="black"); dr.text((60,175),f"Issued {fd(date)}   Due {fd(due)}",font=small,fill="black")
        dr.text((600,140),"Customer",font=small,fill="gray"); dr.text((600,165),cust,font=med,fill="black"); top=280
    else:
        dr.text((60,60),vendor,font=med,fill="black"); dr.text((60,90),"VAT ID: RO12345678",font=small,fill="gray")
        dr.text((60,160),"TAX INVOICE",font=big,fill="black")
        dr.text((60,220),f"No. {num}",font=small,fill="black"); dr.text((300,220),f"Date {fd(date)}",font=small,fill="black"); dr.text((540,220),f"Payment due {fd(due)}",font=small,fill="black")
        dr.text((60,260),f"To: {cust}",font=small,fill="black"); top=340
    dr.line((60,top,W-60,top),fill="black",width=2)
    dr.text((60,top+10),"Description",font=small,fill="black"); dr.text((560,top+10),"Qty",font=small,fill="black"); dr.text((660,top+10),"Unit",font=small,fill="black"); dr.text((820,top+10),"Amount",font=small,fill="black")
    yv=top+50
    for it in items:
        dr.text((60,yv),it["description"],font=small,fill="black"); dr.text((560,yv),str(it["quantity"]),font=small,fill="black")
        dr.text((660,yv),money(it["unit_price"]),font=small,fill="black"); dr.text((820,yv),money(it["amount"]),font=small,fill="black"); yv+=36
    yv+=20; dr.line((560,yv,W-60,yv),fill="gray",width=1); yv+=12
    dr.text((560,yv),"Subtotal",font=small,fill="black"); dr.text((820,yv),f"{money(sub)} {cur}",font=small,fill="black"); yv+=32
    dr.text((560,yv),f"VAT {rate}%",font=small,fill="black"); dr.text((820,yv),f"{money(tax)} {cur}",font=small,fill="black"); yv+=32
    dr.text((560,yv),"TOTAL",font=med,fill="black"); dr.text((820,yv),f"{money(total)} {cur}",font=med,fill="black")
    dr.text((60,H-80),"Thank you for your business. Payment by bank transfer within 30 days.",font=small,fill="gray")
    degraded = i%2==1
    if degraded:
        im=im.rotate(rnd.uniform(-2.5,2.5),resample=Image.BICUBIC,expand=False,fillcolor="white")
        im=im.filter(ImageFilter.GaussianBlur(rnd.uniform(0.4,1.1)))
        px=im.load()
        for _ in range(4000):
            x=rnd.randrange(W); yy=rnd.randrange(H); g=rnd.randint(120,235); px[x,yy]=(g,g,g)
        im=im.convert("L").convert("RGB")
        path=f"{OUT}/inv_{i:03d}.jpg"; im.save(path,"JPEG",quality=rnd.randint(35,60))
    else:
        path=f"{OUT}/inv_{i:03d}.png"; im.save(path,"PNG")
    gt["_file"]=path; gt["_layout"]=layout; gt["_font"]=fk; gt["_degraded"]=degraded; gt["_datefmt"]=datefmt
    return gt
os.makedirs(OUT,exist_ok=True)
gts=[make(i) for i in range(N)]
json.dump(gts,open(f"{OUT}/ground_truth.json","w"),indent=1)
print("made",N,"invoices;",sum(g["_degraded"] for g in gts),"degraded")
