import sys
from datetime import date, timedelta
from openpyxl import Workbook
from openpyxl.styles import Font, PatternFill, Alignment, Border, Side, Protection
from openpyxl.worksheet.datavalidation import DataValidation
from openpyxl.formatting.rule import FormulaRule
NAVY="1F3A5F"; TEAL="0E7C86"; SOFT="F4F6F8"; INPUT="FFF9E6"; LINE="D5DBE3"
fill=lambda c: PatternFill("solid",fgColor=c)
thin=Side(style="thin",color=LINE); box=Border(left=thin,right=thin,top=thin,bottom=thin)
N=500
def build(path, rows, demo_banner=True):
    wb=Workbook()
    # ---------- Settings
    s=wb.active; s.title="Settings"; s.sheet_properties.tabColor="8A94A6"
    s.append(["Status","الحالة","Next follow-up after (days)","Counted from (A = enquiry date, H = last follow-up, HA = last follow-up if entered, else enquiry date)","Template #"])
    for r in [("New enquiry","استفسار جديد",1,"A",1),("Quote sent","عرض سعر مُرسل",3,"HA",2),
              ("Follow-up 1","متابعة ١",4,"H",3),("Follow-up 2","متابعة ٢",7,"H",4),
              ("Won","تم قبول العرض",None,None,None),("Lost","لم يتم قبول العرض",None,None,None),
              ("No reply (closed)","لم يرد العميل (مغلق)",None,None,None)]: s.append(list(r))
    s["G1"]="Channels / القنوات"
    for i,c in enumerate(["WhatsApp","Call","Web form","Walk-in","Other"],start=2): s[f"G{i}"]=c
    for c in s[1]: c.fill=fill(NAVY); c.font=Font(bold=True,color="FFFFFF"); c.alignment=Alignment(wrap_text=True,vertical="center",horizontal="center")
    for col,w in zip("ABCDEFG",[22,22,24,44,12,3,18]): s.column_dimensions[col].width=w
    s.row_dimensions[1].height=34
    for r in range(2,9):
        s[f"C{r}"].fill=fill(INPUT); s[f"C{r}"].border=box; s[f"C{r}"].protection=Protection(locked=False)
        s[f"B{r}"].alignment=Alignment(horizontal="right",readingOrder=2)
    s["A10"]="Yellow cells are yours to edit: change the delay days to change how fast reminders fire. / الخلايا الصفراء قابلة للتعديل: غيّر عدد الأيام لتغيير موعد التذكير."
    s["A10"].font=Font(italic=True,color="555555")
    dvd=DataValidation(type="whole",operator="between",formula1="0",formula2="60",allow_blank=True,showErrorMessage=True,error="Enter a whole number of days (0–60). / أدخل عدداً صحيحاً من الأيام.")
    s.add_data_validation(dvd); dvd.add("C2:C8")
    s.protection.sheet=True
    # ---------- Templates
    t=wb.create_sheet("Templates"); t.sheet_properties.tabColor="8A94A6"
    t.append(["#","Stage / المرحلة","English (copy-ready)","العربية (جاهز للنسخ)"])
    T=[(1,"New enquiry — acknowledge / استفسار جديد","Hello [Name], thank you for contacting us about [service]. We'll send you a quote shortly. Could you share the location and any photos of the unit?","السلام عليكم [Name]، شكراً لتواصلكم بخصوص [service]. سنرسل لكم عرض السعر قريباً. هل يمكنكم مشاركة الموقع وصور للجهاز؟"),
    (2,"Quote sent — first check / عرض مُرسل","Hello [Name], just checking you received our quote for [service]. Happy to answer any questions or adjust the scope.","السلام عليكم [Name]، أردنا التأكد من وصول عرض السعر الخاص بـ [service]. يسعدنا الإجابة عن أي استفسار أو تعديل نطاق العمل."),
    (3,"Follow-up 1 / متابعة ١","Hello [Name], following up on the quote for [service]. If timing or budget is a concern, let us know and we'll see what we can do.","السلام عليكم [Name]، نتابع معكم بخصوص عرض [service]. إذا كان الوقت أو الميزانية يشكلان عائقاً فأخبرونا لنرى ما يمكننا فعله."),
    (4,"Follow-up 2 — final / متابعة ٢ (أخيرة)","Hello [Name], we haven't heard back, so we'll close the quote for [service] for now. Message us any time to reopen it.","السلام عليكم [Name]، لم يصلنا رد، لذا سنغلق عرض [service] حالياً. يمكنكم مراسلتنا في أي وقت لإعادة فتحه.")]
    for x in T: t.append(list(x))
    for c in t[1]: c.fill=fill(NAVY); c.font=Font(bold=True,color="FFFFFF"); c.alignment=Alignment(horizontal="center",vertical="center")
    for col,w in zip("ABCD",[5,38,70,70]): t.column_dimensions[col].width=w
    for row in t.iter_rows(min_row=2,max_row=5):
        for c in row: c.alignment=Alignment(wrap_text=True,vertical="top"); c.border=box
        row[3].alignment=Alignment(wrap_text=True,vertical="top",horizontal="right",readingOrder=2)
        for c in (row[2],row[3]): c.fill=fill(INPUT); c.protection=Protection(locked=False)
        t.row_dimensions[row[0].row].height=62
    t["A7"]="Keep [Name] and [service] in the text: they are filled in automatically. Arabic is a draft; have a native speaker review it. / أبقِ [Name] و [service] في النص. النص العربي مسودة."
    t["A7"].font=Font(italic=True,color="555555")
    t.protection.sheet=True
    # ---------- Quotes
    q=wb.create_sheet("Quotes",0); q.sheet_properties.tabColor=NAVY
    heads=["Enquiry / quote date\nتاريخ الاستفسار / العرض","Customer\nالعميل","Phone / WhatsApp\nالهاتف / واتساب","Service\nالخدمة","Channel\nالقناة","Quote (OMR)\nالعرض (ر.ع.)","Status\nالحالة","Last follow-up\nآخر متابعة",
           "Days since enquiry\nأيام منذ الاستفسار","Next follow-up\nالمتابعة القادمة","Action\nالإجراء","الحالة (عربي)","WhatsApp message (EN)\nرسالة واتساب (إنجليزي)","رسالة واتساب (عربي)","sort","stage"]
    q.append(heads)
    for i,c in enumerate(q[1],start=1):
        c.fill=fill(NAVY if i<=8 else TEAL); c.font=Font(bold=True,color="FFFFFF"); c.alignment=Alignment(wrap_text=True,vertical="center",horizontal="center"); c.border=box
    q.row_dimensions[1].height=50
    for i,row in enumerate(rows,start=2):
        for j,v in enumerate(row,start=1): q.cell(row=i,column=j,value=v)
    for r in range(2,N+1):
        q[f"P{r}"]=f'=IF(OR(A{r}="",G{r}=""),"",IFERROR(MATCH(G{r},Settings!$A$2:$A$8,0),""))'
        q[f"I{r}"]=f'=IF(A{r}="","",MAX(0,TODAY()-A{r}))'
        q[f"J{r}"]=(f'=IF(OR(P{r}="",A{r}=""),"",IF(COUNT(INDEX(Settings!$C$2:$C$8,P{r}))=0,"",IF(AND(INDEX(Settings!$D$2:$D$8,P{r})="H",H{r}=""),"",IF(INDEX(Settings!$D$2:$D$8,P{r})="A",A{r},IF(INDEX(Settings!$D$2:$D$8,P{r})="HA",IF(H{r}="",A{r},H{r}),H{r}))+INDEX(Settings!$C$2:$C$8,P{r}))))')
        q[f"K{r}"]=(f'=IF(A{r}="",IF(B{r}<>"","Add date / أدخل تاريخ الاستفسار",""),IF(G{r}="","Set status / اختر الحالة",IF(P{r}="","Check status / راجع الحالة",IF(COUNT(INDEX(Settings!$C$2:$C$8,P{r}))=0,"Closed / مغلق",IF(AND(INDEX(Settings!$D$2:$D$8,P{r})="H",H{r}=""),"Add last follow-up date / أدخل تاريخ آخر متابعة",IF(J{r}<TODAY(),"OVERDUE / متأخر",IF(J{r}=TODAY(),"DUE TODAY / مستحق اليوم","Upcoming / قادم")))))))')
        q[f"L{r}"]=f'=IF(P{r}="","",INDEX(Settings!$B$2:$B$8,P{r}))'
        for col,tc in (("M","C"),("N","D")):
            q[f"{col}{r}"]=(f'=IF(OR(P{r}="",J{r}=""),"",IFERROR(SUBSTITUTE(SUBSTITUTE(INDEX(Templates!${tc}$2:${tc}$5,INDEX(Settings!$E$2:$E$8,P{r})),"[Name]",B{r}),"[service]",D{r}),"Check template / راجع القالب"))')
        q[f"O{r}"]=f'=IF(OR(P{r}="",A{r}=""),"",IF(J{r}<>"",J{r}+ROW()/100000,IF(COUNT(INDEX(Settings!$C$2:$C$8,P{r}))=0,"",A{r}+ROW()/100000)))'
        for c in "ABCDEFGH":
            cell=q[f"{c}{r}"]; cell.fill=fill(INPUT); cell.border=box; cell.protection=Protection(locked=False)
        for c in "IJKLMN":
            cell=q[f"{c}{r}"]; cell.fill=fill(SOFT); cell.border=box
        for c in "AHJ": q[f"{c}{r}"].number_format="DD-MMM-YYYY"
        q[f"F{r}"].number_format='#,##0.000'
        q[f"L{r}"].alignment=Alignment(horizontal="right",readingOrder=2)
        q[f"N{r}"].alignment=Alignment(horizontal="right",readingOrder=2,wrap_text=True,vertical="top")
        q[f"M{r}"].alignment=Alignment(wrap_text=True,vertical="top")
        q[f"K{r}"].font=Font(bold=True)
    dvs=DataValidation(type="list",formula1="=Settings!$A$2:$A$8",allow_blank=True,showErrorMessage=True,error="Pick a status from the list. / اختر حالة من القائمة.")
    dvc=DataValidation(type="list",formula1="=Settings!$G$2:$G$6",allow_blank=True)
    dva=DataValidation(type="decimal",operator="greaterThanOrEqual",formula1="0",allow_blank=True,showErrorMessage=True,error="Enter an amount in OMR (0 or more). / أدخل مبلغاً بالريال العماني.")
    dvdt=DataValidation(type="date",operator="greaterThan",formula1="36526",allow_blank=True,showErrorMessage=True,error="Enter a valid date. / أدخل تاريخاً صحيحاً.")
    for d,rng in ((dvs,f"G2:G{N}"),(dvc,f"E2:E{N}"),(dva,f"F2:F{N}"),(dvdt,f"A2:A{N}"),(dvdt,f"H2:H{N}")):
        q.add_data_validation(d); d.add(rng)
    K=f"K2:K{N}"
    q.conditional_formatting.add(K,FormulaRule(formula=['LEFT($K2,7)="OVERDUE"'],fill=fill("F8C9C9"),font=Font(bold=True,color="9B1C1C")))
    q.conditional_formatting.add(K,FormulaRule(formula=['LEFT($K2,3)="DUE"'],fill=fill("FFE08A"),font=Font(bold=True,color="7A5200")))
    q.conditional_formatting.add(K,FormulaRule(formula=['LEFT($K2,8)="Upcoming"'],fill=fill("D5EFDF"),font=Font(bold=True,color="1E6B3A")))
    q.conditional_formatting.add(K,FormulaRule(formula=['OR(LEFT($K2,3)="Set",LEFT($K2,5)="Check",LEFT($K2,3)="Add")'],fill=fill("E6D9F5"),font=Font(bold=True,color="5B2C83"),stopIfTrue=True))
    OPEN='AND($A2<>"",OR($G2="New enquiry",$G2="Quote sent",$G2="Follow-up 1",$G2="Follow-up 2"),{c}2="")'
    for col in "BDF":
        q.conditional_formatting.add(f"{col}2:{col}{N}",FormulaRule(formula=[OPEN.format(c="$"+col)],fill=fill("E6D9F5")))
    G=f"G2:G{N}"
    q.conditional_formatting.add(G,FormulaRule(formula=['$G2="Won"'],fill=fill("BFE5CC")))
    q.conditional_formatting.add(G,FormulaRule(formula=['$G2="Lost"'],fill=fill("F2C4C4")))
    q.conditional_formatting.add(G,FormulaRule(formula=['$G2="No reply (closed)"'],fill=fill("DADDE3")))
    for col,w in zip("ABCDEFGHIJKLMNOP",[16,26,20,34,12,15,18,15,12,15,24,16,58,58,4,4]): q.column_dimensions[col].width=w
    q.column_dimensions["O"].hidden=True; q.column_dimensions["P"].hidden=True
    q.freeze_panes="C2"; q.sheet_view.zoomScale=100
    q.protection.sheet=True
    q.protection.formatColumns=False
    q.protection.autoFilter=False
    q.auto_filter.ref=f"A1:N{N}"
    if demo_banner:
        q["R1"]="DEMO — fictional data. Clear the yellow cells before real use. / بيانات تجريبية فقط"; q["R1"].font=Font(bold=True,color="C00000")
    # ---------- Dashboard
    d=wb.create_sheet("Dashboard",0); d.sheet_properties.tabColor=TEAL; d.sheet_view.showGridLines=False
    d.column_dimensions["A"].width=2
    for col,w in zip("BCDEFGHI",[24,24,16,24,18,18,58,58]): d.column_dimensions[col].width=w
    d["B1"]="Quote follow-up dashboard  |  لوحة متابعة عروض الأسعار"; d["B1"].font=Font(size=20,bold=True,color=NAVY)
    d.row_dimensions[1].height=36
    d["G1"]="As of  |  بتاريخ"; d["G1"].alignment=Alignment(horizontal="right"); d["G1"].font=Font(color="666666")
    d["H1"]="=TODAY()"; d["H1"].number_format="DD-MMM-YYYY"; d["H1"].font=Font(bold=True,color=NAVY); d["H1"].alignment=Alignment(horizontal="left")
    d["B2"]="What needs attention first. Updates automatically from the Quotes sheet. / ما يحتاج انتباهك أولاً. يتحدث تلقائياً."; d["B2"].font=Font(italic=True,color="666666")
    open_f="+".join(f'COUNTIF(Quotes!$G$2:$G${N},"{x}")' for x in ["New enquiry","Quote sent","Follow-up 1","Follow-up 2"])
    val_f="+".join(f'SUMIF(Quotes!$G$2:$G${N},"{x}",Quotes!$F$2:$F${N})' for x in ["New enquiry","Quote sent","Follow-up 1","Follow-up 2"])
    won=f'COUNTIF(Quotes!$G$2:$G${N},"Won")'; lost=f'COUNTIF(Quotes!$G$2:$G${N},"Lost")'; nor=f'COUNTIF(Quotes!$G$2:$G${N},"No reply (closed)")'
    cards=[("Open quotes","عروض مفتوحة",f"={open_f}","0"),
           ("Value of open quotes","قيمة العروض المفتوحة",f"={val_f}",'#,##0.000 "OMR"'),
           ("Overdue follow-ups","متابعات متأخرة",f'=COUNTIF(Quotes!$K$2:$K${N},"OVERDUE*")',"0"),
           ("Due today","مستحقة اليوم",f'=COUNTIF(Quotes!$K$2:$K${N},"DUE TODAY*")',"0"),
           ("Won","عروض مقبولة",f"={won}","0"),
           ("Win rate — closed quotes","نسبة الفوز من العروض المغلقة",f'=IF({won}+{lost}+{nor}=0,"N/A",{won}/({won}+{lost}+{nor}))',"0%")]
    for i,(en,ar,f,nf) in enumerate(cards):
        col="BCDEFG"[i]
        a=d[f"{col}4"]; a.value=en; a.font=Font(size=10,color="FFFFFF",bold=True); a.fill=fill(NAVY if i not in (2,3) else "B23A3A" if i==2 else "C98A00"); a.alignment=Alignment(horizontal="center",vertical="center",wrap_text=True)
        b=d[f"{col}5"]; b.value=f; b.number_format=nf; b.font=Font(size=22,bold=True,color=NAVY); b.fill=fill(SOFT); b.alignment=Alignment(horizontal="center",vertical="center")
        c=d[f"{col}6"]; c.value=ar; c.font=Font(size=10,color="555555"); c.fill=fill(SOFT); c.alignment=Alignment(horizontal="center",readingOrder=2,wrap_text=True,vertical="center")
        for r in (4,5,6): d[f"{col}{r}"].border=box
    d.row_dimensions[4].height=26; d.row_dimensions[5].height=44; d.row_dimensions[6].height=28
    d["B8"]="Next actions  |  الإجراءات التالية"; d["B8"].font=Font(size=14,bold=True,color=NAVY)
    d["B9"]="Sorted by due date. Overdue first. Shows the 8 most urgent; all others are on the Quotes sheet. Copy the message and send it yourself. Nothing is sent automatically. / تعرض أكثر 8 متابعات إلحاحاً والباقي في ورقة العروض. مرتبة حسب الموعد. انسخ الرسالة وأرسلها بنفسك. لا يُرسل شيء تلقائياً."; d["B9"].font=Font(italic=True,color="666666")
    qh=["Customer\nالعميل","Service\nالخدمة","Due\nالموعد","Action\nالإجراء","Quote (OMR)\nالعرض","Status\nالحالة","Message to copy (EN)\nالرسالة (إنجليزي)","رسالة للنسخ (عربي)"]
    for i,h in enumerate(qh):
        c=d.cell(row=10,column=2+i,value=h); c.fill=fill(TEAL); c.font=Font(bold=True,color="FFFFFF"); c.alignment=Alignment(horizontal="center",vertical="center",wrap_text=True); c.border=box
    d.row_dimensions[10].height=34
    QN=8
    for k in range(1,QN+1):
        r=10+k
        d[f"Z{r}"]=f'=IFERROR(MATCH(SMALL(Quotes!$O$2:$O${N},{k}),Quotes!$O$2:$O${N},0),"")'
        def pick(col): return f'=IF($Z{r}="","",INDEX(Quotes!${col}$2:${col}${N},$Z{r}))'
        for colD,colQ in zip("BCDEFGHI","BDJKFLMN"): d[f"{colD}{r}"]=pick(colQ)
        d[f"D{r}"].number_format="DD-MMM-YYYY"; d[f"F{r}"].number_format='#,##0.000'
        for c in "BCDEFGHI":
            cell=d[f"{c}{r}"]; cell.border=box; cell.alignment=Alignment(wrap_text=True,vertical="top")
        d[f"G{r}"].alignment=Alignment(horizontal="right",readingOrder=2,vertical="top"); d[f"I{r}"].alignment=Alignment(horizontal="right",readingOrder=2,wrap_text=True,vertical="top")
        d[f"E{r}"].font=Font(bold=True)
        d.row_dimensions[r].height=48
    d.column_dimensions["Z"].hidden=True
    E=f"E11:E{10+QN}"
    d.conditional_formatting.add(E,FormulaRule(formula=['LEFT($E11,7)="OVERDUE"'],fill=fill("F8C9C9"),font=Font(bold=True,color="9B1C1C")))
    d.conditional_formatting.add(E,FormulaRule(formula=['LEFT($E11,3)="DUE"'],fill=fill("FFE08A"),font=Font(bold=True,color="7A5200")))
    d.conditional_formatting.add(E,FormulaRule(formula=['LEFT($E11,8)="Upcoming"'],fill=fill("D5EFDF"),font=Font(bold=True,color="1E6B3A")))
    r0=10+QN+2
    d[f"B{r0}"]="Results  |  النتائج"; d[f"B{r0}"].font=Font(size=14,bold=True,color=NAVY)
    res=[("Won","تم قبول العرض",f"={won}",f'=SUMIF(Quotes!$G$2:$G${N},"Won",Quotes!$F$2:$F${N})'),
         ("Lost","لم يتم قبول العرض",f"={lost}",f'=SUMIF(Quotes!$G$2:$G${N},"Lost",Quotes!$F$2:$F${N})'),
         ("No reply (closed)","لم يرد العميل (مغلق)",f"={nor}",f'=SUMIF(Quotes!$G$2:$G${N},"No reply (closed)",Quotes!$F$2:$F${N})')]
    for j,h in enumerate(["Outcome","النتيجة","Count","Quoted value (OMR)"]):
        c=d.cell(row=r0+1,column=2+j,value=h); c.fill=fill(TEAL); c.font=Font(bold=True,color="FFFFFF"); c.border=box; c.alignment=Alignment(horizontal="center")
    for i,(en,ar,cnt,val) in enumerate(res):
        r=r0+2+i
        d[f"B{r}"]=en; d[f"C{r}"]=ar; d[f"D{r}"]=cnt; d[f"E{r}"]=val; d[f"E{r}"].number_format='#,##0.000 "OMR"'
        d[f"C{r}"].alignment=Alignment(horizontal="right",readingOrder=2)
        for c in "BCDE": d[f"{c}{r}"].border=box
    d[f"B{r0+6}"]="Win rate is calculated from quotes marked Won, Lost or No reply (closed). Open quotes are excluded. It shows N/A until a quote is closed. It describes your own records; it is not a forecast. / نسبة الفوز تُحسب من العروض المغلقة فقط وليست توقعاً."
    inc="+".join(f'SUMPRODUCT((Quotes!$G$2:$G${N}="{x}")*(Quotes!$F$2:$F${N}="")*(Quotes!$A$2:$A${N}<>""))' for x in ["New enquiry","Quote sent","Follow-up 1","Follow-up 2"])
    d[f"B{r0+5}"]="Open quotes with no amount  |  عروض مفتوحة بلا مبلغ"; d[f"D{r0+5}"]=f"={inc}"
    for c in "BCDE": d[f"{c}{r0+5}"].border=box
    d[f"B{r0+6}"].font=Font(italic=True,color="666666")
    d.protection.sheet=True
    # ---------- Start here
    h=wb.create_sheet("Start here",0); h.sheet_properties.tabColor="2E9E5B"; h.sheet_view.showGridLines=False
    h.column_dimensions["A"].width=2; h.column_dimensions["B"].width=110
    lines=[("Quote follow-up tracker  |  متتبع متابعة عروض الأسعار",20,True,NAVY),
    ("Never lose track of a quote. / لا تفقد متابعة أي عرض سعر.",12,False,"555555"),("",11,False,None),
    ("How it works  |  كيف يعمل",14,True,NAVY),
    ("1.  Go to the Quotes sheet and type each enquiry in the yellow cells (columns A–H). / سجّل كل استفسار في الخلايا الصفراء.",11,False,None),
    ("2.  Change the Status as the job moves on. Pick it from the list. / غيّر الحالة من القائمة مع تقدم الطلب.",11,False,None),
    ("3.  Grey and teal columns calculate by themselves: next follow-up date, OVERDUE / DUE TODAY flag, and a copy-ready WhatsApp message in English and Arabic. / الأعمدة الرمادية تُحسب تلقائياً.",11,False,None),
    ("4.  Open the Dashboard each morning: it lists who to follow up with first. / افتح لوحة المتابعة كل صباح.",11,False,None),
    ("5.  Copy the message and send it yourself. The tracker never sends anything. / انسخ الرسالة وأرسلها بنفسك. النظام لا يرسل شيئاً.",11,False,None),("",11,False,None),
    ("Colours  |  الألوان",14,True,NAVY),
    ("Yellow cells = you type here.   Grey cells = automatic, locked.   Red = overdue.   Amber = due today.   Green = upcoming.",11,False,None),("",11,False,None),
    ("Settings  |  الإعدادات",14,True,NAVY),
    ("Change follow-up delays on the Settings sheet and edit message wording on the Templates sheet (yellow cells). Sheets are protected without a password so formulas aren't overwritten by accident; you can unprotect via Review ▸ Unprotect Sheet. / يمكن تعديل المدد والرسائل في الخلايا الصفراء.",11,False,None),("",11,False,None),
    ("Your data  |  بياناتك",14,True,NAVY),
    ("This demo contains fictional records only. In real use you own this file and its customer details; names and phone numbers are personal data, so keep the file where only your team can open it. Up to 499 quotes are supported. / هذه النسخة تحتوي بيانات تجريبية فقط.",11,False,None)]
    for i,(txt,sz,b,colr) in enumerate(lines,start=1):
        c=h[f"B{i}"]; c.value=txt; c.font=Font(size=sz,bold=b,color=colr or "222222"); c.alignment=Alignment(wrap_text=True,vertical="top")
    h.row_dimensions[1].height=34
    h.protection.sheet=True
    wb._sheets=[wb["Start here"],wb["Dashboard"],wb["Quotes"],wb["Templates"],wb["Settings"]]
    wb.active=1
    wb.save(path)
if __name__=="__main__":
    OUT=sys.argv[1]; mode=sys.argv[2]
    T=date.today
    if mode=="demo":
        def D(o): return f"=TODAY()-{-o}" if o<0 else f"=TODAY()+{o}"
        demo=[(-1,"FICTIONAL CUSTOMER A","Split AC gas refill (2 units)","WhatsApp",35,"New enquiry",None),
        (-4,"FICTIONAL CUSTOMER B","Annual maintenance contract (villa, 5 units)","Call",240,"Quote sent",None),
        (-8,"FICTIONAL CUSTOMER C","Install 2 split units","WhatsApp",310,"Follow-up 1",-4),
        (-15,"FICTIONAL CUSTOMER D","Duct AC repair (office)","Web form",120,"Follow-up 2",-6),
        (-10,"FICTIONAL CUSTOMER E","Split AC deep clean (3 units)","WhatsApp",45,"Won",-6),
        (-18,"FICTIONAL CUSTOMER F","Compressor replacement","Call",95,"Lost",-9),
        (-25,"FICTIONAL CUSTOMER G","Window AC replacement","WhatsApp",60,"No reply (closed)",-8),
        (-2,"FICTIONAL CUSTOMER H","Shop AC servicing (4 units)","WhatsApp",80,"Quote sent",None)]
        rows=[[D(o),c,f"+968 0000 000{i}",sv,ch,amt,st,(D(r) if r is not None else None)] for i,(o,c,sv,ch,amt,st,r) in enumerate(demo)]
        build(OUT,rows)
    elif mode=="edge":
        t=T()
        rows=[[t-timedelta(2),"Blank status","x","AC","Call",50,None,None],
              [t-timedelta(3),"Blank amount","x","AC","Call",None,"Quote sent",None],
              [t-timedelta(9),"FU1 no last date","x","AC","Call",10,"Follow-up 1",None],
              [t+timedelta(5),"Future date","x","AC","Call",20,"New enquiry",None],
              [t-timedelta(1),"Bad status","x","AC","Call",20,"Typo status",None],
              [None,"No date","x","AC","Call",20,"Quote sent",None],
              [t-timedelta(30),"Won one","x","AC","Call",100,"Won",t-timedelta(20)]]
        build(OUT,rows,False)
    elif mode=="empty": build(OUT,[],False)
    elif mode=="noclosed":
        t=T(); build(OUT,[[t-timedelta(1),"Only open","x","AC","Call",10,"Quote sent",None]],False)
    elif mode=="stress":
        t=T(); rows=[[t-timedelta(5),f"Same-day {i}","x","AC","Call",10+i,"Quote sent",None] for i in range(12)]
        rows += [[None]*8 for _ in range(N-1-12-1)]
        rows.append([t-timedelta(10),"LAST ROW 500","x","AC","Call",None,"Quote sent",None])
        build(OUT,rows,False)
