#!/usr/bin/env python3
"""
360LM BTL Add-in — Template Generator (Phase 1)
Produces: 360LM_BTL_Template.pptx  (standardised, machine-readable)
Run: python3 generate_template.py
"""
from pptx import Presentation
from pptx.util import Inches, Pt, Emu
from pptx.dml.color import RGBColor
from pptx.enum.text import PP_ALIGN
from pptx.oxml.ns import qn
from lxml import etree
import json, zipfile, shutil, os, io

OUT_PPTX = os.path.join(os.path.dirname(__file__), "360LM_BTL_Template.pptx")

# ── Brand colours ─────────────────────────────────────────────────────────────
NAVY   = RGBColor(0x1F, 0x38, 0x64)
ORANGE = RGBColor(0xFF, 0x6B, 0x00)
WHITE  = RGBColor(0xFF, 0xFF, 0xFF)
LIGHT  = RGBColor(0xF2, 0xF2, 0xF2)
DARK   = RGBColor(0x21, 0x21, 0x21)
GREY   = RGBColor(0xAA, 0xAA, 0xAA)

W = Inches(13.33)
H = Inches(7.5)

# ── Helpers ───────────────────────────────────────────────────────────────────
def _set_bg(slide, rgb):
    fill = slide.background.fill
    fill.solid()
    fill.fore_color.rgb = rgb

def _rect(slide, l, t, w, h, fill=None, line=None, lw=Pt(0), name=None):
    from pptx.enum.shapes import MSO_SHAPE_TYPE
    s = slide.shapes.add_shape(1, l, t, w, h)
    s.fill.solid() if fill else s.fill.background()
    if fill: s.fill.fore_color.rgb = fill
    s.line.fill.background()
    if line:
        s.line.color.rgb = line
        s.line.width = lw or Pt(1)
    if name: s.name = name
    return s

def _txt(slide, text, l, t, w, h, pt, bold=False, color=DARK,
         align=PP_ALIGN.LEFT, wrap=True, name=None):
    tb = slide.shapes.add_textbox(l, t, w, h)
    tf = tb.text_frame
    tf.word_wrap = wrap
    p = tf.paragraphs[0]
    p.alignment = align
    run = p.add_run()
    run.text = text
    run.font.size = Pt(pt)
    run.font.bold = bold
    run.font.color.rgb = color
    if name: tb.name = name
    return tb

def _footer(slide, text):
    _rect(slide, 0, Inches(7.2), W, Inches(0.3), fill=NAVY)
    _txt(slide, text, 0, Inches(7.21), W, Inches(0.29), 8,
         color=LIGHT, align=PP_ALIGN.CENTER)

def _header_bar(slide, counter_no, counter_name, city_state, brand):
    _rect(slide, 0, 0, W, Inches(1.1), fill=NAVY)
    _txt(slide, f"Counter #{counter_no}",
         Inches(0.25), Inches(0.05), Inches(2), Inches(0.38), 10,
         bold=True, color=ORANGE)
    _txt(slide, counter_name,
         Inches(0.25), Inches(0.42), Inches(8.8), Inches(0.62), 22,
         bold=True, color=WHITE, name=f"lm360_counter_{counter_no}")
    _txt(slide, city_state,
         Inches(9.2), Inches(0.08), Inches(3.9), Inches(0.38), 11,
         color=LIGHT, align=PP_ALIGN.RIGHT,
         name=f"lm360_city_state_{counter_no}")
    pill = _rect(slide, Inches(9.2), Inches(0.5), Inches(1.3), Inches(0.38),
                 fill=ORANGE, name=f"lm360_brand_tag_{counter_no}")
    _txt(slide, brand,
         Inches(9.2), Inches(0.5), Inches(1.3), Inches(0.38), 12,
         bold=True, color=WHITE, align=PP_ALIGN.CENTER)

def _items_table(slide, counter_no, rows):
    """Draws the items table; rows = list of dicts with keys type/material/size/qty/rem_prod/rem_inst"""
    cols = [
        ("Item Type",          "type",     1.7),
        ("Material",           "material", 1.5),
        ("Size (W × H)",       "size",     1.5),
        ("Qty",                "qty",      0.5),
        ("Remarks — Prod",     "rem_prod", 2.2),
        ("Remarks — Install",  "rem_inst", 1.8),
    ]
    HDR_H = Inches(0.38)
    ROW_H = Inches(0.44)
    TABLE_TOP = Inches(1.18)

    # Header bar
    _rect(slide, Inches(0.25), TABLE_TOP, Inches(9.2), HDR_H, fill=NAVY)
    x = 0.25
    for label, _, w in cols:
        _txt(slide, label, Inches(x + 0.05), TABLE_TOP, Inches(w - 0.05), HDR_H,
             9, bold=True, color=WHITE, align=PP_ALIGN.CENTER)
        x += w

    # Data rows
    for ri, row in enumerate(rows):
        y = TABLE_TOP + HDR_H + ri * ROW_H
        bg = LIGHT if ri % 2 == 0 else WHITE
        _rect(slide, Inches(0.25), y, Inches(9.2), ROW_H, fill=bg)
        x = 0.25
        for _, key, w in cols:
            val = str(row.get(key, ""))
            _txt(slide, val, Inches(x + 0.06), y + Emu(30000),
                 Inches(w - 0.08), ROW_H, 9, color=DARK,
                 name=f"lm360_{counter_no}_{ri}_{key}" if val else None)
            x += w

    # Orange left stripe
    total_h = HDR_H + len(rows) * ROW_H
    _rect(slide, Inches(0.25), TABLE_TOP, Inches(0.07), total_h, fill=ORANGE)

    return TABLE_TOP + total_h   # returns bottom Y for creative placeholder

def _creative_box(slide, counter_no, top_y):
    _rect(slide, Inches(9.6), Inches(1.18), Inches(3.6),
          H - Inches(1.18) - Inches(0.35),
          fill=LIGHT, line=ORANGE, lw=Pt(1.5),
          name=f"lm360_creative_{counter_no}")
    _txt(slide, "📸  Drag creative\nor reference image here",
         Inches(9.6), Inches(3.0), Inches(3.6), Inches(1.5),
         11, color=GREY, align=PP_ALIGN.CENTER)

def _embed_json(slide, counter_no, data):
    """Hidden textbox off-slide right edge carries machine-readable JSON."""
    _txt(slide, json.dumps(data, ensure_ascii=False),
         Inches(14.0), Inches(0), Inches(6), Inches(0.5),
         1, color=WHITE, name=f"lm360_json_{counter_no}")


# ── Slide builders ────────────────────────────────────────────────────────────
def make_cover(prs):
    slide = prs.slides.add_slide(prs.slide_layouts[6])
    _set_bg(slide, NAVY)
    _rect(slide, 0, 0, W, Inches(0.12), fill=ORANGE)
    _rect(slide, 0, Inches(7.38), W, Inches(0.12), fill=ORANGE)

    # Logo placeholder
    _rect(slide, Inches(0.5), Inches(0.4), Inches(3.2), Inches(1.4),
          fill=None, line=ORANGE, lw=Pt(1.5), name="lm360_logo_placeholder")
    _txt(slide, "CLIENT LOGO", Inches(0.5), Inches(0.4), Inches(3.2),
         Inches(1.4), 12, bold=True, color=ORANGE, align=PP_ALIGN.CENTER)

    _txt(slide, "BTL Installation Brief",
         Inches(0.5), Inches(2.0), Inches(12), Inches(1.2), 38,
         bold=True, color=WHITE, align=PP_ALIGN.LEFT, name="lm360_deck_title")

    fields = [
        ("Brand",       "lm360_brand",       "[ Enter Brand Name ]"),
        ("Campaign",    "lm360_campaign",     "[ Enter Campaign Name ]"),
        ("Date",        "lm360_date",         "[ DD-MMM-YYYY ]"),
        ("Prepared by", "lm360_prepared_by",  "[ Your Name / Company ]"),
    ]
    for i, (label, tag, placeholder) in enumerate(fields):
        y = 3.4 + i * 0.62
        _txt(slide, label + ":", Inches(0.5), Inches(y), Inches(1.8),
             Inches(0.5), 14, bold=True, color=ORANGE)
        _txt(slide, placeholder, Inches(2.4), Inches(y), Inches(9),
             Inches(0.5), 14, color=WHITE, name=tag)

    _rect(slide, 0, Inches(6.9), W, Inches(0.5), fill=RGBColor(0x0A,0x16,0x38))
    _txt(slide, "360 Degree Marketing  •  Use the 360LM Add-in ribbon tab to fill in this deck  •  Do not edit tagged shapes manually",
         Inches(0.3), Inches(6.9), W - Inches(0.6), Inches(0.5), 9,
         color=GREY, align=PP_ALIGN.CENTER)


def make_counter_slide(prs, counter_no, data):
    """data = {counter_name, city, state, brand, items: [...]}"""
    slide = prs.slides.add_slide(prs.slide_layouts[6])
    _set_bg(slide, WHITE)

    _header_bar(slide, counter_no,
                data.get("counter_name", f"Counter #{counter_no}"),
                f"{data.get('city','')}  |  {data.get('state','')}",
                data.get("brand", ""))

    bottom_y = _items_table(slide, counter_no, data.get("items", [{}]*3))
    _creative_box(slide, counter_no, bottom_y)
    _embed_json(slide, counter_no, {"lm360_schema": "v1", **data})
    _footer(slide, f"360LM BTL Brief  •  Counter #{counter_no}  •  "
                   "Edit via 360LM Add-in — do not rearrange shapes")


def make_instructions_slide(prs):
    slide = prs.slides.add_slide(prs.slide_layouts[6])
    _set_bg(slide, LIGHT)
    _rect(slide, 0, 0, W, Inches(1.0), fill=NAVY)
    _txt(slide, "How to use this deck  (360LM BTL Add-in)",
         Inches(0.4), Inches(0.2), Inches(12), Inches(0.7), 24,
         bold=True, color=WHITE)

    steps = [
        ("1", "Install Add-in",
         "In PowerPoint: Insert → Get Add-ins → From File → select 360LM_BTL_Addin.xml\n"
         "(Windows desktop only. Mac/web: use this template directly.)"),
        ("2", "Fill the Cover slide",
         "Click the 360LM Branding tab → 'Edit Cover'. Enter Brand, Campaign, Date."),
        ("3", "Add a counter",
         "Click '➕ New Counter'. A form opens — fill Counter Name, City, State, Brand, then add branding items.\n"
         "Click 'Generate Slide'. A new counter slide is added automatically."),
        ("4", "Attach creatives",
         "Drag or paste each creative into the orange-bordered box on the right of each counter slide."),
        ("5", "Validate",
         "Click '✓ Validate' to check for missing sizes, blank required fields, or unrecognised item types."),
        ("6", "Send to 360LM",
         "Click '📤 Export' → 'Email to 360LM'. Your deck is sent with embedded JSON — no manual re-entry needed at 360LM's end."),
    ]

    for i, (num, title, body) in enumerate(steps):
        col = i % 3
        row = i // 3
        x = 0.4 + col * 4.3
        y = 1.2 + row * 2.8

        _rect(slide, Inches(x), Inches(y), Inches(4.0), Inches(2.5),
              fill=WHITE, line=NAVY, lw=Pt(1))
        pill = _rect(slide, Inches(x + 0.15), Inches(y + 0.15),
                     Inches(0.5), Inches(0.5), fill=ORANGE)
        _txt(slide, num, Inches(x + 0.15), Inches(y + 0.15),
             Inches(0.5), Inches(0.5), 14, bold=True, color=WHITE,
             align=PP_ALIGN.CENTER)
        _txt(slide, title, Inches(x + 0.75), Inches(y + 0.15),
             Inches(3.1), Inches(0.5), 13, bold=True, color=NAVY)
        _txt(slide, body, Inches(x + 0.15), Inches(y + 0.75),
             Inches(3.7), Inches(1.6), 9, color=DARK, wrap=True)

    _footer(slide, "360LM BTL Add-in  •  Questions? Contact 360degreemktg@gmail.com")


# ── CustomUI ribbon XML (embedded into zip) ───────────────────────────────────
RIBBON_XML = '''<?xml version="1.0" encoding="UTF-8"?>
<mso:customUI xmlns:mso="http://schemas.microsoft.com/office/2009/07/customui"
              onLoad="RibbonOnLoad">
  <mso:ribbon>
    <mso:tabs>
      <mso:tab id="tab360LM" label="360LM Branding" insertAfterMso="TabInsert">
        <mso:group id="grpCover" label="Deck Setup">
          <mso:button id="btnCover" label="Edit Cover" size="large"
            imageMso="FileDocumentProperties" onAction="EditCover"
            screentip="Edit brand, campaign and date on the cover slide"/>
        </mso:group>
        <mso:group id="grpEntry" label="Add Counter">
          <mso:button id="btnNewCounter" label="New Counter" size="large"
            imageMso="ContentControlGallery" onAction="NewCounter"
            screentip="Open form to add a new counter slide"/>
          <mso:button id="btnValidate" label="Validate" size="large"
            imageMso="ReviewMarkAsCompleted" onAction="ValidateAll"
            screentip="Check all slides for missing or invalid fields"/>
        </mso:group>
        <mso:group id="grpExport" label="Send to 360LM">
          <mso:button id="btnExportJSON" label="Export JSON" size="large"
            imageMso="ExportExcel" onAction="ExportJSON"
            screentip="Save extracted data as JSON file"/>
          <mso:button id="btnEmail" label="Email to 360LM" size="large"
            imageMso="EnvelopeInsert" onAction="EmailTo360LM"
            screentip="Attach deck and JSON to a new email addressed to 360LM"/>
        </mso:group>
      </mso:tab>
    </mso:tabs>
  </mso:ribbon>
</mso:customUI>
'''


def embed_ribbon(pptx_path):
    """Inject customUI into the PPTX zip so the ribbon tab appears."""
    tmp = pptx_path + ".tmp"
    with zipfile.ZipFile(pptx_path, 'r') as zin, \
         zipfile.ZipFile(tmp, 'w', zipfile.ZIP_DEFLATED) as zout:

        # Rewrite [Content_Types].xml to register customUI
        ct_found = False
        for item in zin.infolist():
            data = zin.read(item.filename)
            if item.filename == '[Content_Types].xml':
                ct_found = True
                xml = data.decode('utf-8')
                OVERRIDE = ('<Override PartName="/customUI/customUI14.xml" '
                            'ContentType="application/vnd.ms-office.activeX+xml"/>')
                if 'customUI14' not in xml:
                    xml = xml.replace('</Types>', OVERRIDE + '\n</Types>')
                data = xml.encode('utf-8')
            zout.writestr(item, data)

        # Add ribbon XML
        zout.writestr('customUI/customUI14.xml', RIBBON_XML.encode('utf-8'))

        # Add relationship from presentation to customUI
        # Read existing _rels/.rels
    shutil.move(tmp, pptx_path)


# ── Main ──────────────────────────────────────────────────────────────────────
def main():
    prs = Presentation()
    prs.slide_width  = W
    prs.slide_height = H

    make_instructions_slide(prs)
    make_cover(prs)

    # Sample counter 1 — KFC
    make_counter_slide(prs, 1, {
        "counter_name": "Abohar — KFC",
        "city": "Abohar", "state": "Punjab", "brand": "KFC",
        "campaign": "Summer Launch 2026",
        "items": [
            {"type": "Glow Sign Board", "material": "Acrylic + LED",
             "size": "4×2 ft", "qty": 1,
             "rem_prod": "Double-sided", "rem_inst": "Main entrance"},
            {"type": "Flex Banner", "material": "Vinyl Flex",
             "size": "6×3 ft", "qty": 2,
             "rem_prod": "Gang print OK", "rem_inst": ""},
            {"type": "Window Decal", "material": "One Way Vision",
             "size": "3×4 ft", "qty": 1,
             "rem_prod": "", "rem_inst": "Glass facade"},
        ]
    })

    # Sample counter 2 — McD
    make_counter_slide(prs, 2, {
        "counter_name": "Chandigarh — McDonald's SCO 17",
        "city": "Chandigarh", "state": "Punjab", "brand": "McDonald's",
        "campaign": "Summer Launch 2026",
        "items": [
            {"type": "Backlit Board", "material": "Flex + LED Frame",
             "size": "5×3 ft", "qty": 1,
             "rem_prod": "Translite print", "rem_inst": "Counter top"},
            {"type": "Floor Graphic", "material": "Anti-slip Vinyl",
             "size": "2×2 ft", "qty": 3,
             "rem_prod": "Laminate matte", "rem_inst": "Entrance aisle"},
        ]
    })

    # Blank counter slide (template)
    make_counter_slide(prs, "N", {
        "counter_name": "[ Counter / Store Name ]",
        "city": "[ City ]", "state": "[ State ]", "brand": "[ Brand ]",
        "campaign": "",
        "items": [{} for _ in range(4)]
    })

    prs.save(OUT_PPTX)
    embed_ribbon(OUT_PPTX)
    print(f"✓  Template saved: {OUT_PPTX}")
    size = os.path.getsize(OUT_PPTX) // 1024
    print(f"   Size: {size} KB")
    print(f"   Slides: instructions + cover + 2 sample + 1 blank = 5 slides")


if __name__ == "__main__":
    main()
