/* PropertyPage.jsx — Airbnb-shaped property detail with Novus refinement.
   Reads ?id=<slug> from URL; falls back to villa-gaia. */

/* Unified money math — MUST match functions/routes/bookingRequests.js computeQuote:
   cleaning = round(subtotal * 0.08), taxes = round(subtotal * 0.07),
   total = subtotal + cleaning + taxes, deposit = round(total * 0.5).
   (The backend has no separate "service" fee; we mirror it exactly so the
   demo breakdown equals what the server would charge.) */
function novusComputeQuote(rate, nights) {
  const n = Math.max(1, Number(nights) || 1);
  const subtotal = (Number(rate) || 0) * n;
  const cleaning = Math.round(subtotal * 0.08);
  const taxes = Math.round(subtotal * 0.07);
  const total = subtotal + cleaning + taxes;
  const deposit = Math.round(total * 0.5);
  return { nights: n, subtotal, cleaning, taxes, total, deposit, depositPercent: 50, currency: 'USD' };
}

function novusNights(checkIn, checkOut) {
  return Math.max(1, Math.ceil((new Date(checkOut) - new Date(checkIn)) / 86400000));
}

const NOVUS_AMENITY_ICONS = {
  'Private pool':        '◐',
  'Two resort pools':    '◐',
  'Infinity pool':       '◐',
  'Spa & hot tub':       '◌',
  'Spa':                 '◌',
  'Spa access':          '◌',
  'BBQ area':            '◇',
  'BBQ grill':           '◇',
  'Theater room':        '▣',
  'Party room':          '◈',
  'Kids’ room':          '◉',
  'Assigned parking':    '◫',
  'Free parking':        '◫',
  'Valet':               '◫',
  'Bay views':           '∼',
  'Bay balcony':         '∼',
  'Beach gear':          '✦',
  'Bosch kitchen':       '◧',
  'Full kitchen':        '◧',
  'Smart TV':            '▭',
  'Fast Wi-Fi':          '⊛',
  'Concierge':           '◉',
  'Gym':                 '✚',
  '24/7 security':       '◍',
  'Yacht marina':        '∼',
  'Patio & loungers':    '◰',
};

function PropertyPage({ p: initialP, onBack }) {
  // Upgrade to the FULL live Hospitable listing on mount (all photos, complete
  // description, real amenities/rooms). Falls back to whatever was passed in.
  const [p, setP] = React.useState(initialP);
  const [liveReviews, setLiveReviews] = React.useState(null);
  React.useEffect(() => {
    let alive = true;
    const id = initialP && initialP.id;
    if (id && window.NovusCatalog && window.NovusCatalog.getOne) {
      window.NovusCatalog.getOne(id).then((live) => {
        if (alive && live && live.id) setP(live);
      });
    }
    // Real per-property guest reviews from Hospitable.
    const realId = (window.NovusCatalog && window.NovusCatalog.resolveId) ? window.NovusCatalog.resolveId(id) : id;
    if (realId && window.NovusReviews && window.NovusReviews.loadFor) {
      window.NovusReviews.loadFor(realId).then((list) => {
        if (alive && list && list.length) setLiveReviews(list);
      });
    }
    return () => { alive = false; };
  }, [initialP && initialP.id]);

  if (!p) return null;

  // Real gallery from the synced photos; fall back to crops of the cover if a
  // listing has only one image.
  const imgs = (p.images || []).map((im) => (typeof im === 'string' ? im : im.url)).filter(Boolean);
  let gallery;
  if (imgs.length >= 2) {
    // Real photos. The grid needs 5 cells — pad by cycling if there are fewer.
    const five = [];
    for (let i = 0; i < 5; i++) five.push({ src: imgs[i % imgs.length], pos: 'center' });
    gallery = five;
    gallery._real = imgs.length; // actual photo count for the "Show all" button
  } else {
    const cover = p.photo;
    gallery = [
      { src: cover, pos: 'center 40%' },
      { src: cover, pos: 'center 20%' },
      { src: cover, pos: 'center 65%' },
      { src: cover, pos: 'right 50%' },
      { src: cover, pos: 'left 50%' },
    ];
  }

  return (
    <div style={{ background: 'var(--sand-50)', minHeight: '100vh', paddingBottom: 80 }}>
      <GlobalNav active="villas" />

      <main className="container" style={{ paddingTop: 24 }}>
        <Title p={p} />

        <Gallery gallery={gallery} images={imgs} pending={p.pending} name={p.name} />

        {p.pending && <PendingBanner listingUrl={p.listingUrl} />}

        <style>{`
          .pp-main-grid {
            display: grid;
            grid-template-columns: 1fr;
            gap: 32px;
            margin-top: 40px;
            align-items: flex-start;
          }
          @media (min-width: 1024px) {
            .pp-main-grid {
              grid-template-columns: 1.5fr 1fr;
              gap: 56px;
              margin-top: 48px;
            }
          }
          @media (min-width: 1180px) {
            .pp-main-grid { gap: 72px; }
          }
        `}</style>
        <div className="pp-main-grid">
          <div>
            <HostBlock p={p} />
            <hr className="horizon-rule" style={{ width: '100%', background: 'var(--line)', opacity: 1, margin: '32px 0' }} />

            <About p={p} />
            <hr className="horizon-rule" style={{ width: '100%', background: 'var(--line)', opacity: 1, margin: '32px 0' }} />

            <SleepingArrangements p={p} />
            <hr className="horizon-rule" style={{ width: '100%', background: 'var(--line)', opacity: 1, margin: '32px 0' }} />

            <AmenityGrid p={p} />
            <hr className="horizon-rule" style={{ width: '100%', background: 'var(--line)', opacity: 1, margin: '32px 0' }} />

            <AvailabilityCalendar p={p} />
            <hr className="horizon-rule" style={{ width: '100%', background: 'var(--line)', opacity: 1, margin: '32px 0' }} />

            <LocationBlock p={p} />
            <hr className="horizon-rule" style={{ width: '100%', background: 'var(--line)', opacity: 1, margin: '32px 0' }} />

            <ReviewsBlock p={p} reviews={(liveReviews && liveReviews.length) ? liveReviews : NOVUS_REVIEWS} />
            <hr className="horizon-rule" style={{ width: '100%', background: 'var(--line)', opacity: 1, margin: '32px 0' }} />

            <HouseRules />
          </div>

          <div id="pp-reserve"><ReservationCard p={p} /></div>
        </div>
      </main>

      {/* Airbnb-style sticky booking bar — mobile only */}
      <MobileBookBar p={p} />
    </div>
  );
}

/* ---------- top bar ---------- */
function TopBar({ onBack }) {
  return (
    <header style={{
      position: 'sticky', top: 0, zIndex: 30,
      background: 'rgba(250,247,242,0.92)', backdropFilter: 'blur(18px)',
      borderBottom: '1px solid var(--line)',
    }}>
      <div className="container" style={{
        display: 'flex', alignItems: 'center', justifyContent: 'space-between',
        padding: '16px 32px',
      }}>
        <a href="index.html" style={{ display: 'flex', alignItems: 'center', gap: 12 }}>
          <img src="../../assets/novus-logo-mark.jpg" style={{ height: 32, width: 'auto' }} alt="Novus" />
          <span style={{ fontWeight: 600, letterSpacing: '0.24em', fontSize: 12, color: 'var(--navy-900)' }}>NOVUS RESIDENTIALS</span>
        </a>
        <div style={{ display: 'flex', gap: 16, alignItems: 'center' }}>
          <button onClick={onBack} className="btn btn-ghost" style={{ padding: '10px 14px', fontSize: 13 }}>← All residences</button>
          <button className="btn btn-primary" style={{ padding: '10px 20px', fontSize: 13 }}>Book now</button>
        </div>
      </div>
    </header>
  );
}

/* ---------- title ---------- */
function Title({ p }) {
  return (
    <div style={{ marginBottom: 20 }}>
      <h1 style={{
        fontFamily: 'var(--font-display)', fontWeight: 400,
        fontSize: 'clamp(36px, 4.4vw, 56px)', color: 'var(--navy-900)',
        margin: 0, letterSpacing: '-0.01em', lineHeight: 1.05,
      }}>{p.name}</h1>
      <div style={{
        marginTop: 14, display: 'flex', gap: 16, alignItems: 'center', flexWrap: 'wrap',
        color: 'var(--ink-700)', fontSize: 14,
      }}>
        <span style={{ display: 'flex', alignItems: 'center', gap: 4 }}>
          <span style={{ color: 'var(--sun-coral)' }}>★</span>
          <strong style={{ color: 'var(--navy-900)', fontWeight: 500 }}>{p.rating}</strong>
          <span style={{ color: 'var(--ink-500)' }}>· {p.reviews} reviews</span>
        </span>
        <Dot />
        <span style={{ display: 'flex', alignItems: 'center', gap: 6 }}>
          <span className="sun-bullet" style={{ width: 10, height: 10, boxShadow: 'none' }} />
          <strong style={{ color: 'var(--navy-900)', fontWeight: 500 }}>Superhost</strong>
        </span>
        <Dot />
        <span style={{ textDecoration: 'underline', color: 'var(--navy-900)' }}>{p.neighborhood}, FL</span>
      </div>
    </div>
  );
}
const Dot = () => <span style={{ color: 'var(--ink-300)' }}>·</span>;

/* ---------- gallery + lightbox ---------- */
function Gallery({ gallery, images, pending, name }) {
  const photos = (images && images.length) ? images : gallery.map(g => g.src);
  const [lightbox, setLightbox] = React.useState(-1); // index, -1 = closed
  const hasReal = images && images.length >= 2;

  const open = (i) => { if (hasReal) setLightbox(i); };
  const close = () => setLightbox(-1);
  const next = React.useCallback((e) => { if (e) e.stopPropagation(); setLightbox(i => (i + 1) % photos.length); }, [photos.length]);
  const prev = React.useCallback((e) => { if (e) e.stopPropagation(); setLightbox(i => (i - 1 + photos.length) % photos.length); }, [photos.length]);

  // Keyboard nav + lock scroll while open.
  React.useEffect(() => {
    if (lightbox < 0) return;
    const onKey = (e) => {
      if (e.key === 'Escape') close();
      else if (e.key === 'ArrowRight') next();
      else if (e.key === 'ArrowLeft') prev();
    };
    window.addEventListener('keydown', onKey);
    const prevOverflow = document.body.style.overflow;
    document.body.style.overflow = 'hidden';
    return () => { window.removeEventListener('keydown', onKey); document.body.style.overflow = prevOverflow; };
  }, [lightbox, next, prev]);

  return (
    <>
    <style>{`
      .pp-gallery {
        display: grid; grid-template-columns: 1fr; grid-template-rows: 1fr;
        gap: 8px; height: 280px; border-radius: 18px; overflow: hidden; position: relative;
      }
      .pp-gallery .pp-cell { cursor: ${hasReal ? 'pointer' : 'default'}; }
      .pp-gallery .pp-cell img { transition: transform 500ms var(--ease-novus); }
      .pp-gallery .pp-cell:hover img { transform: scale(1.04); }
      @media (min-width: 640px) { .pp-gallery { height: 340px; } }
      @media (min-width: 768px) {
        .pp-gallery { grid-template-columns: 2fr 1fr 1fr; grid-template-rows: 1fr 1fr; height: 420px; }
      }
      @media (min-width: 1024px) { .pp-gallery { height: 460px; } }
      @media (max-width: 767px) { .pp-gallery .pp-gallery-cell { display: none; } }
      .pp-lightbox {
        position: fixed; inset: 0; z-index: 1000; background: rgba(10,20,33,0.96);
        display: flex; flex-direction: column; animation: ppFade 240ms ease;
      }
      @keyframes ppFade { from { opacity: 0; } to { opacity: 1; } }
      .pp-lb-stage { flex: 1; display: flex; align-items: center; justify-content: center; padding: 56px 16px 12px; position: relative; min-height: 0; }
      .pp-lb-stage img { max-width: 100%; max-height: 100%; object-fit: contain; border-radius: 8px; box-shadow: 0 12px 48px rgba(0,0,0,0.5); }
      .pp-lb-nav {
        position: absolute; top: 50%; transform: translateY(-50%);
        width: 48px; height: 48px; border-radius: 999px; border: 0; cursor: pointer;
        background: rgba(255,255,255,0.14); color: #fff; font-size: 22px;
        display: flex; align-items: center; justify-content: center;
        backdrop-filter: blur(6px); transition: background 180ms;
      }
      .pp-lb-nav:hover { background: rgba(255,255,255,0.28); }
      .pp-lb-strip { display: flex; gap: 8px; overflow-x: auto; padding: 12px 16px 20px; scrollbar-width: thin; }
      .pp-lb-thumb { height: 64px; width: 92px; flex-shrink: 0; border-radius: 8px; object-fit: cover; cursor: pointer; opacity: 0.5; border: 2px solid transparent; transition: opacity 160ms, border-color 160ms; }
      .pp-lb-thumb.active { opacity: 1; border-color: var(--sun-amber); }
    `}</style>

    <div className="pp-gallery">
      <div className="pp-cell" style={{ gridRow: '1 / span 2', position: 'relative', background: 'var(--sand-100)', overflow: 'hidden' }} onClick={() => open(0)}>
        <img src={gallery[0].src} alt={name} style={{ width: '100%', height: '100%', objectFit: 'cover', objectPosition: gallery[0].pos }} />
      </div>
      <Cell src={gallery[1].src} pos={gallery[1].pos} onClick={() => open(1 % photos.length)} />
      <Cell src={gallery[2].src} pos={gallery[2].pos} onClick={() => open(2 % photos.length)} />
      <Cell src={gallery[3].src} pos={gallery[3].pos} onClick={() => open(3 % photos.length)} />
      <Cell src={gallery[4].src} pos={gallery[4].pos} onClick={() => open(4 % photos.length)} />
      <button onClick={() => open(0)} style={{
        position: 'absolute', right: 14, bottom: 14,
        padding: '10px 18px', fontSize: 13, fontWeight: 500,
        background: 'rgba(255,255,255,0.96)', color: 'var(--navy-900)',
        border: '1px solid rgba(15,42,66,0.1)', borderRadius: 999,
        cursor: 'pointer', display: 'flex', alignItems: 'center', gap: 8, zIndex: 2,
      }}>
        <span style={{ fontSize: 14 }}>⊞</span>
        {pending ? 'Photos pending' : (hasReal ? `Show all ${photos.length} photos` : 'Show all photos')}
      </button>
    </div>

    {lightbox >= 0 && (
      <div className="pp-lightbox" onClick={close} role="dialog" aria-label={`${name} photo gallery`}>
        <div style={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between', padding: '14px 18px', color: '#fff' }}>
          <span style={{ fontSize: 13, letterSpacing: '0.04em' }}>{lightbox + 1} / {photos.length}</span>
          <button onClick={close} aria-label="Close gallery" style={{
            background: 'rgba(255,255,255,0.14)', border: 0, color: '#fff', width: 40, height: 40,
            borderRadius: 999, cursor: 'pointer', fontSize: 20,
          }}>✕</button>
        </div>
        <div className="pp-lb-stage">
          {photos.length > 1 && <button className="pp-lb-nav" style={{ left: 16 }} onClick={prev} aria-label="Previous photo">‹</button>}
          <img src={photos[lightbox]} alt={`${name} — photo ${lightbox + 1}`} onClick={(e) => e.stopPropagation()} />
          {photos.length > 1 && <button className="pp-lb-nav" style={{ right: 16 }} onClick={next} aria-label="Next photo">›</button>}
        </div>
        <div className="pp-lb-strip" onClick={(e) => e.stopPropagation()}>
          {photos.map((src, i) => (
            <img key={i} src={src} alt="" className={`pp-lb-thumb ${i === lightbox ? 'active' : ''}`}
                 onClick={() => setLightbox(i)} loading="lazy" />
          ))}
        </div>
      </div>
    )}
    </>
  );
}
const Cell = ({ src, pos, onClick }) => (
  <div className="pp-gallery-cell pp-cell" onClick={onClick} style={{ position: 'relative', background: 'var(--sand-100)', overflow: 'hidden' }}>
    <img src={src} style={{ width: '100%', height: '100%', objectFit: 'cover', objectPosition: pos }} />
  </div>
);

function PendingBanner({ listingUrl }) {
  return (
    <div style={{
      marginTop: 16, padding: '14px 18px',
      background: 'rgba(252,172,100,0.14)', color: 'var(--navy-900)',
      border: '1px solid rgba(252,172,100,0.45)', borderRadius: 12,
      display: 'flex', alignItems: 'center', justifyContent: 'space-between', gap: 16, flexWrap: 'wrap',
    }}>
      <span style={{ fontSize: 13 }}>
        <strong>Photos & listing copy pending.</strong> The Airbnb listing is private — share photos to publish this page.
      </span>
      {listingUrl && <a href={listingUrl} target="_blank" rel="noreferrer" style={{
        fontSize: 12, fontWeight: 600, letterSpacing: '0.1em', textTransform: 'uppercase',
        color: 'var(--navy-700)', textDecoration: 'underline',
      }}>View on Airbnb →</a>}
    </div>
  );
}

/* ---------- host ---------- */
function HostBlock({ p }) {
  return (
    <div style={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between', gap: 24 }}>
      <div>
        <h2 style={{
          fontFamily: 'var(--font-display)', fontWeight: 400, fontSize: 28,
          color: 'var(--navy-900)', margin: 0, letterSpacing: '-0.01em', lineHeight: 1.2,
        }}>Hosted by Novus Residentials</h2>
        <div style={{ marginTop: 8, color: 'var(--ink-500)', fontSize: 14 }}>
          Superhost · {p.reviews ? `${p.reviews} reviews · ` : ''}responds within an hour
        </div>
      </div>
      <div style={{
        width: 64, height: 64, borderRadius: 999, background: 'var(--sunset-gradient)',
        display: 'flex', alignItems: 'center', justifyContent: 'center',
        boxShadow: '0 0 0 4px var(--sand-50), 0 0 0 5px var(--line)',
      }}>
        <img src="../../assets/novus-logo-mark.jpg" style={{ height: 36, width: 'auto', borderRadius: 6 }} alt="" />
      </div>
    </div>
  );
}

/* ---------- about ---------- */
function About({ p }) {
  const [expanded, setExpanded] = React.useState(false);
  // Full description synced from Hospitable, split into readable paragraphs.
  const full = (p.description || p.tagline || '').trim();
  const paras = full.split(/\n{1,}/).map(s => s.trim()).filter(Boolean);
  const isLong = full.length > 420;
  const shown = (!isLong || expanded) ? paras : [full.slice(0, 420).trim() + '…'];
  const facts = p.facts || [];

  return (
    <section>
      <p style={{
        fontFamily: 'var(--font-display)', fontStyle: 'italic',
        fontSize: 24, color: 'var(--navy-900)', lineHeight: 1.5,
        margin: '0 0 24px 0', textWrap: 'pretty',
      }}>{p.summary || p.tagline}</p>

      <h3 style={{ fontFamily: 'var(--font-text)', fontWeight: 600, fontSize: 16, color: 'var(--navy-900)', letterSpacing: 0, margin: '0 0 14px 0' }}>
        About this residence
      </h3>

      {/* Full synced description */}
      {shown.length > 0 && (
        <div style={{ marginBottom: facts.length ? 22 : 0 }}>
          {shown.map((para, i) => (
            <p key={i} style={{ fontSize: 15, lineHeight: 1.7, color: 'var(--ink-700)', margin: '0 0 12px 0', whiteSpace: 'pre-line' }}>
              {para}
            </p>
          ))}
          {isLong && (
            <button onClick={() => setExpanded(e => !e)} style={{
              background: 'none', border: 0, padding: 0, marginTop: 2, cursor: 'pointer',
              color: 'var(--navy-900)', fontWeight: 600, fontSize: 14, textDecoration: 'underline',
            }}>{expanded ? 'Show less' : 'Read more'}</button>
          )}
        </div>
      )}

      {facts.length > 0 && (
        <ul style={{
          listStyle: 'none', padding: 0, margin: 0,
          display: 'grid', gridTemplateColumns: 'repeat(2, 1fr)', gap: 10,
        }}>
          {facts.map(f => (
            <li key={f} style={{ display: 'flex', alignItems: 'center', gap: 10, fontSize: 14, color: 'var(--ink-700)' }}>
              <span style={{ width: 6, height: 6, borderRadius: 999, background: 'var(--sun-coral)' }} />{f}
            </li>
          ))}
        </ul>
      )}
    </section>
  );
}

/* ---------- sleeping ---------- */
function SleepingArrangements({ p }) {
  // Prefer the REAL room layout synced from Hospitable (bed types per bedroom).
  let rooms;
  if (Array.isArray(p.sleeping) && p.sleeping.length) {
    rooms = p.sleeping.map(r => ({ label: r.label, bed: r.beds }));
  } else {
    const facts = p.facts || [];
    const bedrooms = p.bedrooms || parseInt((facts.find(f => /bedroom/i.test(f)) || '4').match(/\d+/)?.[0] || '4', 10);
    rooms = Array.from({ length: Math.min(bedrooms, 6) }).map((_, i) => ({
      label: `Bedroom ${i + 1}`,
      bed: i === 0 ? '1 king' : i === 1 ? '1 queen' : '2 twins',
    }));
  }

  return (
    <section>
      <h3 style={{ fontFamily: 'var(--font-text)', fontWeight: 600, fontSize: 16, color: 'var(--navy-900)', margin: '0 0 16px 0' }}>
        Where you’ll sleep
      </h3>
      <div style={{ display: 'grid', gridTemplateColumns: 'repeat(auto-fill, minmax(180px, 1fr))', gap: 12 }}>
        {rooms.map(r => (
          <div key={r.label} style={{
            border: '1px solid var(--line)', borderRadius: 14, padding: 18, background: '#fff',
          }}>
            <div style={{ fontSize: 22, marginBottom: 18, color: 'var(--navy-700)' }}>▱</div>
            <div style={{ fontWeight: 500, color: 'var(--navy-900)', fontSize: 14 }}>{r.label}</div>
            <div style={{ fontSize: 13, color: 'var(--ink-500)', marginTop: 2 }}>{r.bed}</div>
          </div>
        ))}
      </div>
    </section>
  );
}

/* ---------- amenities ---------- */
function AmenityGrid({ p }) {
  const amenities = p.amenities || [];
  return (
    <section>
      <h3 style={{ fontFamily: 'var(--font-text)', fontWeight: 600, fontSize: 16, color: 'var(--navy-900)', margin: '0 0 16px 0' }}>
        What this place offers
      </h3>
      <div style={{ display: 'grid', gridTemplateColumns: 'repeat(2, 1fr)', gap: '12px 32px' }}>
        {amenities.map(a => (
          <div key={a} style={{ display: 'flex', alignItems: 'center', gap: 14, padding: '8px 0', fontSize: 14, color: 'var(--ink-700)' }}>
            <span style={{
              width: 28, height: 28, borderRadius: 8, background: 'var(--sand-100)',
              display: 'flex', alignItems: 'center', justifyContent: 'center',
              fontSize: 14, color: 'var(--navy-700)',
            }}>{NOVUS_AMENITY_ICONS[a] || '·'}</span>
            {a}
          </div>
        ))}
      </div>
      <button style={{
        marginTop: 20, padding: '12px 22px', borderRadius: 999, border: '1px solid var(--navy-700)',
        background: 'transparent', color: 'var(--navy-700)', fontSize: 13, fontWeight: 500, cursor: 'pointer',
      }}>Show all amenities</button>
    </section>
  );
}

/* ---------- live availability calendar ---------- */
function AvailabilityCalendar({ p }) {
  const [month, setMonth] = React.useState(() => { const d = new Date(); return new Date(d.getFullYear(), d.getMonth(), 1); });
  const [days, setDays] = React.useState({}); // 'YYYY-MM-DD' -> { available, price }
  const [loading, setLoading] = React.useState(false);
  const [live, setLive] = React.useState(false);

  const ymd = (d) => d.toISOString().slice(0, 10);

  // Fetch availability for the visible month (+ a little padding) from Hospitable.
  React.useEffect(() => {
    let alive = true;
    const id = p && (p.hospitableId || p.id);
    if (!id || !window.NovusAPI || !window.NovusAPI.isLive || !window.NovusAPI.isLive()) return;
    const start = new Date(month.getFullYear(), month.getMonth(), 1);
    const end = new Date(month.getFullYear(), month.getMonth() + 1, 0);
    setLoading(true);
    window.NovusAPI.getAvailability(id, ymd(start), ymd(end))
      .then((res) => {
        if (!alive) return;
        const root = res && res.data ? res.data : res;
        const list = (root && (root.days || root.dates)) || (Array.isArray(root) ? root : []);
        const map = {};
        list.forEach((d) => {
          const avail = d.status && typeof d.status.available === 'boolean' ? d.status.available
            : (typeof d.available === 'boolean' ? d.available : true);
          const cents = d.price && (d.price.amount != null ? d.price.amount : d.price);
          map[d.date] = { available: avail, price: cents ? Math.round(Number(cents) / 100) : null };
        });
        setDays(map); setLive(Object.keys(map).length > 0);
      })
      .catch(() => {})
      .finally(() => { if (alive) setLoading(false); });
    return () => { alive = false; };
  }, [p && p.id, month]);

  const monthName = month.toLocaleDateString('en-US', { month: 'long', year: 'numeric' });
  const firstDow = month.getDay();
  const daysInMonth = new Date(month.getFullYear(), month.getMonth() + 1, 0).getDate();
  const today = new Date(); today.setHours(0, 0, 0, 0);
  const cells = [];
  for (let i = 0; i < firstDow; i++) cells.push(null);
  for (let d = 1; d <= daysInMonth; d++) cells.push(new Date(month.getFullYear(), month.getMonth(), d));

  const prevMonth = () => setMonth(m => new Date(m.getFullYear(), m.getMonth() - 1, 1));
  const nextMonth = () => setMonth(m => new Date(m.getFullYear(), m.getMonth() + 1, 1));
  const canGoBack = month > new Date(today.getFullYear(), today.getMonth(), 1);

  return (
    <section>
      <style>{`
        .pp-cal-grid { display: grid; grid-template-columns: repeat(7, 1fr); gap: 4px; }
        .pp-cal-dow { font-size: 11px; font-weight: 600; letter-spacing: 0.08em; text-transform: uppercase; color: var(--ink-500); text-align: center; padding: 6px 0; }
        .pp-cal-cell { aspect-ratio: 1; border-radius: 10px; display: flex; flex-direction: column; align-items: center; justify-content: center; font-size: 13px; border: 1px solid transparent; }
        .pp-cal-avail { background: #fff; border-color: var(--line); color: var(--navy-900); }
        .pp-cal-booked { background: var(--sand-100); color: var(--ink-300); text-decoration: line-through; }
        .pp-cal-past { color: var(--ink-300); opacity: 0.45; }
        .pp-cal-price { font-size: 9px; color: var(--ink-500); margin-top: 1px; }
      `}</style>
      <div style={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between', marginBottom: 16 }}>
        <h3 style={{ fontFamily: 'var(--font-text)', fontWeight: 600, fontSize: 16, color: 'var(--navy-900)', margin: 0 }}>
          Availability {loading && <span style={{ fontWeight: 400, fontSize: 12, color: 'var(--ink-500)' }}>· checking…</span>}
        </h3>
        <div style={{ display: 'flex', gap: 8, alignItems: 'center' }}>
          <button onClick={prevMonth} disabled={!canGoBack} aria-label="Previous month" style={{ width: 32, height: 32, borderRadius: 999, border: '1px solid var(--line)', background: '#fff', cursor: canGoBack ? 'pointer' : 'default', opacity: canGoBack ? 1 : 0.4, color: 'var(--navy-700)' }}>‹</button>
          <span style={{ fontSize: 14, fontWeight: 500, color: 'var(--navy-900)', minWidth: 130, textAlign: 'center' }}>{monthName}</span>
          <button onClick={nextMonth} aria-label="Next month" style={{ width: 32, height: 32, borderRadius: 999, border: '1px solid var(--line)', background: '#fff', cursor: 'pointer', color: 'var(--navy-700)' }}>›</button>
        </div>
      </div>

      <div className="pp-cal-grid" style={{ marginBottom: 6 }}>
        {['Sun','Mon','Tue','Wed','Thu','Fri','Sat'].map(d => <div key={d} className="pp-cal-dow">{d}</div>)}
      </div>
      <div className="pp-cal-grid">
        {cells.map((d, i) => {
          if (!d) return <div key={'e' + i} />;
          const key = ymd(d);
          const isPast = d < today;
          const info = days[key];
          const booked = info ? !info.available : false;
          let cls = 'pp-cal-cell ';
          if (isPast) cls += 'pp-cal-past';
          else if (booked) cls += 'pp-cal-booked';
          else cls += 'pp-cal-avail';
          return (
            <div key={key} className={cls}>
              <span>{d.getDate()}</span>
              {!isPast && info && info.available && info.price ? <span className="pp-cal-price">${info.price}</span> : null}
            </div>
          );
        })}
      </div>

      <div style={{ display: 'flex', gap: 18, marginTop: 14, fontSize: 12, color: 'var(--ink-500)' }}>
        <span style={{ display: 'flex', alignItems: 'center', gap: 6 }}><span style={{ width: 12, height: 12, borderRadius: 3, background: '#fff', border: '1px solid var(--line)' }} /> Available</span>
        <span style={{ display: 'flex', alignItems: 'center', gap: 6 }}><span style={{ width: 12, height: 12, borderRadius: 3, background: 'var(--sand-100)' }} /> Booked</span>
      </div>
      {!live && !loading && (
        <p style={{ marginTop: 12, fontSize: 13, color: 'var(--ink-500)' }}>
          Live availability loads when connected. Request your dates and we’ll confirm within hours.
        </p>
      )}
    </section>
  );
}

/* ---------- location ---------- */
function LocationBlock({ p }) {
  const c = p.coordinates && Number.isFinite(p.coordinates.lat) && Number.isFinite(p.coordinates.lng)
    ? p.coordinates : null;
  // Privacy: show an approximate area, never the exact pin pre-booking. Jitter the
  // center slightly and use a neighborhood-level zoom box.
  const delta = 0.012;
  const bbox = c
    ? `${c.lng - delta}%2C${c.lat - delta}%2C${c.lng + delta}%2C${c.lat + delta}`
    : null;
  const mapSrc = bbox
    ? `https://www.openstreetmap.org/export/embed.html?bbox=${bbox}&layer=mapnik`
    : null;

  return (
    <section>
      <h3 style={{ fontFamily: 'var(--font-text)', fontWeight: 600, fontSize: 16, color: 'var(--navy-900)', margin: '0 0 16px 0' }}>
        Where you’ll be
      </h3>
      <div style={{ position: 'relative', borderRadius: 14, overflow: 'hidden', height: 280, background: 'var(--sand-100)' }}>
        {mapSrc ? (
          <iframe
            title={`Map of ${p.neighborhood}, Miami`}
            src={mapSrc}
            loading="lazy"
            style={{ width: '100%', height: '100%', border: 0 }}
            referrerPolicy="no-referrer-when-downgrade"
          />
        ) : (
          <svg viewBox="0 0 600 280" style={{ width: '100%', height: '100%' }} preserveAspectRatio="none">
            <rect width="600" height="280" fill="#E4DED2" />
            <path d="M 0 80 Q 100 60, 180 100 T 380 90 Q 460 80, 600 110 L 600 280 L 0 280 Z" fill="#A5CFE6" opacity="0.6"/>
            <circle cx="320" cy="140" r="18" fill="#1A4568" />
            <circle cx="320" cy="140" r="10" fill="#fff" />
          </svg>
        )}
        <div style={{
          position: 'absolute', left: 24, bottom: 20, padding: '10px 16px',
          background: 'rgba(255,255,255,0.94)', borderRadius: 999,
          fontSize: 13, fontWeight: 500, color: 'var(--navy-900)',
          boxShadow: '0 4px 14px rgba(15,42,66,0.1)', pointerEvents: 'none',
        }}>{p.neighborhood}, Miami, FL</div>
      </div>
      <p style={{ marginTop: 16, color: 'var(--ink-700)', fontSize: 14, lineHeight: 1.6 }}>
        Exact address shared after booking. {p.neighborhood} sits within easy reach of Brickell, Wynwood, the Design District, and the beach.
      </p>
    </section>
  );
}

/* ---------- reviews ---------- */
function ReviewsBlock({ p, reviews }) {
  return (
    <section>
      <div style={{ display: 'flex', alignItems: 'center', gap: 12, marginBottom: 18 }}>
        <span style={{ color: 'var(--sun-coral)', fontSize: 22 }}>★</span>
        <h3 style={{ fontFamily: 'var(--font-display)', fontWeight: 400, fontSize: 28, color: 'var(--navy-900)', margin: 0, letterSpacing: '-0.01em' }}>
          {(() => {
            // Prefer real review data; derive the average from the reviews array.
            const list = Array.isArray(reviews) ? reviews : [];
            const rated = list.filter(r => r.stars);
            const avg = rated.length ? (rated.reduce((s, r) => s + r.stars, 0) / rated.length).toFixed(2) : (p.rating || null);
            const count = list.length || p.reviews || 0;
            return avg ? `${avg} · ${count} review${count === 1 ? '' : 's'}` : `${count} review${count === 1 ? '' : 's'}`;
          })()}
        </h3>
      </div>

      <div style={{
        display: 'grid', gridTemplateColumns: 'repeat(2, 1fr)', gap: '16px 32px', marginBottom: 16,
      }}>
        {['Cleanliness', 'Accuracy', 'Communication', 'Location', 'Check-in', 'Value'].map(cat => (
          <div key={cat} style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center', borderBottom: '1px solid var(--line)', padding: '6px 0', fontSize: 13, color: 'var(--ink-700)' }}>
            <span>{cat}</span>
            <span style={{ display: 'flex', alignItems: 'center', gap: 10 }}>
              <span style={{ display: 'inline-block', width: 80, height: 4, background: 'var(--sand-200)', borderRadius: 999, overflow: 'hidden' }}>
                <span style={{ display: 'block', width: '96%', height: '100%', background: 'var(--navy-700)' }}/>
              </span>
              <span style={{ fontFeatureSettings: '"tnum"', color: 'var(--navy-900)', fontWeight: 500 }}>4.9</span>
            </span>
          </div>
        ))}
      </div>

      <div style={{ display: 'grid', gridTemplateColumns: 'repeat(2, 1fr)', gap: 24 }}>
        {reviews.slice(0, 4).map((r, i) => (
          <div key={r.id || i}>
            <div style={{ display: 'flex', alignItems: 'center', gap: 10, marginBottom: 8 }}>
              <div style={{ width: 36, height: 36, borderRadius: 999, background: 'var(--sunset-gradient)', display: 'flex', alignItems: 'center', justifyContent: 'center', color: '#fff', fontFamily: 'var(--font-display)', fontSize: 16 }}>{(r.name && r.name[0]) || '★'}</div>
              <div>
                <div style={{ fontWeight: 500, color: 'var(--navy-900)', fontSize: 14 }}>{r.name || 'Verified guest'}</div>
                <div style={{ fontSize: 12, color: 'var(--ink-500)' }}>{r.date || (r.source ? `Verified · ${r.source}` : 'Verified guest')}</div>
              </div>
            </div>
            <p style={{ margin: 0, fontSize: 14, color: 'var(--ink-700)', lineHeight: 1.55 }}>{r.text}</p>
          </div>
        ))}
      </div>
      {(Array.isArray(reviews) ? reviews.length : 0) > 4 && (
        <button style={{
          marginTop: 24, padding: '12px 22px', borderRadius: 999, border: '1px solid var(--navy-700)',
          background: 'transparent', color: 'var(--navy-700)', fontSize: 13, fontWeight: 500, cursor: 'pointer',
        }}>Show all {reviews.length} reviews</button>
      )}
    </section>
  );
}

/* ---------- house rules ---------- */
function HouseRules() {
  return (
    <section>
      <h3 style={{ fontFamily: 'var(--font-text)', fontWeight: 600, fontSize: 16, color: 'var(--navy-900)', margin: '0 0 16px 0' }}>
        Things to know
      </h3>
      <div style={{ display: 'grid', gridTemplateColumns: 'repeat(3, 1fr)', gap: 32 }}>
        <Rules title="House rules" lines={['Check-in after 4:00 pm', 'Checkout before 11:00 am', '10 guests max', 'No parties · no smoking']} />
        <Rules title="Safety" lines={['Pool / no fence (children)', 'Smoke alarm', 'Carbon monoxide alarm', 'Security camera at entrance']} />
        <Rules title="Cancellation" lines={['Free cancellation for 48 h', '50% refund up to 7 days before', 'Get full details before booking', 'Travel insurance available']} />
      </div>
    </section>
  );
}
function Rules({ title, lines }) {
  return (
    <div>
      <div style={{ fontWeight: 500, color: 'var(--navy-900)', marginBottom: 10, fontSize: 14 }}>{title}</div>
      <ul style={{ listStyle: 'none', padding: 0, margin: 0, display: 'flex', flexDirection: 'column', gap: 6 }}>
        {lines.map(l => <li key={l} style={{ fontSize: 13, color: 'var(--ink-700)' }}>{l}</li>)}
      </ul>
    </div>
  );
}

/* ---------- mobile sticky booking bar (Airbnb-style) ---------- */
function MobileBookBar({ p }) {
  const scrollToReserve = () => {
    const el = document.getElementById('pp-reserve');
    if (el) el.scrollIntoView({ behavior: 'smooth', block: 'center' });
  };
  return (
    <>
      <style>{`
        .pp-bookbar { display: none; }
        @media (max-width: 1023px) {
          .pp-bookbar {
            display: flex; align-items: center; justify-content: space-between; gap: 14px;
            position: fixed; left: 0; right: 0; bottom: 0; z-index: 60;
            padding: 12px 18px calc(12px + env(safe-area-inset-bottom));
            background: rgba(255,255,255,0.96);
            backdrop-filter: blur(16px); -webkit-backdrop-filter: blur(16px);
            border-top: 1px solid var(--line);
            box-shadow: 0 -4px 24px rgba(15,42,66,0.10);
            animation: ppBarUp 360ms var(--ease-novus);
          }
          /* leave room so the footer/content isn't hidden behind the bar */
          body { padding-bottom: 76px; }
        }
        @keyframes ppBarUp { from { transform: translateY(100%); } to { transform: translateY(0); } }
        .pp-bookbar-price { font-feature-settings: "tnum"; }
        .pp-bookbar-price b { font-size: 18px; font-weight: 600; color: var(--navy-900); }
        .pp-bookbar-price span { font-size: 13px; color: var(--ink-500); }
        .pp-bookbar-price .rate-sub { display: block; font-size: 11px; color: var(--ink-500); margin-top: 1px; }
      `}</style>
      <div className="pp-bookbar">
        <div className="pp-bookbar-price">
          <b>${(p.rate || 0).toLocaleString()}</b> <span>/ night</span>
          <span className="rate-sub">★ {p.rating || '4.9'} · {p.reviews || 0} reviews</span>
        </div>
        <button className="btn btn-primary" onClick={scrollToReserve}
          style={{ padding: '14px 28px', fontSize: 15, minHeight: 50, flexShrink: 0 }}>
          Reserve
        </button>
      </div>
    </>
  );
}

/* ---------- reservation card ---------- */
function ReservationCard({ p }) {
  const today = new Date().toISOString().slice(0, 10);
  const inThreeDays = new Date(Date.now() + 3 * 86400000).toISOString().slice(0, 10);
  const inWeek = new Date(Date.now() + 7 * 86400000).toISOString().slice(0, 10);
  const params = new URLSearchParams(window.location.search);

  const [checkIn, setCheckIn] = React.useState(params.get('checkIn') || inThreeDays);
  const [checkOut, setCheckOut] = React.useState(params.get('checkOut') || inWeek);
  const [guests, setGuests] = React.useState(parseInt(params.get('guests') || '4', 10) || 4);
  const [open, setOpen] = React.useState(false);
  const [submitting, setSubmitting] = React.useState(false);
  const [requested, setRequested] = React.useState(false);
  const [error, setError] = React.useState('');
  const [serverQuote, setServerQuote] = React.useState(null);

  // Guard against malformed/invalid date params (e.g. hand-crafted URLs) so the
  // quote never renders NaN and the request can't be sent with bad dates.
  const _ci = new Date(checkIn), _co = new Date(checkOut);
  const validDates = !isNaN(_ci.getTime()) && !isNaN(_co.getTime()) && _co > _ci;
  const nights = validDates ? novusNights(checkIn, checkOut) : 0;
  // Local (demo / fallback) quote computed with the backend-matching formula.
  const localQuote = novusComputeQuote(p.rate, validDates ? nights : 1);
  // In live mode the server returns the authoritative quote; display that when present.
  const quote = serverQuote || localQuote;
  const subtotal = quote.subtotal;
  const cleaning = quote.cleaning;
  const tax = quote.taxes;
  const grand = quote.total;
  const deposit = quote.deposit != null ? quote.deposit : Math.round(grand * 0.5);

  async function handleReserve() {
    if (submitting || !validDates) return;
    setError('');
    setSubmitting(true);
    try {
      if (window.NovusAPI) {
        const resp = await window.NovusAPI.createBookingRequest({
          listingId: p.hospitableId || null,
          propertyId: p.id,
          propertyName: p.name,
          checkIn, checkOut,
          guests: Number(guests) || 1,
          fallbackNightly: p.rate,
          guest: {},
        });
        // Live mode returns the server-computed quote — display it.
        if (resp && resp.quote) setServerQuote(resp.quote);
      }
      setRequested(true);
    } catch (e) {
      console.error('Booking request failed:', e);
      setError(e.message || 'Could not send your request. Please try again.');
    } finally {
      setSubmitting(false);
    }
  }

  return (
    <aside style={{
      position: 'sticky', top: 96, alignSelf: 'flex-start',
      background: '#fff', border: '1px solid var(--line)',
      borderRadius: 18, padding: 28, boxShadow: 'var(--shadow-soft)',
    }}>
      <div style={{ display: 'flex', alignItems: 'baseline', justifyContent: 'space-between', marginBottom: 18 }}>
        <div>
          <span style={{ fontFamily: 'var(--font-text)', fontFeatureSettings: '"tnum"', fontSize: 28, fontWeight: 500, color: 'var(--navy-900)' }}>
            ${p.rate.toLocaleString()}
          </span>
          <span style={{ fontSize: 14, color: 'var(--ink-500)' }}> / night</span>
        </div>
        <div style={{ fontSize: 12, color: 'var(--ink-500)', display: 'flex', alignItems: 'center', gap: 4 }}>
          <span style={{ color: 'var(--sun-coral)' }}>★</span>{p.rating}
        </div>
      </div>

      <div style={{ border: '1px solid var(--line)', borderRadius: 12, overflow: 'hidden' }}>
        <div style={{ display: 'grid', gridTemplateColumns: '1fr 1fr' }}>
          <DateInput label="Check in" value={checkIn} setValue={setCheckIn} type="date" min={today} />
          <DateInput label="Check out" value={checkOut} setValue={setCheckOut} type="date" min={checkIn} borderLeft />
        </div>
        <button onClick={() => setOpen(o => !o)} style={{
          width: '100%', borderTop: '1px solid var(--line)', background: '#fff',
          padding: '12px 16px', textAlign: 'left', cursor: 'pointer', border: 0,
          display: 'flex', justifyContent: 'space-between', alignItems: 'center',
        }}>
          <div>
            <div style={{ fontSize: 10, letterSpacing: '0.14em', textTransform: 'uppercase', color: 'var(--ink-500)', fontWeight: 600 }}>Guests</div>
            <div style={{ fontSize: 14, color: 'var(--navy-900)', marginTop: 2 }}>{guests} guests</div>
          </div>
          <span style={{ color: 'var(--ink-500)' }}>▾</span>
        </button>
        {open && (
          <div style={{ padding: '12px 16px', borderTop: '1px solid var(--line)', display: 'flex', justifyContent: 'space-between' }}>
            <span style={{ fontSize: 13, color: 'var(--ink-700)' }}>Adults</span>
            <span style={{ display: 'flex', gap: 8, alignItems: 'center' }}>
              <Step onClick={() => setGuests(g => Math.max(1, g - 1))}>−</Step>
              <span style={{ minWidth: 24, textAlign: 'center', fontFeatureSettings: '"tnum"' }}>{guests}</span>
              <Step onClick={() => setGuests(g => g + 1)}>+</Step>
            </span>
          </div>
        )}
      </div>

      {requested ? (
        <div style={{ marginTop: 16, padding: 16, borderRadius: 12, background: 'var(--sand-50)', border: '1px solid var(--line)', textAlign: 'center' }}>
          <div style={{ fontFamily: 'var(--font-display)', fontSize: 20, color: 'var(--navy-900)', marginBottom: 4 }}>Request received</div>
          <div style={{ fontSize: 13, color: 'var(--ink-700)', lineHeight: 1.5 }}>
            Your host will confirm and send a secure link for the 50% deposit
            {' '}(${deposit.toLocaleString()}).
          </div>
        </div>
      ) : (
        <>
          <button className="btn btn-primary" disabled={submitting || !validDates} onClick={handleReserve}
                  style={{ width: '100%', justifyContent: 'center', marginTop: 16, padding: 16, opacity: (submitting || !validDates) ? 0.7 : 1 }}>
            {submitting ? 'Sending request…' : 'Request to book'}
          </button>
          {!validDates && (
            <div style={{ marginTop: 10, fontSize: 13, color: 'var(--ink-500)', textAlign: 'center' }}>
              Select valid check-in and check-out dates.
            </div>
          )}
          {error && (
            <div role="alert" style={{ marginTop: 10, fontSize: 13, color: '#b4231f', textAlign: 'center' }}>
              {error}
            </div>
          )}
          <div style={{ textAlign: 'center', marginTop: 10, fontSize: 12, color: 'var(--ink-500)' }}>
            You won’t be charged yet — host confirms, then a 50% deposit link is sent.
          </div>
        </>
      )}

      {validDates && (
        <div style={{ marginTop: 20, display: 'flex', flexDirection: 'column', gap: 10, fontSize: 14 }}>
          <Row label={`$${p.rate.toLocaleString()} × ${nights} nights`} value={`$${subtotal.toLocaleString()}`} underline />
          <Row label="Cleaning fee" value={`$${cleaning.toLocaleString()}`} underline />
          <Row label="Taxes" value={`$${tax.toLocaleString()}`} underline />
          <hr style={{ border: 0, height: 1, background: 'var(--line)', margin: '6px 0' }} />
          <Row label={<strong style={{ color: 'var(--navy-900)' }}>Total</strong>} value={<strong style={{ color: 'var(--navy-900)' }}>${grand.toLocaleString()}</strong>} />
        </div>
      )}

      <div style={{ marginTop: 24, paddingTop: 16, borderTop: '1px solid var(--line)' }}>
        <div style={{ fontSize: 10, letterSpacing: '0.14em', textTransform: 'uppercase', color: 'var(--ink-500)', fontWeight: 600, marginBottom: 10 }}>
          Available on
        </div>
        <div style={{ display: 'flex', flexWrap: 'wrap', gap: 8 }}>
          {['Novus direct', 'Airbnb', 'VRBO', 'Booking.com'].map(c => (
            <span key={c} style={{
              padding: '6px 12px', border: '1px solid var(--horizon-200)',
              borderRadius: 999, fontSize: 11, color: 'var(--navy-700)', fontWeight: 500,
            }}>{c}</span>
          ))}
        </div>
      </div>
    </aside>
  );
}

function DateInput({ label, value, setValue, borderLeft, type, min }) {
  return (
    <label style={{ padding: '10px 16px', borderLeft: borderLeft ? '1px solid var(--line)' : 0, cursor: 'text' }}>
      <div style={{ fontSize: 10, letterSpacing: '0.14em', textTransform: 'uppercase', color: 'var(--ink-500)', fontWeight: 600 }}>{label}</div>
      <input value={value} onChange={e => setValue(e.target.value)} type={type || 'text'} min={min}
        style={{ border: 0, outline: 'none', background: 'transparent', width: '100%', padding: 0, marginTop: 2, fontSize: 14, color: 'var(--navy-900)', fontFeatureSettings: '"tnum"' }}/>
    </label>
  );
}
function Step({ children, onClick }) {
  return (
    <button onClick={onClick} style={{
      width: 28, height: 28, borderRadius: 999, border: '1px solid var(--line)',
      background: '#fff', cursor: 'pointer', color: 'var(--navy-700)',
    }}>{children}</button>
  );
}
function Row({ label, value, underline }) {
  return (
    <div style={{ display: 'flex', justifyContent: 'space-between', color: 'var(--ink-700)' }}>
      <span style={{ textDecoration: underline ? 'underline' : 'none', textDecorationStyle: 'dotted', textUnderlineOffset: 3 }}>{label}</span>
      <span style={{ fontFeatureSettings: '"tnum"' }}>{value}</span>
    </div>
  );
}

window.PropertyPage = PropertyPage;
