// sidebar.jsx — left rail: add-blocks palette + holiday/cruise search

const BLOCK_TYPES = ["header", "offer", "cruise", "text", "image", "video", "footer"];

const CRUISE_API = 'https://tms-cruises-api.tms-travel-marketing-systems.workers.dev';

const UK_AIRPORTS = [
  'BHX','BRS','EDI','GLA','LBA','LGW','LHR','LPL','MAN','NCL',
  'SEN','STN','EMA','ABZ','BFS','ORK','DUB','INV','DSA','EXT',
  'HUY','LCY','LTN','MME','NWI','PIK','SOU','CWL','BOH','JER','GCI',
];

const CRUISE_TYPES = [
  { value: 'ocean', label: 'Ocean' },
  { value: 'river', label: 'River' },
  { value: 'tour',  label: 'Tour' },
];

// Formats offer price. Falls back to departureAirports[0].price when offer.price is empty.
function formatPrice(offer) {
  const rawPrice = offer.price || offer.departureAirports?.[0]?.price || '';
  if (!rawPrice) return '';
  // If already formatted (contains £ or 'pp'), return as-is to avoid double-formatting.
  if (rawPrice.includes('£') || rawPrice.toLowerCase().includes('pp')) return rawPrice;
  const num = parseFloat(rawPrice);
  if (isNaN(num)) return rawPrice;
  const formatted = '£' + Math.round(num).toLocaleString('en-GB');
  const cat = (offer.priceCategory || '').toLowerCase();
  return (cat.includes('pp') || cat.includes('per person')) ? formatted + 'pp' : formatted;
}

// Derives availability text from offer.bookingPeriodEnd (ISO date string) if present.
function formatBookBy(offer) {
  if (offer.bookingPeriodEnd) {
    // Force midnight local time to avoid UTC-boundary day-shift.
    const d = new Date(offer.bookingPeriodEnd + 'T00:00:00');
    if (!isNaN(d)) {
      return 'Book by ' + d.toLocaleDateString('en-GB', { day: 'numeric', month: 'long', year: 'numeric' });
    }
  }
  return 'Limited availability';
}

function CruisesTab({ onAddCruise }) {
  const [q, setQ] = React.useState('');
  const [ship, setShip] = React.useState('');
  const [cruiseType, setCruiseType] = React.useState('ocean');
  const [operatorId, setOperatorId] = React.useState('');
  const [region, setRegion] = React.useState('');
  const [monthKey, setMonthKey] = React.useState('');
  const [operators, setOperators] = React.useState([]);
  const [regions, setRegions] = React.useState([]);
  const [results, setResults] = React.useState(null);
  const [loading, setLoading] = React.useState(false);
  const [error, setError] = React.useState(null);
  const debounceRef = React.useRef(null);

  const monthOptions = React.useMemo(() => {
    const months = [];
    const now = new Date();
    for (let i = 0; i < 18; i++) {
      const d = new Date(now.getFullYear(), now.getMonth() + i, 1);
      months.push({
        label: d.toLocaleString('en-GB', { month: 'long', year: 'numeric' }),
        month: String(d.getMonth() + 1).padStart(2, '0'),
        year:  String(d.getFullYear()),
      });
    }
    return months;
  }, []);

  // Reload operators + regions when cruiseType changes
  React.useEffect(() => {
    setOperatorId('');
    setRegion('');
    Promise.all([
      fetch(`${CRUISE_API}/operators?type=${cruiseType}`).then(r => r.json()),
      fetch(`${CRUISE_API}/regions?type=${cruiseType}`).then(r => r.json()),
    ]).then(([opData, regData]) => {
      setOperators(opData.operators || []);
      setRegions(regData.regions || []);
    }).catch(() => {});
  }, [cruiseType]);

  const doSearch = React.useCallback(async (cQ, cShip, cT, cO, cR, cMonthKey) => {
    setLoading(true);
    setError(null);
    try {
      const params = new URLSearchParams({ limit: '20', sort: 'date' });
      const combinedQ = [cQ, cShip].filter(Boolean).join(' ');
      if (combinedQ) params.set('q', combinedQ);
      if (cT) params.set('type', cT);
      if (cO) params.set('operator_id', cO);
      if (cR) params.set('region', cR);
      if (cMonthKey) {
        const [mm, yyyy] = cMonthKey.split('/');
        params.set('month', mm);
        params.set('year', yyyy);
      }
      const url = `${CRUISE_API}/search?${params}`;
      console.log('[TMS cruise search]', url);
      const res = await fetch(url);
      if (!res.ok) throw new Error(res.status);
      const data = await res.json();
      setResults(data.cruises || data.results || []);
    } catch (e) {
      setError('Could not load cruises — check your connection');
      setResults(null);
    } finally {
      setLoading(false);
    }
  }, []);

  // Debounce search on any filter change
  React.useEffect(() => {
    clearTimeout(debounceRef.current);
    debounceRef.current = setTimeout(() => doSearch(q, ship, cruiseType, operatorId, region, monthKey), 300);
    return () => clearTimeout(debounceRef.current);
  }, [q, ship, cruiseType, operatorId, region, monthKey, doSearch]);

  const hasFilters = cruiseType !== 'ocean' || operatorId !== '' || region !== '' || ship !== '' || monthKey !== '';
  const clearFilters = () => { setCruiseType('ocean'); setOperatorId(''); setRegion(''); setShip(''); setMonthKey(''); };

  const fmtDate = (iso) => {
    if (!iso) return '';
    const d = new Date(iso + 'T00:00:00');
    return isNaN(d) ? iso : d.toLocaleDateString('en-GB', { day: 'numeric', month: 'short', year: 'numeric' });
  };

  const fmtPrice = (c) => c.display_price
    ? 'From £' + Math.round(c.display_price).toLocaleString('en-GB') + 'pp' : '';

  const normalizeUrl = (url) => {
    if (!url) return '';
    // Widgety returns protocol-relative URLs (//widgety.co.uk/...) — add https: for Cloudinary
    return url.startsWith('//') ? 'https:' + url : url;
  };

  const handleAdd = (c) => {
    console.log('cover_image_url raw:', c.cover_image_url);
    const rawImg = normalizeUrl(c.cover_image_url);
    onAddCruise({
      cruiseName:    c.name,
      operatorLine:  [c.operator_name, c.ship_name].filter(Boolean).join(' · '),
      departureDate: fmtDate(c.departure_date),
      duration:      c.duration_nights ? String(c.duration_nights) : '',
      departurePort: c.departure_port || '',
      regions:       Array.isArray(c.regions) ? c.regions.join(', ') : (c.regions || ''),
      price:         fmtPrice(c),
      imageUrl:      rawImg,
      ctaUrl:        `https://cruise-search.pages.dev/detail?id=${c.id}`,
      teaser:        '',
      promoBadge:    c.has_promo ? (c.promo_headline || 'Special offer') : '',
      cruiseType:    c.cruise_type || 'ocean',
      _id:           c.id,
    });
  };

  return (
    <div className="bld-search">
      <div className="bld-filters" style={{ gridTemplateColumns: '1fr' }}>
        <input className="bld-input" placeholder="Destination or cruise line…"
               value={q} onChange={(e) => setQ(e.target.value)} />
      </div>
      <div className="bld-filters">
        <select className="bld-select" value={cruiseType} onChange={(e) => setCruiseType(e.target.value)}>
          {CRUISE_TYPES.map(t => <option key={t.value} value={t.value}>{t.label}</option>)}
        </select>
        <select className="bld-select" value={operatorId} onChange={(e) => setOperatorId(e.target.value)}>
          <option value="">Any operator</option>
          {operators.map(o => <option key={o.id} value={String(o.id)}>{o.name}</option>)}
        </select>
        <select className="bld-select" value={region} onChange={(e) => setRegion(e.target.value)}>
          <option value="">Any region</option>
          {regions.map(r => <option key={r}>{r}</option>)}
        </select>
        <select className="bld-select" value={monthKey} onChange={(e) => setMonthKey(e.target.value)}>
          <option value="">Any month</option>
          {monthOptions.map(m => (
            <option key={`${m.month}/${m.year}`} value={`${m.month}/${m.year}`}>{m.label}</option>
          ))}
        </select>
        <input className="bld-input" placeholder="Ship name…" value={ship}
               onChange={(e) => setShip(e.target.value)}
               style={{ gridColumn: '1 / -1' }} />
      </div>
      {hasFilters && (
        <div style={{ padding: '2px 0 4px', textAlign: 'right' }}>
          <button onClick={clearFilters}
                  style={{ background: 'none', border: 'none', padding: 0, fontSize: 11, color: 'var(--product-fg-muted)', cursor: 'pointer', textDecoration: 'underline' }}>
            Clear filters
          </button>
        </div>
      )}
      {loading ? (
        <p className="bld-search-empty">Loading cruises…</p>
      ) : error ? (
        <p className="bld-search-empty" style={{ color: 'var(--tms-red-500)' }}>{error}</p>
      ) : results === null ? (
        <p className="bld-search-empty">Browse sailings by destination, cruise line or region.</p>
      ) : results.length === 0 ? (
        <p className="bld-search-empty">No cruises match. Try widening your filters.</p>
      ) : (
        <>
          <div className="bld-search-meta"><span>{results.length} cruise{results.length === 1 ? '' : 's'}</span><span>Tap to add</span></div>
          <div className="bld-results">
            {results.map((c, i) => (
              <button key={c.id || i} className="bld-result bld-result--cruise" onClick={() => handleAdd(c)}>
                {c.cover_image_url && (
                  <img className="bld-result-thumb" src={normalizeUrl(c.cover_image_url)} alt={c.name} />
                )}
                <div className="bld-result-body">
                  <span className="bld-result-top">
                    <span className="bld-result-dest">{c.name}</span>
                    {fmtPrice(c) && <span className="bld-result-price">{fmtPrice(c)}</span>}
                  </span>
                  <span className="bld-result-sub">
                    {[c.operator_name, fmtDate(c.departure_date), c.duration_nights && c.duration_nights + ' nts'].filter(Boolean).join(' · ')}
                  </span>
                  {c.has_promo && (
                    <span className="bld-result-badge bld-result-badge--promo">{c.promo_headline || 'Special offer'}</span>
                  )}
                </div>
              </button>
            ))}
          </div>
        </>
      )}
    </div>
  );
}

function TemplatesTab({ onLoadTemplate }) {
  const cfg       = window.TMS_CONFIG || {};
  const workerUrl = (cfg.workerUrl || 'https://tms-promotions-api.tms-travel-marketing-systems.workers.dev').replace(/\/$/, '');
  const token     = cfg.agentToken || '';

  const [subTab, setSubTab]         = React.useState('library');
  const [categories, setCategories] = React.useState([]);
  const [categoryId, setCategoryId] = React.useState('');
  const [templates, setTemplates]   = React.useState(null);
  const [loading, setLoading]       = React.useState(false);
  const [using, setUsing]           = React.useState(null);

  React.useEffect(() => {
    fetch(`${workerUrl}/email-builder/categories`)
      .then(r => r.json())
      .then(d => { if (Array.isArray(d)) setCategories(d); })
      .catch(() => {});
  }, [workerUrl]);

  React.useEffect(() => {
    setLoading(true);
    setTemplates(null);
    const endpoint = subTab === 'library'
      ? `/email-builder/templates/library`
      : `/email-builder/templates/mine`;
    const url = categoryId
      ? `${workerUrl}${endpoint}?category_id=${encodeURIComponent(categoryId)}`
      : `${workerUrl}${endpoint}`;
    const headers = token ? { 'Authorization': `Bearer ${token}` } : {};
    fetch(url, { headers })
      .then(r => r.json())
      .then(d => { if (Array.isArray(d)) setTemplates(d); else setTemplates([]); })
      .catch(() => setTemplates([]))
      .finally(() => setLoading(false));
  }, [subTab, categoryId, workerUrl, token]);

  const handleUse = async (id) => {
    if (using) return;
    setUsing(id);
    try {
      const res = await fetch(`${workerUrl}/email-builder/templates/${id}`, {
        headers: token ? { 'Authorization': `Bearer ${token}` } : {},
      });
      const data = await res.json();
      if (data.blocks) onLoadTemplate(data.blocks);
    } catch (_) {}
    setUsing(null);
  };

  return (
    <div className="bld-search">
      <div style={{ display: 'flex', gap: 6, marginBottom: 8 }}>
        {['library', 'mine'].map(s => (
          <button key={s} onClick={() => setSubTab(s)} style={{
            flex: 1, padding: '5px 0', border: 'none', borderRadius: 6, cursor: 'pointer',
            fontSize: 11.5, fontWeight: 600, fontFamily: 'var(--font-display)',
            background: subTab === s ? 'var(--tms-teal-600)' : 'var(--product-surface-2)',
            color: subTab === s ? '#fff' : 'var(--product-fg-muted)',
          }}>
            {s === 'library' ? 'Library' : 'My Templates'}
          </button>
        ))}
      </div>
      <select className="bld-select" value={categoryId} onChange={e => setCategoryId(e.target.value)}
              style={{ width: '100%', marginBottom: 8 }}>
        <option value="">All categories</option>
        {categories.map(c => <option key={c.id} value={c.id}>{c.name}</option>)}
      </select>
      {loading ? (
        <p className="bld-search-empty">Loading templates…</p>
      ) : !templates || templates.length === 0 ? (
        <p className="bld-search-empty">
          {subTab === 'library'
            ? 'No library templates yet — check back soon.'
            : 'No saved templates. Save your current design as a template from the menu.'}
        </p>
      ) : (
        <div className="bld-results">
          {templates.map(t => (
            <button key={t.id} className="bld-result" onClick={() => handleUse(t.id)}
                    disabled={using === t.id}
                    style={{ flexDirection: 'column', alignItems: 'flex-start', gap: 4, padding: '10px 12px' }}>
              <span style={{ fontWeight: 600, fontSize: 13, color: 'var(--product-fg)' }}>{t.name}</span>
              {t.category_id && categories.find(c => c.id === t.category_id) && (
                <span style={{ fontSize: 11, color: 'var(--product-fg-muted)' }}>
                  {categories.find(c => c.id === t.category_id).name}
                </span>
              )}
              <span style={{ fontSize: 11, color: 'var(--tms-teal-600)', fontWeight: 600, marginTop: 2 }}>
                {using === t.id ? 'Loading…' : 'Use template →'}
              </span>
            </button>
          ))}
        </div>
      )}
    </div>
  );
}

function Sidebar({ onAddBlock, onAddOffer, onAddCruise, onLoadTemplate, onCloneOperatorTemplate, section, authReady }) {
  const { Search, Lock } = window.Icons;
  const [tab, setTab] = React.useState('holidays');
  const [q, setQ] = React.useState("");
  const [airport, setAirport] = React.useState("Any airport");
  const [operator, setOperator] = React.useState("Any operator");
  const [date, setDate] = React.useState("Any date");
  const [allOffers, setAllOffers] = React.useState(null);
  const [loading, setLoading] = React.useState(false);
  const [error, setError] = React.useState(null);

  const cruisesEnabled = (window.TMS_CONFIG?.features?.cruises) !== false;

  // Once role is verified switch to the appropriate default tab
  React.useEffect(() => {
    if (!authReady) return;
    const role = (window.TMS_CONFIG || {}).role;
    if (role === 'operator') setTab('op-offers');
  }, [authReady]); // eslint-disable-line

  const cfg           = window.TMS_CONFIG || {};
  const isOperator    = cfg.role === 'operator';
  const entitledBrands = Array.isArray(cfg.entitled_operator_brands) ? cfg.entitled_operator_brands : [];
  const hasOpTemplates = !isOperator && entitledBrands.length > 0;

  React.useEffect(() => {
    if (tab === 'holidays' && allOffers === null) runSearch();
  }, [tab]); // eslint-disable-line

  const airportOptions = React.useMemo(() => {
    if (!allOffers) return [];
    const seen = new Map();
    allOffers.forEach(o => {
      (o.departureAirports || []).forEach(a => {
        if (UK_AIRPORTS.includes(a.type) && !seen.has(a.type)) {
          seen.set(a.type, a.airport);
        }
      });
    });
    return [...seen.entries()]
      .sort((a, b) => a[1].localeCompare(b[1]))
      .map(([type, airport]) => ({ type, airport }));
  }, [allOffers]);

  const operators = React.useMemo(() => {
    if (!allOffers) return ["Any operator"];
    const vals = [...new Set(allOffers.map(o => o.operator).filter(Boolean))].sort();
    return ["Any operator", ...vals];
  }, [allOffers]);

  const dates = React.useMemo(() => {
    if (!allOffers) return ["Any date"];
    const vals = [...new Set(allOffers.map(o => {
      if (!o.holidayStartDate) return null;
      const d = new Date(o.holidayStartDate + 'T00:00:00');
      if (isNaN(d)) return null;
      return d.toLocaleDateString('en-GB', { month: 'short', year: 'numeric' });
    }).filter(Boolean))].sort();
    return ["Any date", ...vals];
  }, [allOffers]);

  const results = React.useMemo(() => {
    if (!allOffers) return null;
    const ql = q.trim().toLowerCase();
    return allOffers.filter(o => {
      if (ql && ![(o.destination || ''), (o.name || ''), (o.operator || ''), (o.title || '')].some(s => s.toLowerCase().includes(ql))) return false;
      if (operator !== "Any operator" && (o.operator || '') !== operator) return false;
      // Airport filter: match against any departure airport, not just [0].
      if (airport !== "Any airport" && !(o.departureAirports || []).some(d => d.airport === airport)) return false;
      if (date !== "Any date") {
        if (!o.holidayStartDate) return false;
        const d = new Date(o.holidayStartDate + 'T00:00:00');
        if (isNaN(d)) return false;
        if (d.toLocaleDateString('en-GB', { month: 'short', year: 'numeric' }) !== date) return false;
      }
      return true;
    });
  }, [allOffers, q, airport, operator, date]);

  const runSearch = async () => {
    const cfg = window.TMS_CONFIG || {};
    const siteId = cfg.siteId || '';
    const offersUrl = (cfg.offersUrl || 'https://tms-offers.tms-travel-marketing-systems.workers.dev').replace(/\/$/, '');
    if (!siteId) {
      setError("No siteId configured — set TMS_CONFIG.siteId");
      setAllOffers(null);
      return;
    }
    setLoading(true);
    setError(null);
    try {
      const res = await fetch(`${offersUrl}/offers?siteId=${encodeURIComponent(siteId)}`);
      if (!res.ok) {
        const text = await res.text();
        console.warn('[TMS offer search] Worker error', res.status, text);
        throw new Error(`${res.status}`);
      }
      const data = await res.json();
      const offers = Array.isArray(data.offers) ? data.offers : Array.isArray(data) ? data : [];
      console.log('[TMS offer search] raw response', {
        source: data.source,
        fetchedAt: data.fetchedAt,
        count: offers.length,
        firstOffer: offers[0] || null,
      });
      if (offers.length === 0) {
        console.warn('[TMS offer search] Worker returned 0 offers for siteId:', siteId, '— full response:', data);
      }
      setAllOffers(offers);
    } catch (e) {
      setError("Could not load offers — check your connection");
      setAllOffers(null);
    } finally {
      setLoading(false);
    }
  };

  // Maps a raw Offer (from Worker) to the shape addOffer() in app.jsx expects.
  const mapOffer = (o) => {
    const cfg = window.TMS_CONFIG || {};
    const ctaUrl = o.extra?.url
      || o.ctaUrl
      || (cfg.defaultOfferUrl
          ? `${cfg.defaultOfferUrl}/${o.id}`
          : `https://www.screen.latecards.co.uk/offer-detail-page?id=${encodeURIComponent(o.id)}`);
    return {
      dest:         o.destination || o.name,
      title:        o.title || '',
      price:        formatPrice(o),
      badge:        o.flightIncluded !== false ? 'ATOL Protected' : 'ABTA',
      op:           o.operator || '',
      airport:      o.departureAirports?.[0]?.airport || '',
      nights:       o.nights || '',
      board:        o.board || '',
      imageUrl:     o.imageUrl || '',
      ctaUrl,
      availability: formatBookBy(o),
    };
  };

  return (
    <aside className="bld-sidebar">
      {section !== 'search' && (<>
        <div className="bld-side-head">Add blocks</div>
        <div className="bld-palette">
          {BLOCK_TYPES.map((t) => {
            const Ic = window.Icons[window.TYPE_META[t].label];
            return (
              <button key={t} className="bld-palette-btn" onClick={() => onAddBlock(t)}>
                <span className="bld-palette-ic"><Ic /></span>
                {window.TYPE_META[t].label}
              </button>
            );
          })}
        </div>
        <div className="bld-divider" />
      </>)}

      {section !== 'blocks' && (<>
        <div id="tour-sidebar-tabs" className="bld-tabs">
          {isOperator ? (<>
            <button className={"bld-tab" + (tab === 'op-offers' ? " is-active" : "")}
                    onClick={() => setTab('op-offers')}>Our Offers</button>
            <button className={"bld-tab" + (tab === 'op-library' ? " is-active" : "")}
                    onClick={() => setTab('op-library')}>My Templates</button>
          </>) : (<>
            <button className={"bld-tab" + (tab === 'holidays' ? " is-active" : "")}
                    onClick={() => setTab('holidays')}>Holidays</button>
            <button className={"bld-tab" + (tab === 'cruises' ? " is-active" : "")}
                    onClick={() => cruisesEnabled && setTab('cruises')}
                    disabled={!cruisesEnabled}
                    title={!cruisesEnabled ? 'Cruise search — contact your account manager to enable' : ''}>
              {!cruisesEnabled && <Lock />}Cruises
            </button>
            {hasOpTemplates && (
              <button className={"bld-tab" + (tab === 'op-templates' ? " is-active" : "")}
                      onClick={() => setTab('op-templates')}>Operator</button>
            )}
          </>)}
          <button className={"bld-tab" + (tab === 'templates' ? " is-active" : "")}
                  onClick={() => setTab('templates')}>Templates</button>
        </div>
      </>)}

      {section !== 'blocks' && tab === 'holidays' && (
        <div className="bld-search">
          <div className="bld-search-head">Offer search</div>
          <div className="bld-search-row">
            <input className="bld-input" placeholder="Destination or operator…" value={q}
                   onChange={(e) => setQ(e.target.value)} />
            <button className="bld-search-go" title="Search offers" onClick={runSearch} disabled={loading}>
              {loading ? <span style={{ fontSize: 11, lineHeight: 1, letterSpacing: 1 }}>...</span> : <Search />}
            </button>
          </div>
          <div className="bld-filters">
            <select className="bld-select" value={airport} onChange={(e) => setAirport(e.target.value)}>
              <option value="Any airport">Any airport</option>
              {airportOptions.map(({ type, airport: name }) => (
                <option key={type} value={name}>{name}</option>
              ))}
            </select>
            <select className="bld-select" value={operator} onChange={(e) => setOperator(e.target.value)}>
              {operators.map((o) => <option key={o}>{o}</option>)}
            </select>
            <select className="bld-select" value={date} onChange={(e) => setDate(e.target.value)} style={{ gridColumn: "1 / -1" }}>
              {dates.map((d) => <option key={d}>{d}</option>)}
            </select>
          </div>

          {error ? (
            <p className="bld-search-empty" style={{ color: 'var(--tms-red-500)' }}>{error}</p>
          ) : results === null ? (
            <p className="bld-search-empty">Search live availability from your agent account — by destination, airport, operator or date.</p>
          ) : results.length === 0 ? (
            <p className="bld-search-empty">No offers match. Try widening your filters.</p>
          ) : (
            <>
              <div className="bld-search-meta"><span>{results.length} offer{results.length === 1 ? "" : "s"}</span><span>Tap to add</span></div>
              <div className="bld-results">
                {results.map((o, i) => {
                  const thumbUrl = (o.imageUrl || '').replace('http://', 'https://');
                  return (
                    <button key={i} className="bld-result bld-result--cruise" onClick={() => onAddOffer(mapOffer(o))}>
                      <span className="bld-result-thumb" style={{ flexShrink: 0, background: '#0A6E7E', display: 'flex', alignItems: 'center', justifyContent: 'center', overflow: 'hidden' }}>
                        {thumbUrl
                          ? <img src={thumbUrl} alt="" width="48" height="48"
                                 style={{ width: '100%', height: '100%', objectFit: 'cover', display: 'block' }}
                                 onError={(e) => { e.currentTarget.style.display = 'none'; }} />
                          : <span style={{ color: '#C5EAEE', fontSize: 16 }}>&#9992;</span>
                        }
                      </span>
                      <div className="bld-result-body">
                        <span className="bld-result-top">
                          <span className="bld-result-dest">{o.destination || o.name}</span>
                          <span className="bld-result-price">{formatPrice(o)}</span>
                        </span>
                        <span className="bld-result-sub">
                          {[o.title, o.nights && `${o.nights} nts`, o.board, o.operator, o.departureAirports?.[0]?.airport].filter(Boolean).join(' · ')}
                        </span>
                        <span className="bld-result-badge">{o.flightIncluded !== false ? 'ATOL Protected' : 'ABTA'}</span>
                      </div>
                    </button>
                  );
                })}
              </div>
            </>
          )}
        </div>
      )}

      {section !== 'blocks' && tab === 'cruises' && cruisesEnabled && (
        <CruisesTab onAddCruise={onAddCruise} />
      )}

      {section !== 'blocks' && tab === 'templates' && (
        <TemplatesTab onLoadTemplate={onLoadTemplate} />
      )}
      {section !== 'blocks' && tab === 'op-offers' && isOperator && (
        <OperatorOffersTab onAddOffer={onAddOffer} />
      )}
      {section !== 'blocks' && tab === 'op-library' && isOperator && (
        <OperatorLibraryTab onLoadTemplate={onLoadTemplate} />
      )}
      {section !== 'blocks' && tab === 'op-templates' && hasOpTemplates && (
        <OperatorTemplatesTab entitledBrands={entitledBrands} onClone={onCloneOperatorTemplate} />
      )}
    </aside>
  );
}

// ── Operator Library tab — operators manage their saved templates ─────────────
function OperatorLibraryTab({ onLoadTemplate }) {
  const cfg       = window.TMS_CONFIG || {};
  const workerUrl = (cfg.workerUrl || 'https://tms-promotions-api.tms-travel-marketing-systems.workers.dev').replace(/\/$/, '');
  const token     = cfg.agentToken || '';

  const [templates, setTemplates] = React.useState(null);
  const [loading, setLoading]     = React.useState(false);
  const [error, setError]         = React.useState(null);
  const [editingId, setEditingId] = React.useState(null);
  const [draft, setDraft]         = React.useState({});
  const [saving, setSaving]       = React.useState(false);
  const [opening, setOpening]     = React.useState(null);

  const load = () => {
    if (!token) { setError('No auth token'); return; }
    setLoading(true);
    setError(null);
    fetch(`${workerUrl}/email-builder/operator-templates`, {
      headers: { 'Authorization': `Bearer ${token}` },
    })
      .then(r => r.json())
      .then(data => {
        if (data.error) { setError(data.error); return; }
        setTemplates(Array.isArray(data) ? data : []);
      })
      .catch(() => setError('Failed to load templates'))
      .finally(() => setLoading(false));
  };

  React.useEffect(load, []); // eslint-disable-line

  const startEdit = (t) => {
    setEditingId(t.id);
    setDraft({ name: t.name, expiry_date: t.expiry_date || '' });
  };

  const saveEdit = async (id) => {
    setSaving(true);
    try {
      const res = await fetch(`${workerUrl}/email-builder/operator-templates/${encodeURIComponent(id)}`, {
        method: 'PUT',
        headers: { 'Authorization': `Bearer ${token}`, 'Content-Type': 'application/json' },
        body: JSON.stringify({ name: draft.name, expiry_date: draft.expiry_date }),
      });
      const data = await res.json();
      if (data.error) { alert(data.error); return; }
      setEditingId(null);
      load();
    } catch { alert('Failed to save'); }
    finally { setSaving(false); }
  };

  const openInBuilder = async (id, name) => {
    if (!onLoadTemplate) return;
    setOpening(id);
    try {
      const res = await fetch(`${workerUrl}/email-builder/operator-templates/${encodeURIComponent(id)}`, {
        headers: { 'Authorization': `Bearer ${token}` },
      });
      const data = await res.json();
      if (data.error) { alert(data.error); return; }
      if (Array.isArray(data.blocks)) onLoadTemplate(data.blocks);
    } catch { alert('Failed to open template'); }
    finally { setOpening(null); }
  };

  if (loading) return <div className="bld-search"><p className="bld-search-empty">Loading templates…</p></div>;
  if (error)   return <div className="bld-search"><p className="bld-search-empty" style={{color:'var(--tms-red-500)'}}>{error}</p></div>;
  if (!templates || templates.length === 0) return (
    <div className="bld-search">
      <p className="bld-search-empty">No templates saved yet. Design an email and use <strong>Save as Template</strong> in the header.</p>
    </div>
  );

  return (
    <div className="bld-search">
      <div className="bld-search-head">My templates ({templates.length})</div>
      <div className="bld-results">
        {templates.map(t => {
          const isExpired = t.computed_status === 'expired';
          const expFmt    = t.expiry_date
            ? new Date(t.expiry_date + 'T00:00:00Z').toLocaleDateString('en-GB', {day:'numeric',month:'short',year:'numeric'})
            : '—';
          if (editingId === t.id) {
            return (
              <div key={t.id} className="bld-result" style={{display:'block', padding:'10px 12px'}}>
                <input className="bld-input" style={{width:'100%',boxSizing:'border-box',marginBottom:6}}
                       value={draft.name} onChange={e => setDraft(d => ({...d, name: e.target.value}))} />
                <label style={{display:'block',marginBottom:8}}>
                  <span style={{fontSize:11,color:'var(--product-fg-muted)'}}>Expiry date</span>
                  <input type="date" className="bld-input"
                         style={{width:'100%',boxSizing:'border-box',marginTop:2}}
                         value={draft.expiry_date}
                         onChange={e => setDraft(d => ({...d, expiry_date: e.target.value}))} />
                </label>
                <div style={{display:'flex',gap:6}}>
                  <button className="bld-btn bld-btn--primary" style={{flex:1,padding:'5px 0',fontSize:12}}
                          onClick={() => saveEdit(t.id)} disabled={saving}>
                    {saving ? 'Saving…' : 'Save'}
                  </button>
                  <button className="bld-btn bld-btn--outline" style={{flex:1,padding:'5px 0',fontSize:12}}
                          onClick={() => setEditingId(null)}>Cancel</button>
                </div>
              </div>
            );
          }
          return (
            <div key={t.id} className="bld-result" style={{display:'flex',alignItems:'flex-start',padding:'10px 12px',gap:8}}>
              <div style={{flex:1,minWidth:0}}>
                <div style={{fontWeight:600,fontSize:13,color:'var(--product-fg)',whiteSpace:'nowrap',overflow:'hidden',textOverflow:'ellipsis'}}>{t.name}</div>
                <div style={{fontSize:11,marginTop:2,color: isExpired ? 'var(--tms-red-500)' : 'var(--product-fg-muted)'}}>
                  {isExpired ? 'Expired' : 'Expires'} {expFmt}
                </div>
              </div>
              <div style={{display:'flex',gap:4,flexShrink:0}}>
                <button className="bld-btn bld-btn--outline" style={{padding:'3px 8px',fontSize:11}}
                        onClick={() => startEdit(t)} title="Rename / re-date">Edit</button>
                <button className="bld-btn bld-btn--primary" style={{padding:'3px 8px',fontSize:11}}
                        onClick={() => openInBuilder(t.id, t.name)} disabled={opening === t.id}
                        title="Load blocks into builder for editing">
                  {opening === t.id ? '…' : 'Open'}
                </button>
              </div>
            </div>
          );
        })}
      </div>
    </div>
  );
}

// ── Operator Offers tab — fetches operator's own D1 promotions ────────────────
function OperatorOffersTab({ onAddOffer }) {
  const cfg       = window.TMS_CONFIG || {};
  const workerUrl = (cfg.workerUrl || 'https://tms-promotions-api.tms-travel-marketing-systems.workers.dev').replace(/\/$/, '');
  const token     = cfg.agentToken || '';

  const [offers, setOffers]   = React.useState(null);
  const [loading, setLoading] = React.useState(false);
  const [error, setError]     = React.useState(null);

  React.useEffect(() => {
    if (!token) { setError('No auth token — reload from Hub'); return; }
    setLoading(true);
    fetch(`${workerUrl}/email-builder/operator-offers`, {
      headers: { 'Authorization': `Bearer ${token}` },
    })
      .then(r => r.json())
      .then(data => {
        if (data.error) { setError(data.error); return; }
        setOffers(Array.isArray(data.offers) ? data.offers : []);
      })
      .catch(() => setError('Failed to load offers'))
      .finally(() => setLoading(false));
  }, []); // eslint-disable-line

  if (loading) return <div className="bld-search"><p className="bld-search-empty">Loading offers…</p></div>;
  if (error)   return <div className="bld-search"><p className="bld-search-empty" style={{color:'var(--tms-red-500)'}}>{error}</p></div>;
  if (!offers || offers.length === 0) return (
    <div className="bld-search"><p className="bld-search-empty">No published offers found. Publish offers in the Create Promotion form first.</p></div>
  );

  return (
    <div className="bld-search">
      <div className="bld-search-head">Your offers ({offers.length})</div>
      <div className="bld-results">
        {offers.map((o, i) => (
          <button key={i} className="bld-result bld-result--cruise" onClick={() => onAddOffer(o)}>
            <span className="bld-result-thumb" style={{flexShrink:0, background:'#1a2b4a', display:'flex', alignItems:'center', justifyContent:'center', overflow:'hidden'}}>
              {o.imageUrl
                ? <img src={o.imageUrl} alt="" width="48" height="48"
                       style={{width:'100%', height:'100%', objectFit:'cover', display:'block'}}
                       onError={e => { e.currentTarget.style.display = 'none'; }} />
                : <span style={{color:'#8ab4d4', fontSize:16}}>&#9992;</span>}
            </span>
            <div className="bld-result-body">
              <span className="bld-result-top">
                <span className="bld-result-dest">{o.dest || o.title}</span>
                <span className="bld-result-price">{o.price}</span>
              </span>
              <span className="bld-result-sub">
                {[o.nights && `${o.nights} nts`, o.board, o.airport].filter(Boolean).join(' · ')}
              </span>
              {o.badge && <span className="bld-result-badge">{o.badge}</span>}
            </div>
          </button>
        ))}
      </div>
    </div>
  );
}

// ── Operator Templates tab — agents see unexpired operator templates ───────────
function OperatorTemplatesTab({ entitledBrands, onClone }) {
  const cfg       = window.TMS_CONFIG || {};
  const workerUrl = (cfg.workerUrl || 'https://tms-promotions-api.tms-travel-marketing-systems.workers.dev').replace(/\/$/, '');
  const token     = cfg.agentToken || '';

  const [templates, setTemplates] = React.useState(null);
  const [loading, setLoading]     = React.useState(false);
  const [cloning, setCloning]     = React.useState(null);
  const [error, setError]         = React.useState(null);

  React.useEffect(() => {
    if (!token) { setError('No auth token — reload from Hub'); return; }
    setLoading(true);
    fetch(`${workerUrl}/email-builder/operator-templates/for-agent`, {
      headers: { 'Authorization': `Bearer ${token}` },
    })
      .then(r => r.json())
      .then(data => {
        if (data.error) { setError(data.error); return; }
        setTemplates(Array.isArray(data) ? data : []);
      })
      .catch(() => setError('Failed to load operator templates'))
      .finally(() => setLoading(false));
  }, []); // eslint-disable-line

  const handleClone = async (tmpl) => {
    if (!onClone) return;
    setCloning(tmpl.id);
    try {
      const res = await fetch(`${workerUrl}/email-builder/operator-templates/${encodeURIComponent(tmpl.id)}/clone`, {
        method: 'POST',
        headers: { 'Authorization': `Bearer ${token}`, 'Content-Type': 'application/json' },
        body: JSON.stringify({}),
      });
      const data = await res.json();
      if (data.error) { alert(data.error); return; }
      onClone(data.designId, data.name);
    } catch {
      alert('Failed to open template');
    } finally {
      setCloning(null);
    }
  };

  if (loading)  return <div className="bld-search"><p className="bld-search-empty">Loading operator templates…</p></div>;
  if (error)    return <div className="bld-search"><p className="bld-search-empty" style={{color:'var(--tms-red-500)'}}>{error}</p></div>;
  if (!templates || templates.length === 0) return (
    <div className="bld-search"><p className="bld-search-empty">No operator templates available yet.</p></div>
  );

  return (
    <div className="bld-search">
      <div className="bld-search-head">Operator templates ({templates.length})</div>
      <div className="bld-results">
        {templates.map((t) => {
          const expiry = t.expiry_date
            ? new Date(t.expiry_date + 'T00:00:00Z').toLocaleDateString('en-GB', {day:'numeric', month:'short', year:'numeric'})
            : null;
          return (
            <button key={t.id} className="bld-result" disabled={cloning === t.id}
                    onClick={() => handleClone(t)}
                    style={{display:'block', width:'100%', textAlign:'left', padding:'10px 12px'}}>
              <div style={{fontWeight:600, fontSize:13, color:'var(--product-fg)'}}>{t.name}</div>
              {expiry && <div style={{fontSize:11, color:'var(--product-fg-muted)', marginTop:2}}>Expires {expiry}</div>}
              {cloning === t.id && <div style={{fontSize:11, color:'var(--product-fg-muted)', marginTop:2}}>Opening…</div>}
            </button>
          );
        })}
      </div>
    </div>
  );
}

window.Sidebar = Sidebar;
