#!/usr/bin/env python3
from __future__ import annotations
import base64, csv, json, os, re, zipfile, time
from datetime import date, datetime, timezone
from pathlib import Path
import requests
from PIL import Image, ImageDraw, ImageFont, ImageFilter

ROOT=Path('/root/etsy_printify_revenue_engine')
TODAY=date.today().isoformat()
BATCH=f'{TODAY}-multi-product-batch-1'
ASSET_DIR=ROOT/'assets'/'designs'/BATCH
QA_DIR=ROOT/'qa'/'visual'/BATCH
PROD_DIR=ROOT/'printify_products'/BATCH
DIG_DIR=ROOT/'digital_products'/BATCH
LISTING_DIR=ROOT/'listings'
for d in [ASSET_DIR,QA_DIR,PROD_DIR,DIG_DIR,LISTING_DIR]: d.mkdir(parents=True, exist_ok=True)

FONT_BOLD='/usr/share/fonts/truetype/dejavu/DejaVuSansCondensed-Bold.ttf'
FONT_SANS='/usr/share/fonts/truetype/dejavu/DejaVuSans.ttf'
FONT_SERIF='/usr/share/fonts/truetype/dejavu/DejaVuSerif-Bold.ttf'
FONT_SERIF_REG='/usr/share/fonts/truetype/dejavu/DejaVuSerif.ttf'
CREAM=(248,239,216,255); GOLD=(205,164,83,255); DARK=(35,32,28,255); BURG=(95,35,45,255); OLIVE=(72,86,55,255); ROSE=(118,58,72,255); BLUE=(33,52,68,255)

def font(path,size): return ImageFont.truetype(path,size)
def tb(draw,text,fnt):
    b=draw.multiline_textbbox((0,0),text,font=fnt,spacing=8,align='center'); return b[2]-b[0], b[3]-b[1]
def fit(draw,text,path,max_w,start,min_size=70):
    for s in range(start,min_size-1,-8):
        f=font(path,s)
        if tb(draw,text,f)[0]<=max_w: return f
    return font(path,min_size)
def center_text(draw,W,y,text,fnt,fill,spacing=16):
    b=draw.multiline_textbbox((0,0),text,font=fnt,spacing=spacing,align='center')
    x=(W-(b[2]-b[0]))/2
    draw.multiline_text((x,y),text,font=fnt,fill=fill,spacing=spacing,align='center')
    return y+(b[3]-b[1])
def cross(draw,cx,cy,scale,fill):
    w=int(42*scale); h=int(170*scale); arm=int(126*scale); ah=int(36*scale)
    draw.rounded_rectangle((cx-w//2,cy-h//2,cx+w//2,cy+h//2),radius=max(5,w//3),fill=fill)
    draw.rounded_rectangle((cx-arm//2,cy-ah//2,cx+arm//2,cy+ah//2),radius=max(5,ah//3),fill=fill)
def shield(draw,cx,cy,s,fill,outline=None):
    pts=[(cx,cy-210*s),(cx+180*s,cy-130*s),(cx+145*s,cy+90*s),(cx,cy+235*s),(cx-145*s,cy+90*s),(cx-180*s,cy-130*s)]
    draw.polygon(pts,fill=fill,outline=outline or fill)
    if outline: draw.line(pts+[pts[0]],fill=outline,width=int(18*s),joint='curve')
def save_png(img,path):
    img.save(path,optimize=True)
    return str(path)

def apparel_pray(path):
    W,H=4500,5400; img=Image.new('RGBA',(W,H),(0,0,0,0)); d=ImageDraw.Draw(img)
    cross(d,W//2,670,1.35,GOLD)
    d.line((920,925,3580,925),fill=GOLD,width=20)
    center_text(d,W,1070,'PRAY',font(FONT_SERIF,700),CREAM,10)
    center_text(d,W,1830,'LIKE IT',font(FONT_BOLD,360),GOLD,10)
    center_text(d,W,2260,'ALREADY',fit(d,'ALREADY',FONT_SERIF,3500,520),CREAM,10)
    center_text(d,W,2860,'MOVED',font(FONT_SERIF,650),CREAM,10)
    d.line((1000,3650,3500,3650),fill=GOLD,width=18)
    center_text(d,W,3800,'MARK 11:24',font(FONT_SERIF_REG,170),GOLD,8)
    center_text(d,W,4080,'FAITH SPEAKS BEFORE EYES SEE',font(FONT_BOLD,135),CREAM,8)
    return save_png(img,path)

def apparel_armor(path):
    W,H=4500,5400; img=Image.new('RGBA',(W,H),(0,0,0,0)); d=ImageDraw.Draw(img)
    shield(d,W//2,860,2.4,GOLD)
    cross(d,W//2,850,1.1,DARK)
    center_text(d,W,1370,'ARMOR ON',fit(d,'ARMOR ON',FONT_SERIF,3650,600),CREAM,12)
    d.rounded_rectangle((590,2120,3910,2550),radius=35,outline=GOLD,width=10)
    center_text(d,W,2150,'FEAR OFF',fit(d,'FEAR OFF',FONT_BOLD,3000,390),GOLD,8)
    center_text(d,W,2860,'STAND FIRM',font(FONT_SERIF,385),CREAM,8)
    d.line((1200,3415,3300,3415),fill=GOLD,width=16)
    center_text(d,W,3560,'EPHESIANS 6:11',font(FONT_SERIF_REG,165),GOLD,8)
    center_text(d,W,3835,'PUT ON THE WHOLE ARMOR OF GOD',font(FONT_BOLD,122),CREAM,8)
    return save_png(img,path)

def apparel_panic(path):
    W,H=4500,5400; img=Image.new('RGBA',(W,H),(0,0,0,0)); d=ImageDraw.Draw(img)
    for side in [-1,1]:
        x0=W//2+side*1280
        for i in range(7):
            y=920+i*260; x=x0-side*i*60
            d.line((x0,y-75,x,y+85),fill=OLIVE,width=20)
            d.ellipse((min(x,x+side*130),y-55,max(x,x+side*130),y+75),outline=OLIVE,width=12)
    center_text(d,W,980,"I DON'T",font(FONT_BOLD,360),ROSE,8)
    center_text(d,W,1440,'PANIC',font(FONT_SERIF,650),DARK,8)
    center_text(d,W,2190,'I PRAY',font(FONT_SERIF,650),DARK,8)
    d.line((1110,3000,3390,3000),fill=ROSE,width=16)
    center_text(d,W,3170,'PHILIPPIANS 4:6',font(FONT_SERIF_REG,155),ROSE,8)
    center_text(d,W,3460,'PRAYER BEFORE WORRY',font(FONT_BOLD,140),DARK,8)
    return save_png(img,path)

def mug(path, phrase, ref, ink=DARK, accent=GOLD):
    W,H=2700,1125; img=Image.new('RGBA',(W,H),(0,0,0,0)); d=ImageDraw.Draw(img)
    # repeat design on left/right front viewing zones, leave handle-safe center/end margins
    for cx in (675,2025):
        cross(d,cx,190,0.45,accent)
        d.line((cx-390,290,cx+390,290),fill=accent,width=8)
        words=phrase.upper().replace(' ALREADY ','\nALREADY\n').replace(' BEFORE ','\nBEFORE\n').replace(' THE ',' THE\n')
        f=fit(d,words,FONT_SERIF,880,154,70)
        b=d.multiline_textbbox((0,0),words,font=f,spacing=6,align='center')
        d.multiline_text((cx-(b[2]-b[0])/2,330),words,font=f,fill=ink,spacing=6,align='center')
        rf=font(FONT_SERIF_REG,54)
        rb=d.textbbox((0,0),ref,font=rf); d.text((cx-(rb[2]-rb[0])/2,870),ref,font=rf,fill=accent)
    return save_png(img,path)

def notebook(path):
    W,H=4500,5700; img=Image.new('RGBA',(W,H),(246,238,221,255)); d=ImageDraw.Draw(img)
    d.rounded_rectangle((270,280,4230,5420),radius=80,outline=BURG,width=28)
    d.rounded_rectangle((520,555,3980,5145),radius=45,outline=GOLD,width=10)
    cross(d,W//2,900,1.25,BURG)
    center_text(d,W,1260,'COMMAND\nTHE\nMORNING',font(FONT_SERIF,560),BURG,20)
    d.line((1050,3350,3450,3350),fill=GOLD,width=18)
    center_text(d,W,3540,'PRAYER JOURNAL',font(FONT_BOLD,230),DARK,8)
    center_text(d,W,3920,'WAKE IT WITH PRAYER',font(FONT_SERIF_REG,165),BURG,8)
    center_text(d,W,4500,'Psalm 5:3',font(FONT_SERIF_REG,135),DARK,8)
    return save_png(img,path)

def poster(path):
    W,H=4800,6000; img=Image.new('RGB',(W,H),(247,240,226)); d=ImageDraw.Draw(img)
    d.rectangle((260,260,W-260,H-260),outline=(75,52,35),width=20)
    d.rectangle((410,410,W-410,H-410),outline=(205,164,83),width=8)
    cross(d,W//2,840,1.45,(75,52,35,255))
    center_text(d,W,1220,'COVERED\nBEFORE\nI ENTERED',font(FONT_SERIF,520),(42,35,28),22)
    d.line((1050,3250,3750,3250),fill=(205,164,83),width=18)
    verse='The Lord is my refuge and my fortress;\nmy God, in Him will I trust.'
    center_text(d,W,3540,verse,font(FONT_SERIF_REG,165),(42,35,28),18)
    center_text(d,W,4350,'PSALM 91',font(FONT_BOLD,220),(95,35,45),12)
    center_text(d,W,4840,'HOME PROTECTION DECLARATION',font(FONT_SANS,118),(72,86,55),10)
    img.save(path,quality=95)
    return str(path)

# Create assets
products=[]
def add(**kw): products.append(kw)
pray=ASSET_DIR/'pray-like-it-already-moved-hoodie-4500x5400.png'; apparel_pray(pray)
armor=ASSET_DIR/'armor-on-fear-off-hoodie-4500x5400.png'; apparel_armor(armor)
panic=ASSET_DIR/'i-dont-panic-i-pray-sweatshirt-4500x5400.png'; apparel_panic(panic)
cmd_mug=ASSET_DIR/'command-the-morning-mug-2700x1125.png'; mug(cmd_mug,'Command The Morning','Psalm 5:3',BURG,GOLD)
covered_mug=ASSET_DIR/'covered-before-i-entered-mug-2700x1125.png'; mug(covered_mug,'Covered Before I Entered','Psalm 91',BLUE,GOLD)
nb=ASSET_DIR/'command-the-morning-prayer-journal-cover-4500x5700.png'; notebook(nb)
post=ASSET_DIR/'covered-before-i-entered-psalm91-poster-4800x6000.jpg'; poster(post)

add(key='pray_hoodie', lane='hoodie', title='Pray Like It Already Moved Hoodie | Christian Prayer Warrior Sweatshirt', path=str(pray), blueprint_id=77, provider_id=99, colors=['Black','Dark Heather','Maroon'], sizes=['S','M','L','XL','2XL'], price=4499, price_2xl=4699, tags=['prayer hoodie','christian hoodie','faith hoodie','prayer warrior','mark 11 24','christian gift','faith sweatshirt','pray shirt','bible verse hoodie','church hoodie','jesus hoodie','christian apparel','gift for believer'], description='Original Christian prayer declaration hoodie inspired by Mark 11:24. Large high-contrast artwork for dark fleece colors; Printify-generated mockups only.')
add(key='armor_hoodie', lane='hoodie', title='Armor On Fear Off Hoodie | Mens Christian Spiritual Warfare Sweatshirt', path=str(armor), blueprint_id=77, provider_id=99, colors=['Black','Dark Heather','Military Green'], sizes=['S','M','L','XL','2XL'], price=4499, price_2xl=4699, tags=['armor of god hoodie','mens christian hoodie','spiritual warfare','ephesians 6','christian hoodie','faith hoodie','prayer warrior','armor of god','christian men gift','bible verse hoodie','jesus hoodie','church hoodie','faith apparel'], description='Original spiritual warfare Christian hoodie design referencing Ephesians 6:11. Bold shield layout built for dark garments.')
add(key='panic_sweatshirt', lane='sweatshirt', title="I Don't Panic I Pray Sweatshirt | Christian Women Prayer Sweatshirt", path=str(panic), blueprint_id=49, provider_id=99, colors=['Sand','Ash','Sport Grey'], sizes=['S','M','L','XL','2XL'], price=3999, price_2xl=4299, tags=['christian sweatshirt','prayer sweatshirt','i dont panic i pray','faith women','philippians 4 6','christian women','prayer gift','faith sweatshirt','bible verse sweatshirt','jesus sweatshirt','church sweatshirt','womens faith gift','pray sweatshirt'], description='Original calming prayer sweatshirt design with dark rose/charcoal ink for light fleece. Inspired by Philippians 4:6; no copied artwork.')
add(key='command_mug', lane='mug', title='Command The Morning Mug | Christian Prayer Coffee Cup', path=str(cmd_mug), blueprint_id=68, provider_id=1, variants='all', price=1699, tags=['morning prayer mug','christian mug','command the morning','prayer warrior','faith coffee','coffee and prayer','psalm 5 3','christian gift','prayer cup','bible verse mug','jesus mug','church gift','faith mug'], description='Wrap-safe Christian coffee mug artwork with repeat front-facing Command The Morning layout and Psalm 5:3 reference.')
add(key='covered_mug', lane='mug', title='Covered Before I Entered Mug | Psalm 91 Christian Coffee Cup', path=str(covered_mug), blueprint_id=425, provider_id=1, variants='all', price=1899, tags=['psalm 91 mug','christian mug','covered by god','prayer cup','faith coffee','christian gift','protection prayer','bible verse mug','jesus mug','church gift','faith mug','prayer warrior','covered before i entered'], description='15oz Christian coffee mug with original Psalm 91 protection declaration, designed with dark ink for white ceramic readability.')
add(key='command_notebook', lane='office', title='Command The Morning Prayer Journal | Christian Notebook', path=str(nb), blueprint_id=74, provider_id=1, variants='all', price=1899, tags=['prayer journal','christian notebook','command the morning','faith office','christian journal','morning prayer','psalm 5 3','bible study notebook','church gift','christian gift','prayer notebook','faith journal','desk faith'], description='Christian prayer journal cover designed for office/desk use: Command The Morning, Prayer Journal, Wake It With Prayer.')
add(key='covered_poster', lane='wall_art', title='Psalm 91 Wall Art | Covered Before I Entered Christian Poster', path=str(post), blueprint_id=282, provider_id=99, variant_filter={'size':['11″ x 14″','12″ x 18″','16″ x 20″','18″ x 24″'],'paper':['Matte']}, price=1299, tags=['psalm 91 wall art','christian poster','home blessing','scripture print','covered by god','christian wall art','prayer poster','bible verse art','faith wall decor','christian home','protection prayer','psalm 91 print','christian gift'], description='Premium Psalm 91 home protection poster layout with warm ivory paper style, strong typography, and large readable declaration.')

research={'date':TODAY,'batch':BATCH,'sources':['Existing trend notes: research/trends/visual_trend_analysis_100_christian_pod.md','Cross-product expansion plan CSV','Public marketplace demand patterns: short Christian declarations, prayer/coffee gifts, Psalm 91 protection, spiritual warfare, prayer journal/desk faith items'],'demand_signals':['Prayer-first declarations work across hoodies and mugs','Psalm 91 protection language is giftable for home, car, work, and family','Spiritual warfare / Armor of God remains a strong men’s Christian apparel lane','Anxiety-to-prayer phrases are strong women’s sweatshirt and printable candidates','Command-the-morning is suited to mugs, notebooks, and desk routines'],'selected_products':[{'title':p['title'],'lane':p['lane']} for p in products]}
(PROD_DIR/'research_and_selection.json').write_text(json.dumps(research,indent=2),encoding='utf-8')

# QA sheets
bgmap={'Black':(18,18,18),'Dark Heather':(55,55,58),'Maroon':(74,24,34),'Military Green':(67,76,48),'Sand':(224,210,181),'Ash':(228,226,218),'Sport Grey':(178,178,170),'white ceramic':(252,252,248),'cream paper':(247,240,226)}
# Apparel sheet
app=[p for p in products if p['lane'] in ('hoodie','sweatshirt')]
cellw,cellh=680,860; sheet=Image.new('RGB',(40+4*cellw,110+len(app)*cellh),(245,240,229)); sd=ImageDraw.Draw(sheet)
sd.text((40,30),f'{BATCH} apparel contrast QA',font=font(FONT_BOLD,42),fill=(25,25,25))
for r,p in enumerate(app):
    art=Image.open(p['path']).convert('RGBA')
    for c,color in enumerate(p['colors']):
        x=40+c*cellw; y=100+r*cellh
        bg=Image.new('RGBA',(4500,5400),bgmap[color]+(255,))
        comp=Image.alpha_composite(bg,art).convert('RGB'); comp.thumbnail((cellw-60,cellh-120),Image.LANCZOS)
        sd.rounded_rectangle((x,y,x+cellw-30,y+cellh-35),radius=22,fill=(255,255,255),outline=(205,195,175),width=2)
        sheet.paste(comp,(x+(cellw-30-comp.width)//2,y+30))
        sd.text((x+20,y+cellh-80),p['title'][:42],font=font(FONT_BOLD,20),fill=(20,20,20))
        sd.text((x+20,y+cellh-50),color,font=font(FONT_SANS,22),fill=(50,50,50))
app_sheet=QA_DIR/f'{BATCH}_apparel_contact_sheet.jpg'; sheet.save(app_sheet,quality=92)
# Non-apparel sheet
non=[p for p in products if p['lane'] not in ('hoodie','sweatshirt')]
cellw,cellh=760,620; ns=Image.new('RGB',(40+2*cellw,110+((len(non)+1)//2)*cellh),(245,240,229)); nd=ImageDraw.Draw(ns)
nd.text((40,30),f'{BATCH} mug/office/wall-art QA',font=font(FONT_BOLD,40),fill=(25,25,25))
for i,p in enumerate(non):
    x=40+(i%2)*cellw; y=100+(i//2)*cellh
    im=Image.open(p['path']).convert('RGBA') if p['path'].endswith('.png') else Image.open(p['path']).convert('RGB')
    if im.mode=='RGBA':
        base_color=bgmap['white ceramic'] if p['lane']=='mug' else bgmap['cream paper']
        bg=Image.new('RGBA',im.size,base_color+(255,)); im=Image.alpha_composite(bg,im).convert('RGB')
    else: im=im.convert('RGB')
    im.thumbnail((cellw-80,cellh-130),Image.LANCZOS)
    nd.rounded_rectangle((x,y,x+cellw-35,y+cellh-35),radius=22,fill=(255,255,255),outline=(205,195,175),width=2)
    ns.paste(im,(x+(cellw-35-im.width)//2,y+25))
    nd.text((x+22,y+cellh-82),p['title'][:55],font=font(FONT_BOLD,20),fill=(20,20,20))
    nd.text((x+22,y+cellh-52),p['lane'],font=font(FONT_SANS,22),fill=(50,50,50))
non_sheet=QA_DIR/f'{BATCH}_non_apparel_contact_sheet.jpg'; ns.save(non_sheet,quality=92)

# QA gates
qa=[]; passed=True
scripture_ok={'Mark 11:24':'faith/prayer reference','Ephesians 6:11':'armor of God reference','Philippians 4:6':'prayer over anxiety reference','Psalm 5:3':'morning prayer reference','Psalm 91':'protection/refuge reference'}
for p in products:
    im=Image.open(p['path'])
    issues=[]
    if p['lane'] in ('hoodie','sweatshirt') and im.size!=(4500,5400): issues.append('wrong_apparel_size')
    if p['lane']=='mug' and im.size!=(2700,1125): issues.append('wrong_mug_size')
    if p['lane']=='office' and im.size!=(4500,5700): issues.append('wrong_notebook_size')
    if p['lane']=='wall_art' and im.size!=(4800,6000): issues.append('wrong_poster_size')
    if im.mode=='RGBA' and not im.getchannel('A').getbbox(): issues.append('blank_transparent_art')
    low=' '.join([p['title'],p['description'],' '.join(p['tags'])]).lower()
    if any(bad in low for bad in ['disney','nike','starbucks','barbie','swift','super bowl','nfl','nba']): issues.append('ip_risk_keyword')
    if p['lane'] in ('hoodie','sweatshirt') and 'White' in p.get('colors',[]): issues.append('unapproved_white_apparel_combo')
    if issues: passed=False
    qa.append({'title':p['title'],'lane':p['lane'],'file':p['path'],'size':im.size,'mode':im.mode,'issues':issues or ['none']})
qa_report={'result':'PASS' if passed else 'FAIL','checks':qa,'scripture_references_checked':scripture_ok,'contact_sheets':[str(app_sheet),str(non_sheet)],'notes':['Artwork-only upload files; contact sheets are QA previews only.','Apparel contrast checked on exact target garment colors; light sweatshirt uses dark ink, dark hoodies use cream/gold ink.','Mugs use 2700x1125 wrap-safe layouts with repeated front-view design and handle margins.','Poster/notebook were laid out separately with product-appropriate margins and hierarchy.']}
(QA_DIR/f'{BATCH}_qa_report.json').write_text(json.dumps(qa_report,indent=2),encoding='utf-8')
if not passed: raise SystemExit('QA failed; refusing Printify upload')

# Digital wall art candidate bundle (not uploaded to Etsy)
dig_files=[]
for ratio,(w,h) in {'2x3':(3600,5400),'3x4':(3600,4800),'4x5':(4800,6000),'11x14':(3300,4200)}.items():
    src=Image.open(post).convert('RGB'); src.thumbnail((w,h),Image.LANCZOS)
    canvas=Image.new('RGB',(w,h),(247,240,226)); canvas.paste(src,((w-src.width)//2,(h-src.height)//2))
    out=DIG_DIR/f'covered-before-i-entered-psalm91-{ratio}.jpg'; canvas.save(out,quality=95); dig_files.append(out)
readme=DIG_DIR/'README-BUYER-INSTRUCTIONS.txt'; readme.write_text('Digital printable candidate prepared for Etsy direct access. No physical item is included. Print at the matching ratio or send the JPG to a local/online print shop.\n',encoding='utf-8')
zip_path=DIG_DIR/'covered-before-i-entered-psalm91-digital-printable-candidate.zip'
with zipfile.ZipFile(zip_path,'w',zipfile.ZIP_DEFLATED) as z:
    for f in dig_files+[readme]: z.write(f,f.name)
(DIG_DIR/'etsy_listing_kit.md').write_text('# Etsy digital listing candidate\n\nTitle: Psalm 91 Wall Art Printable | Covered Before I Entered Christian Wall Art\n\nPrice seed: $9.99\n\nTags: psalm 91 printable, christian wall art, scripture print, home blessing, prayer poster, covered by god, bible verse art, faith wall decor\n\nStatus: Prepared only; requires direct Etsy digital-file access before listing.\n',encoding='utf-8')

# Listings CSV
csv_path=LISTING_DIR/f'{BATCH}.csv'
with csv_path.open('w',newline='',encoding='utf-8') as f:
    w=csv.DictWriter(f,fieldnames=['title','lane','description','tags','price_seed','asset_file'])
    w.writeheader()
    for p in products:
        w.writerow({'title':p['title'],'lane':p['lane'],'description':p['description'],'tags':','.join(p['tags']),'price_seed':f"{p['price']/100:.2f}",'asset_file':p['path']})

# Printify upload/create drafts
for line in Path('/root/.hermes/.env').read_text().splitlines():
    if '=' in line and not line.strip().startswith('#'):
        k,v=line.split('=',1); os.environ.setdefault(k.strip(),v.strip().strip('"').strip("'"))
token=os.environ.get('PRINTIFY_API_TOKEN'); shop=os.environ.get('PRINTIFY_SHOP_ID','27001435')
if not token: raise SystemExit('missing PRINTIFY_API_TOKEN')
base='https://api.printify.com/v1'; headers={'Authorization':f'Bearer {token}','Content-Type':'application/json'}
image_ids={}; created=[]; payloads=[]; failures=[]; variant_cache={}
def get_variants(bp,pp):
    key=(bp,pp)
    if key not in variant_cache:
        r=requests.get(f'{base}/catalog/blueprints/{bp}/print_providers/{pp}/variants.json',headers=headers,timeout=30); r.raise_for_status()
        js=r.json(); variant_cache[key]=js.get('variants',js) if isinstance(js,dict) else js
    return variant_cache[key]
def enabled_variants(p):
    vs=get_variants(p['blueprint_id'],p['provider_id']); out=[]
    if p.get('variants')=='all':
        for v in vs: out.append({'id':v['id'],'price':p['price'],'is_enabled':True})
    elif p.get('variant_filter'):
        flt=p['variant_filter']
        for v in vs:
            opt=v.get('options',{})
            if all(opt.get(k) in vals for k,vals in flt.items()): out.append({'id':v['id'],'price':p['price'],'is_enabled':True})
    else:
        by={(v['options'].get('color'),v['options'].get('size')):v['id'] for v in vs}
        for color in p['colors']:
            for size in p['sizes']:
                vid=by.get((color,size))
                if vid: out.append({'id':vid,'price':p.get('price_2xl',p['price']) if size=='2XL' else p['price'],'is_enabled':True})
    return out
for p in products:
    try:
        data=base64.b64encode(Path(p['path']).read_bytes()).decode('ascii')
        up=requests.post(f'{base}/uploads/images.json',headers=headers,json={'file_name':Path(p['path']).name,'contents':data},timeout=120)
        if up.status_code>=300: raise RuntimeError(f'upload {up.status_code}: {up.text[:500]}')
        image_id=up.json()['id']; image_ids[p['key']]=image_id
        enabled=enabled_variants(p); vids=[v['id'] for v in enabled]
        if not enabled: raise RuntimeError('no enabled variants selected')
        desc=p['description']+'\n\nDraft created by GracedPrintCo autonomous POD factory. Artwork-only asset uploaded; Printify generates product mockups. Not published by this automation.'
        body={'title':p['title'][:140],'description':desc,'blueprint_id':p['blueprint_id'],'print_provider_id':p['provider_id'],'variants':enabled,'print_areas':[{'variant_ids':vids,'placeholders':[{'position':'front','images':[{'id':image_id,'x':0.5,'y':0.5,'scale':0.86 if p['lane'] in ('hoodie','sweatshirt') else 1.0,'angle':0}]}]}],'tags':p['tags'][:13]}
        cr=requests.post(f'{base}/shops/{shop}/products.json',headers=headers,json=body,timeout=120)
        payloads.append({'title':p['title'],'payload':body,'status_code':cr.status_code,'response':cr.text[:1200]})
        if cr.status_code>=300: raise RuntimeError(f'create {cr.status_code}: {cr.text[:700]}')
        pj=cr.json(); created.append({'id':pj.get('id'),'title':pj.get('title',p['title']),'lane':p['lane'],'visible':pj.get('visible'),'status':'draft_created_not_published','image_id':image_id,'variant_count':len(enabled),'price_seed':p['price']/100})
        time.sleep(1)
    except Exception as e:
        failures.append({'title':p['title'],'lane':p['lane'],'error':str(e)})
(PROD_DIR/'uploaded_image_ids.json').write_text(json.dumps(image_ids,indent=2),encoding='utf-8')
(PROD_DIR/'created_products_summary.json').write_text(json.dumps(created,indent=2),encoding='utf-8')
(PROD_DIR/'create_product_payloads_and_responses.json').write_text(json.dumps(payloads,indent=2),encoding='utf-8')
if failures: (PROD_DIR/'printify_failures.json').write_text(json.dumps(failures,indent=2),encoding='utf-8')

summary='# Daily Multi-Product Factory Batch — '+BATCH+'\n\n'
summary+='Status: QA passed. Printify draft creation attempted for all passed physical POD assets; digital candidate prepared only for future Etsy digital access. No products were intentionally published.\n\n'
summary+=f'QA contact sheets:\n- {app_sheet}\n- {non_sheet}\n\n'
summary+='## Printify drafts created\n\n| Product | Lane | Printify product ID | Image ID | Variants | Price seed |\n|---|---|---:|---:|---:|---:|\n'
for c in created:
    summary+=f"| {c['title']} | {c['lane']} | {c['id']} | {c['image_id']} | {c['variant_count']} | ${c['price_seed']:.2f} |\n"
if failures:
    summary+='\n## Printify creation failures\n\n'
    for f in failures: summary+=f"- {f['title']} ({f['lane']}): {f['error']}\n"
summary+='\n## Digital-download candidate prepared\n\n- '+str(zip_path)+'\n- '+str(DIG_DIR/'etsy_listing_kit.md')+'\n\nDashboard: https://etsy.aetherisinnovations.com\n'
(PROD_DIR/'daily_factory_report.md').write_text(summary,encoding='utf-8')
print(json.dumps({'batch':BATCH,'qa':'PASS','created_count':len(created),'failures':failures,'created':created,'report':str(PROD_DIR/'daily_factory_report.md'),'dashboard':'https://etsy.aetherisinnovations.com'},indent=2))
