# -*- coding: utf-8 -*-
"""Generate the Summer Camps 2027 page as Gutenberg block markup.

Structures are lifted from Financial Information (#915), which is the closest
existing page in shape: heavy reassurance text organised behind accordions.
Kadence blocks keep their exact saved markup; only uniqueID and content change.
"""
import json, hashlib, itertools

PID = 39226
_c = itertools.count(1)

def uid():
    """Kadence uniqueIDs are {postID}_{6hex}-{2hex} and must be unique per block."""
    h = hashlib.md5(f"{PID}-{next(_c)}".encode()).hexdigest()
    return f"{PID}_{h[:6]}-{h[6:8]}"

def attrs(d):
    s = json.dumps(d, ensure_ascii=False, separators=(',', ':'))
    return s.replace('--', r'--')   # Kadence escapes CSS-var dashes

MARK = {
    "markBorder": "",
    "markBorderStyles":       [{"top":[None,"",""],"right":[None,"",""],"bottom":[None,"",""],"left":[None,"",""],"unit":"px"}],
    "tabletMarkBorderStyles": [{"top":[None,"",""],"right":[None,"",""],"bottom":[None,"",""],"left":[None,"",""],"unit":"px"}],
    "mobileMarkBorderStyles": [{"top":[None,"",""],"right":[None,"",""],"bottom":[None,"",""],"left":[None,"",""],"unit":"px"}],
}

def head(text, level=2, extra=None):
    u = uid()
    a = {}
    if level != 2:
        a["level"] = level
    a["uniqueID"] = u
    a.update(MARK)
    if extra:
        a.update(extra)
    # The saved HTML tag must match what Kadence's save() produces for these
    # attributes, or Gutenberg flags the block as invalid when it is next opened.
    # htmlTag wins over level; otherwise the tag follows the heading level.
    tag = a.get("htmlTag") or f"h{level}"
    return (f'<!-- wp:kadence/advancedheading {attrs(a)} -->\n'
            f'<{tag} class="kt-adv-heading{u} wp-block-kadence-advancedheading" '
            f'data-kb-block="kb-adv-heading{u}">{text}</{tag}>\n'
            f'<!-- /wp:kadence/advancedheading -->')

def p(text):
    return f'<!-- wp:paragraph -->\n<p>{text}</p>\n<!-- /wp:paragraph -->'

def h(text, level=2):
    return f'<!-- wp:heading{" " + attrs({"level":level}) if level!=2 else ""} -->\n<h{level} class="wp-block-heading">{text}</h{level}>\n<!-- /wp:heading -->'

def ul(items):
    lis = "\n\n".join(f'<!-- wp:list-item -->\n<li>{i}</li>\n<!-- /wp:list-item -->' for i in items)
    return f'<!-- wp:list -->\n<ul class="wp-block-list">{lis}</ul>\n<!-- /wp:list -->'

def col(inner, a=None):
    u = uid()
    d = {"borderWidth":["","","",""], "uniqueID":u, "kbVersion":2}
    if a: d.update(a)
    return (f'<!-- wp:kadence/column {attrs(d)} -->\n'
            f'<div class="wp-block-kadence-column kadence-column{u}"><div class="kt-inside-inner-col">{inner}</div></div>\n'
            f'<!-- /wp:kadence/column -->')

def row(cols, a=None):
    d = {"uniqueID":uid(), "columns":len(cols), "collapseGutter":"none",
         "customRowGutter":[0,"",""], "colLayout":"equal",
         "firstColumnWidth":0,"secondColumnWidth":0,"thirdColumnWidth":0,
         "fourthColumnWidth":0,"fifthColumnWidth":0,"sixthColumnWidth":0,
         "padding":["0","0","0","0"], "margin":["md","","md",""], "kbVersion":2}
    if a: d.update(a)
    return (f'<!-- wp:kadence/rowlayout {attrs(d)} -->\n' + "\n\n".join(cols) +
            f'\n<!-- /wp:kadence/rowlayout -->')

def acc_item(label, inner, full=False):
    a = {"label": label}
    if full: a["fullWidth"] = True
    return (f'<!-- wp:bw/accordion-item {attrs(a)} -->\n{inner}\n'
            f'<!-- /wp:bw/accordion-item -->')

def acc(items, a=None):
    d = {"variant":"table", "columns":2, "showChevron":True, "singleOpen":True}
    if a: d.update(a)
    return (f'<!-- wp:bw/table-accordion {attrs(d)} -->\n' + "\n\n".join(items) +
            f'\n<!-- /wp:bw/table-accordion -->')

def card(img_id, url, alt, ratio="16 / 9"):
    return ('<!-- wp:bw/card-gallery ' + attrs({
        "layout":"lightbox","imageUrl":url,"imageId":img_id,"imageAlt":alt,
        "ratio":ratio,"radius":4}) + ' /-->')

def table_link(items, cols=2):
    return '<!-- wp:bw/table-link ' + attrs({"columns":cols,"items":items}) + ' /-->'

U = "https://brentwood.demoing.info/wp-content/uploads/2026/09/"
IMG = {
 "aerial":   (39215, U+"camps-2027-campus-aerial-1.jpg",        "Aerial view of the Brentwood campus and waterfront"),
 "scuba":    (39216, U+"camps-2027-scuba-waterfront-1.jpg",     "Students in scuba gear at the Brentwood waterfront"),
 "campfire": (39217, U+"camps-2027-beach-campfire-1.jpg",       "Campers around a beach campfire at dusk"),
 "map":      (39219, U+"camps-2027-vancouver-island-map-1.jpg", "Map showing Vancouver Island in relation to Vancouver and Victoria"),
 "mun":      (39220, U+"camps-2027-model-un-delegates-1.jpg",   "Model UN delegates at a committee table"),
 "debate":   (39218, U+"camps-2027-debate-speaking-1.jpg",      "A student speaking at a Model UN committee session"),
 "boarding": (39221, U+"camps-2027-boarding-house-1.jpg",       "A Brentwood boarding house room"),
 "dining":   (39222, U+"camps-2027-outdoor-dining-1.jpg",       "Students eating outdoors overlooking the inlet"),
 "forest":   (39223, U+"camps-2027-oldgrowth-forest-1.jpg",     "Walking through old-growth forest on Vancouver Island"),
 "sealion":  (39224, U+"camps-2027-sea-lion-1.jpg",             "A sea lion on the Brentwood waterfront"),
 "kayak":    (39225, U+"camps-2027-sea-kayaking-1.jpg",         "Sea kayaking the Saanich Inlet"),
}
def C(k, ratio="16 / 9"):
    i, u, a = IMG[k]; return card(i, u, a, ratio)

OVERNIGHT_FORM = "https://docs.google.com/forms/d/e/1FAIpQLScPCS1vOWk1VtZjhUJ1-E9H985B9eI2EQa8TyW7t1nTuXUZtw/viewform"
DAY_FORM       = "https://docs.google.com/forms/d/e/1FAIpQLSezydEizKMI0nwnkKuYaTd9-d2OPfFoUf1uDqi4uVoYTBzLuw/viewform"

B = []

# ---------- Hero card -------------------------------------------------------
B.append(
 '<!-- wp:brentwood/hero-card -->\n' +
 head('Brentwood&rsquo;s campus sits on ninety acres of Vancouver Island coastline, with a '
      'kilometre of waterfront looking out to the Gulf Islands. In the summer of 2027 it opens '
      'to young people from around the world for the first time, for camps that run day and '
      'night rather than nine to five.<br><br>Two overnight camps, two day camps, and a coastline '
      'that does most of the teaching.', level=2, extra={"htmlTag":"p"}) +
 '\n<!-- /wp:brentwood/hero-card -->')

# ---------- Choose your route ----------------------------------------------
B.append(row([col(
    head('Two ways to spend the summer here') + "\n\n" +
    p('Our <strong>overnight camps</strong> are new for 2027. Campers live in a Brentwood '
      'boarding house for a week or two, with meals, excursions and evenings on campus included. '
      'Our <strong>day camps</strong> are the programmes we have run for years, running weekdays '
      'through July.') + "\n\n" +
    table_link([
      {"label":"Overnight Camps","url":"#overnight-camps","fullWidth":False},
      {"label":"Day Camps","url":"#day-camps","fullWidth":False}])
)]))

# ---------- OVERNIGHT -------------------------------------------------------
B.append(row([col(
    head('<span id="overnight-camps"></span>Overnight Camps') + "\n\n" +
    p('Two camps, each running as a single week or as a full fortnight. Campers live on campus '
      'in a Brentwood boarding house, eat in the dining hall, and spend their evenings and '
      'weekends with the other campers.') + "\n\n" +
    ul(['<strong>Ages</strong> 13 to 17',
        '<strong>Week 1</strong> Saturday 27 June to Friday 3 July 2027',
        '<strong>Week 2</strong> Saturday 4 July to Friday 10 July 2027',
        '<strong>Take one week or both</strong> &mdash; each week has its own focus',
        '<strong>English</strong> conversational, fluent or advanced',
        '<strong>Registration opens</strong> 1 December 2026'])
)], {"bgColor":"palette9","bgColorClass":"theme-palette9",
     "padding":["md","xxs","md","xxs"], "bwShadow":True, "bwRadius":True}))

B.append(row([col(C("kayak", "4 / 5")), col(C("mun", "4 / 5"))]))

B.append(row([col(
    head('Choose your camp', level=3) + "\n\n" +
    p('Select a camp to see what each week covers.') + "\n\n" +
    acc([
      acc_item('Marine Sciences Camp',
        h('Marine Sciences Camp', 3) + "\n\n" +
        p('<em>Ages 13 to 17. Available as one week or two.</em>') + "\n\n" +
        h('Week 1 &mdash; Coastal Systems &amp; Marine Life', 4) + "\n\n" +
        p('<em>27 June to 3 July 2027</em>') + "\n\n" +
        p('Turn the rugged Pacific shoreline into your personal laboratory. Unleash your inner '
          'explorer by sea-kayaking hidden inlets and diving headfirst into marine biology field '
          'studies with peers from around the globe.') + "\n\n" +
        h('Week 2 &mdash; Boats, Whales &amp; Whole Ecosystems', 4) + "\n\n" +
        p('<em>4 July to 10 July 2027</em>') + "\n\n" +
        p('Step into Brentwood&rsquo;s high-tech, West Coast labs. Take your skills further by '
          'conducting advanced marine research alongside experts and transforming your coastal '
          'discoveries into real scientific insight.') + "\n\n" +
        C("scuba", "3 / 2")),
      acc_item('Model UN &amp; Debate Camp',
        h('Model UN &amp; Debate Camp', 3) + "\n\n" +
        p('<em>Ages 13 to 17. Available as one week or two.</em>') + "\n\n" +
        h('Week 1 &mdash; The Art of Persuasion', 4) + "\n\n" +
        p('<em>27 June to 3 July 2027</em>') + "\n\n" +
        p('Master public speaking and ironclad argumentation with techniques used to coach '
          'world-class debaters. Students build elite skills and prepare to compete for the '
          'Camp Cup.') + "\n\n" +
        h('Week 2 &mdash; Global Diplomacy', 4) + "\n\n" +
        p('<em>4 July to 10 July 2027</em>') + "\n\n" +
        p('Step into the shoes of international delegates to negotiate treaties and solve global '
          'crises. Modelled on Brentwood&rsquo;s award-winning Model UN program, this camp offers '
          'tailored challenges for all skill levels while building empathy, listening, and '
          'eloquence.') + "\n\n" +
        C("debate", "3 / 2")),
    ], {"variant":"grid","columns":2})
)]))

# ---------- Planning your stay ---------------------------------------------
B.append(row([col(
    head('Planning the trip', level=3) + "\n\n" +
    p('Everything families usually want to know before they book. Select a heading to open it.') + "\n\n" +
    acc([
      acc_item('Fees and what is included',
        h('Fees', 3) + "\n\n" +
        p('Prices are in Canadian dollars and include tax.') + "\n\n" +
        p('<strong>Marine Sciences</strong>') + "\n\n" +
        ul(['One week &mdash; CAD $2,095', 'Two weeks &mdash; CAD $3,985']) + "\n\n" +
        p('<strong>Model UN &amp; Debate</strong>') + "\n\n" +
        ul(['One week &mdash; CAD $1,990', 'Two weeks &mdash; CAD $3,880']) + "\n\n" +
        h('Included', 3) + "\n\n" +
        ul(['Shared accommodation in a Brentwood boarding house',
            'All meals and snacks on campus',
            'Programme fees and excursions',
            'Coin-operated laundry in the boarding house',
            'Wi-Fi access',
            'Scheduled shuttle from Victoria International Airport (YYJ) or the Swartz Bay ferry terminal']) + "\n\n" +
        h('Not included', 3) + "\n\n" +
        ul(['Travel to Vancouver Island',
            'Travel documents',
            'Medical and travel insurance',
            'Spending money &mdash; we suggest CAD $300 to $500'])),
      acc_item('Getting to Brentwood',
        h('Getting here', 3) + "\n\n" +
        p('Campers arrange their own travel to Vancouver Island. Once you are here, a '
          'complimentary scheduled shuttle runs to campus from <strong>Victoria International '
          'Airport (YYJ)</strong> and the <strong>Swartz Bay ferry terminal</strong>.') + "\n\n" +
        p('<strong>Please note that Vancouver Island is not in the city of Vancouver.</strong> '
          'Flights and ferries to Vancouver do not arrive on the Island, so check your route '
          'carefully before booking.') + "\n\n" +
        C("map", "3 / 4")),
      acc_item('Where you will stay',
        h('Accommodation', 3) + "\n\n" +
        p('Campers stay in a Brentwood boarding house, the same houses our students live in '
          'during the school year. Each room has a twin XL bed, occasionally in a bunk '
          'configuration, with a desk and chair for every camper. Towels and bedding are provided.') + "\n\n" +
        p('Houses have Wi-Fi, shared kitchen areas, flatscreens and common areas for spending '
          'time with friends. Coin-operated laundry is available at any time.') + "\n\n" +
        C("boarding", "3 / 2") + "\n\n" +
        h('Meals', 3) + "\n\n" +
        p('All meals and snacks on campus are included, served in the dining hall.') + "\n\n" +
        C("dining", "3 / 2")),
      acc_item('Entry requirements and travel documents',
        h('What campers need', 3) + "\n\n" +
        ul(['Conversational English',
            'Travel arrangements to Vancouver Island',
            'Any travel documents required to enter Canada']) + "\n\n" +
        h('Study permits and visas', 3) + "\n\n" +
        p('International students must make sure they hold the travel documents required to '
          'enter Canada. Most will need either a temporary resident visa or an electronic travel '
          'authorization.') + "\n\n" +
        p('<strong>Processing times are currently longer than usual, so apply early.</strong> '
          'To find out which document you need, see the Government of Canada travel site at '
          '<a href="https://travel.gc.ca/" target="_blank" rel="noreferrer noopener">travel.gc.ca</a>.')),
      acc_item('Paying for camp',
        h('Payment', 3) + "\n\n" +
        p('International families can pay through Flywire, which handles currency conversion '
          'and local payment methods.') + "\n\n" +
        table_link([{"label":"Flywire payment instructions (PDF)",
                     "url":"https://drive.google.com/file/d/164koPAeHDIPJGDZ-7zDlnW4TENZxTD4C/view",
                     "fullWidth":True}], 1)),
      acc_item('Key dates',
        h('Key dates', 3) + "\n\n" +
        ul(['<strong>1 December 2026</strong> &mdash; registration opens',
            '<strong>27 June to 3 July 2027</strong> &mdash; Week 1',
            '<strong>4 July to 10 July 2027</strong> &mdash; Week 2']) + "\n\n" +
        p('Further detail on refunds, what to pack and the camper code of conduct will be '
          'published here before registration opens.')),
    ], {"variant":"table","columns":2})
)]))

B.append(row([col(
    head('Register your interest', level=3) + "\n\n" +
    p('Registration opens on 1 December 2026. Tell us you are interested and we will be in touch '
      'as soon as places are available.') + "\n\n" +
    table_link([{"label":"Overnight camp enquiry form","url":OVERNIGHT_FORM,"fullWidth":True}], 1)
)], {"bgColor":"palette9","bgColorClass":"theme-palette9",
     "padding":["md","xxs","md","xxs"], "bwShadow":True, "bwRadius":True}))

# ---------- DAY CAMPS -------------------------------------------------------
B.append(row([col(C("campfire", "21 / 9"))]))

B.append(row([col(
    head('<span id="day-camps"></span>Day Camps') + "\n\n" +
    p('Our day camps run on weekdays through July, on the same campus and waterfront. Campers go '
      'home at the end of each day.') + "\n\n" +
    acc([
      acc_item('Junior Theatre',
        h('Junior Theatre', 3) + "\n\n" + p('<em>4 to 9 July 2027</em>') + "\n\n" +
        p('A week of performance for younger campers, working towards a production at the end '
          'of the week in the Killy Theatre.')),
      acc_item('Senior Theatre',
        h('Senior Theatre', 3) + "\n\n" + p('<em>11 to 16 July 2027</em>') + "\n\n" +
        p('A week of performance for older campers, working towards a production at the end of '
          'the week in the Killy Theatre.')),
      acc_item('Climbing Camp &mdash; July 5 to 9',
        h('Climbing Camp', 3) + "\n\n" + p('<em>5 to 9 July 2027</em>') + "\n\n" +
        p('A week on the climbing wall and outdoors, building technique and confidence at every '
          'level from first-timer upwards.')),
      acc_item('Climbing Camp &mdash; July 12 to 16',
        h('Climbing Camp', 3) + "\n\n" + p('<em>12 to 16 July 2027</em>') + "\n\n" +
        p('A second week of climbing, running the same programme as the week before.')),
    ], {"variant":"grid","columns":2}) + "\n\n" +
    p('Pricing, instructors and the season&rsquo;s productions will be confirmed closer to '
      'registration.') + "\n\n" +
    table_link([{"label":"Day camp enquiry form","url":DAY_FORM,"fullWidth":True}], 1)
)]))

# ---------- Contact ---------------------------------------------------------
B.append(row([col(
    head('Questions', level=3) + "\n\n" +
    p('For anything not covered here, contact Stacey Shrout, Auxiliary Programming Manager, at '
      '<a href="mailto:stacey.shrout@brentwood.ca">stacey.shrout@brentwood.ca</a>.')
)]))

open('/tmp/camps-content.html', 'w').write("\n\n".join(B))
print("blocks generated, bytes:", len("\n\n".join(B)))
