/* NRI Property Guard — owner web dashboard logic */
(function () {
  var sb = window.sbClient;
  var N = window.NRIPG;

  var State = {
    session: null, userId: null, profile: null, customerId: null, packageTier: 'regular',
    properties: [], visits: [], reports: [], invoices: [], documents: [], tickets: [], notifications: [],
    selectedPropertyId: null, selectedVisitId: null, capturedLat: null, capturedLng: null, currentView: 'home', channel: null
  };

  var PRESET_QUESTIONS = [
    { label: '📅 Inspection schedule', subject: 'Question about my inspection schedule', body: 'I would like to know when my next site visit is scheduled.' },
    { label: '📄 Downloading a report', subject: 'Help downloading my report', body: "I'm having trouble downloading my property inspection report." },
    { label: '📍 GPS / location concern', subject: 'GPS / location concern', body: 'I have a question about the location or GPS coordinates recorded for my property.' },
    { label: '✏️ Update property details', subject: 'Update my property details', body: 'I need to update some details on my registered property.' },
    { label: '💳 Invoice / payment question', subject: 'Invoice / payment question', body: 'I have a question about my invoice or payment.' },
    { label: '❓ Something else', subject: '', body: '' }
  ];

  /* ============================== BOOT ============================== */
  async function boot() {
    var { data } = await sb.auth.getSession();
    if (!data || !data.session) { location.href = 'index.html'; return; }
    State.session = data.session; State.userId = data.session.user.id;

    // This dashboard is the Property Owner (customer) view only. Admin, Property
    // Manager and Property Executive accounts have their own web dashboards now —
    // send them back through index.html, which routes each role to its own screen,
    // rather than silently creating a customer row for a staff account and showing
    // them owner data that isn't theirs.
    var roleRes = await sb.from('users').select('role').eq('id', State.userId).maybeSingle();
    var role = (roleRes.data && roleRes.data.role) || 'customer';
    if (role !== 'customer') { location.href = 'index.html'; return; }

    var custRes = await sb.from('customers').select('id, package_tier').eq('user_id', State.userId).maybeSingle();
    if (!custRes.data) {
      var ins = await sb.from('customers').insert({ user_id: State.userId, package_tier: 'regular' }).select('id, package_tier').single();
      custRes.data = ins.data;
    }
    State.customerId = custRes.data.id;
    State.packageTier = custRes.data.package_tier || 'regular';

    var userRes = await sb.from('users').select('*').eq('id', State.userId).maybeSingle();
    State.profile = userRes.data || { name: data.session.user.email, email: data.session.user.email };

    document.getElementById('avatarChip').textContent = N.initials(State.profile.name || State.profile.email);
    document.getElementById('homeGreeting').textContent = 'Welcome back, ' + (State.profile.name || 'there').split(' ')[0] + ' 👋';

    document.getElementById('loadingGate').style.display = 'none';
    document.getElementById('appShell').style.display = '';

    bindNav();
    bindModals();
    bindForms();

    await Promise.all([loadProperties(), loadHome()]);
    setupRealtime();
    // Backstop refresh on top of Realtime (which only covers site_visits/
    // properties/invoices): every 30s, silently reload whatever's on screen so
    // nothing on this dashboard ever needs a manual page reload to catch up.
    N.startAutoRefresh(function () { refreshCurrentView(true); }, 30000);

    if (location.hash === '#register') openModal('registerModal');
  }

  /* ============================== NAV ============================== */
  function bindNav() {
    document.querySelectorAll('.side-nav a[data-view]').forEach(function (a) {
      a.addEventListener('click', function () { switchView(a.dataset.view); });
    });
    document.querySelectorAll('[data-view-link]').forEach(function (a) {
      a.addEventListener('click', function (e) { e.preventDefault(); switchView(a.dataset.viewLink); });
    });
    document.getElementById('menuBtn').addEventListener('click', function () {
      document.getElementById('sidebar').classList.toggle('open');
    });
    document.getElementById('logoutBtn').addEventListener('click', async function () {
      await sb.auth.signOut();
      location.href = 'index.html';
    });
    document.getElementById('backToProperties').addEventListener('click', function () { switchView('property'); });
    document.getElementById('backToVisits').addEventListener('click', function () { switchView('visits'); });
  }

  var TITLES = { home: 'Home', property: 'My Property', propdetail: 'Property Detail', visits: 'Site Visits', visitdetail: 'Visit Detail', reports: 'Reports', documents: 'Documents & Invoices', location: 'Location & Nearby', support: 'Support', notifications: 'Notifications', sos: 'Emergency SOS', profile: 'Profile' };

  function switchView(name) {
    document.querySelectorAll('.view').forEach(function (v) { v.classList.remove('active'); });
    var el = document.getElementById('view-' + name);
    if (el) el.classList.add('active');
    document.querySelectorAll('.side-nav a[data-view]').forEach(function (a) { a.classList.toggle('active', a.dataset.view === name); });
    document.getElementById('viewTitle').textContent = TITLES[name] || name;
    document.getElementById('sidebar').classList.remove('open');
    State.currentView = name;

    // Always reload (not just "if empty") — Home's widget already populates
    // State.visits with a capped top-5 preview, which used to make this guard
    // skip loadVisits() entirely and leave the Site Visits tab stuck on its
    // loading skeleton forever for any owner who had at least one visit.
    if (name === 'visits') loadVisits();
    if (name === 'reports') loadReports();
    if (name === 'documents') loadDocumentsInvoices();
    if (name === 'location') loadLocation();
    if (name === 'support') loadSupport();
    if (name === 'notifications') loadNotifications();
    if (name === 'sos') loadSOS();
    if (name === 'profile') loadProfile();
  }

  /* ============================== HOME ============================== */
  async function loadHome() {
    var totalProps = State.properties.length;
    var approved = State.properties.filter(function (p) { return p.workflow_status === 'approved'; }).length;
    var inProgress = State.properties.filter(function (p) { return ['submitted', 'admin_review', 'assigned_to_manager', 'executive_assigned', 'visit_scheduled', 'inspection_started', 'inspection_completed', 'pending_manager_approval'].indexOf(p.workflow_status) !== -1; }).length;

    var visitsRes = await sb.from('site_visits').select('*, properties(property_name)').eq('customer_id', State.customerId).order('scheduled_date', { ascending: true }).limit(5);
    State.visits = visitsRes.data || [];
    var upcoming = State.visits.filter(function (v) { return ['scheduled', 'assigned', 'reached', 'in_progress'].indexOf(v.status) !== -1; }).length;

    document.getElementById('homeStats').innerHTML = [
      statCard('Registered Properties', totalProps),
      statCard('Approved & Verified', approved),
      statCard('In Progress', inProgress),
      statCard('Upcoming Visits', upcoming)
    ].join('');

    renderHomeVisits();

    var notifRes = await sb.from('notifications').select('*').eq('user_id', State.userId).order('created_at', { ascending: false }).limit(5);
    State.notifications = notifRes.data || [];
    renderHomeNotifs();
    updateNotifBadge();
  }
  function statCard(label, val) { return '<div class="stat-card"><div class="lbl">' + label + '</div><div class="val">' + val + '</div></div>'; }

  function renderHomeVisits() {
    var el = document.getElementById('homeVisits');
    if (!State.visits.length) { el.innerHTML = emptyState('📅', 'No site visits scheduled yet.'); return; }
    el.innerHTML = State.visits.map(function (v) {
      return '<div class="prop-card" style="cursor:default;"><div class="row1"><b>' + N.escapeHtml((v.properties && v.properties.property_name) || 'Property') + '</b><span class="status-chip status-' + v.status + '">' + N.statusLabel(v.status) + '</span></div><div class="meta">' + N.fmtDateTime(v.scheduled_date) + (v.auto_scheduled ? ' · Auto-scheduled follow-up' : '') + '</div></div>';
    }).join('');
  }

  function renderHomeNotifs() {
    var el = document.getElementById('homeNotifs');
    if (!State.notifications.length) { el.innerHTML = emptyState('🔔', 'No notifications yet.'); return; }
    el.innerHTML = State.notifications.map(notifRow).join('');
  }

  function notifRow(n) {
    return '<div class="notif-item ' + (n.read ? '' : 'unread') + '"><div class="ic">🔔</div><div><b>' + N.escapeHtml(n.title) + '</b><p style="margin:2px 0;font-size:.85rem;">' + N.escapeHtml(n.body) + '</p><span class="time">' + N.fmtDateTime(n.created_at) + '</span></div></div>';
  }

  function emptyState(icon, msg) { return '<div class="empty-state"><div class="ic">' + icon + '</div><p class="mb-0">' + msg + '</p></div>'; }

  /* ============================== MY PROPERTY ============================== */
  // Perf (Phase 9): a signed Storage URL for the same photo is reused for up to
  // THUMB_URL_TTL_MS instead of being re-signed on every call. Before this fix,
  // loadProperties() ran on every 30s auto-refresh tick AND on every Realtime
  // event (any change to this owner's properties/visits/invoices) regardless of
  // which tab was open, and State.properties was rebuilt from scratch each time
  // — so every property's thumbnail got a brand-new signed URL every ~30s. A
  // signed URL is a distinct string each time it's (re-)signed, so the browser
  // could never recognize it as "the same image it already has" and re-downloaded
  // every thumbnail from Storage on every tick, even when nothing about the
  // photo had changed and even while the user was sitting on an unrelated tab.
  // Caching by storage_path (which only changes if the photo itself changes)
  // fixes both: far fewer Storage API calls, and a stable URL the browser's own
  // HTTP cache can actually serve from cache on repeat renders.
  var thumbUrlCache = {}; // storage_path -> { url, ts }
  var THUMB_URL_TTL_MS = 5 * 60 * 1000; // signed URLs are valid 600s; reuse well inside that window
  async function loadProperties() {
    var res = await sb.from('properties').select('*, users:manager_id(name)').eq('customer_id', State.customerId).order('created_at', { ascending: false });
    if (res.error) { console.error(res.error); State.properties = []; }
    else State.properties = res.data || [];

    // One thumbnail per property, using the photo submitted at registration time
    // (media_source='initial') rather than an inspection photo, so the card
    // always has a picture even before any visit has happened. Fetched here (not
    // inline in the render) because turning a storage path into a viewable image
    // needs a signed URL, which is an async Supabase Storage call.
    var propIds = State.properties.map(function (p) { return p.id; });
    if (propIds.length) {
      var thumbRes = await sb.from('visit_photos').select('property_id, storage_path, captured_at')
        .in('property_id', propIds).eq('media_source', 'initial').order('captured_at', { ascending: true });
      var firstByProperty = {};
      (thumbRes.data || []).forEach(function (row) { if (!firstByProperty[row.property_id]) firstByProperty[row.property_id] = row; });
      var now = Date.now();
      // Each signed-url fetch is isolated with its own try/catch so one property's
      // thumbnail failing to sign (network hiccup, missing file, etc.) never takes
      // down the rest of the list — it should just fall back to the placeholder
      // icon for that one card, not block loadProperties() from ever reaching
      // renderPropertyList() below.
      await Promise.all(State.properties.map(async function (p) {
        var row = firstByProperty[p.id];
        if (!row) return;
        var cached = thumbUrlCache[row.storage_path];
        if (cached && (now - cached.ts) < THUMB_URL_TTL_MS) { p._thumbUrl = cached.url; return; }
        try {
          var signed = await sb.storage.from('property-images').createSignedUrl(row.storage_path, 600);
          if (signed && signed.data) {
            p._thumbUrl = signed.data.signedUrl;
            thumbUrlCache[row.storage_path] = { url: signed.data.signedUrl, ts: now };
          }
        } catch (e) { /* leave p._thumbUrl unset — card falls back to the placeholder icon */ }
      }));
    }

    renderPropertyList();
  }

  function renderPropertyList() {
    var el = document.getElementById('propertyList');
    if (!State.properties.length) {
      el.innerHTML = emptyState('🏘️', "You haven't registered a property yet.") +
        '<div class="text-center"><button class="btn btn-primary btn-sm" onclick="document.getElementById(\'openRegisterBtn\').click()">Register Your First Property</button></div>';
      return;
    }
    el.innerHTML = State.properties.map(function (p) {
      return '<div class="prop-card" data-prop-id="' + p.id + '">' +
        '<div class="prop-thumb">' + (p._thumbUrl ? '<img src="' + N.escapeHtml(p._thumbUrl) + '" alt="' + N.escapeHtml(p.property_name) + '" loading="lazy" decoding="async">' : '🏠') + '</div>' +
        '<div class="row1"><b>' + N.escapeHtml(p.property_name) + '</b><span class="status-chip status-' + p.workflow_status + '">' + N.statusLabel(p.workflow_status) + '</span></div>' +
        '<div class="meta">' + N.escapeHtml(p.city) + ', ' + N.escapeHtml(p.state) + ' · ' + N.propertyTypeLabel(p.property_type) + '</div>' +
        '<div class="chip-row">' +
          '<span class="mini-chip">Assigned: ' + (p.users && p.users.name ? N.escapeHtml(p.users.name) : 'Not yet assigned') + '</span>' +
          '<span class="mini-chip">Condition: ' + N.statusLabel(p.status) + '</span>' +
          '<span class="mini-chip">Added ' + N.fmtDate(p.created_at) + '</span>' +
        '</div></div>';
    }).join('');
    el.querySelectorAll('.prop-card[data-prop-id]').forEach(function (card) {
      card.addEventListener('click', function () { openPropertyDetail(card.dataset.propId); });
    });
  }

  async function openPropertyDetail(propertyId) {
    State.selectedPropertyId = propertyId;
    switchView('propdetail');
    var body = document.getElementById('propDetailBody');
    body.innerHTML = '<div class="skel" style="height:200px;"></div>';

    var p = State.properties.find(function (x) { return x.id === propertyId; });
    if (!p) return;

    var visitsRes = await sb.from('site_visits').select('*').eq('property_id', propertyId).order('scheduled_date', { ascending: false });
    var visits = visitsRes.data || [];

    var photosRes = await sb.from('visit_photos').select('*').eq('property_id', propertyId)
      .or('and(media_source.eq.initial,owner_id.eq.' + State.customerId + '),approval_status.eq.approved')
      .order('captured_at', { ascending: false });
    var photos = photosRes.data || [];

    var videosRes = await sb.from('visit_videos').select('*').eq('property_id', propertyId)
      .or('and(media_source.eq.initial,owner_id.eq.' + State.customerId + '),approval_status.eq.approved')
      .order('captured_at', { ascending: false });
    var videos = videosRes.data || [];

    var reportsRes = await sb.from('property_reports').select('*').eq('property_id', propertyId).order('version', { ascending: false });
    var reports = reportsRes.data || [];

    // Latest inspection checklist for this property, pulled via its most recent
    // site visit that has one — this is the same checklist data shown on the
    // per-visit Visit Detail screen, surfaced here too so "My Property" alone
    // gives the same at-a-glance picture the mobile app's property screen does.
    var visitIds = visits.map(function (v) { return v.id; });
    var latestReport = null;
    if (visitIds.length) {
      var reportRes = await sb.from('inspection_reports').select('*').in('site_visit_id', visitIds).order('submitted_at', { ascending: false }).limit(1).maybeSingle();
      latestReport = reportRes.data || null;
    }
    var checklistHtml = '';
    if (latestReport && latestReport.checklist && latestReport.checklist.length) {
      checklistHtml = '<ul class="checklist-view">' + latestReport.checklist.map(function (c) {
        return '<li>' + (c.checked ? '✅' : '⬜') + ' ' + N.escapeHtml(c.item || '') + '</li>';
      }).join('') + '</ul>';
    }

    body.innerHTML =
      '<div class="card">' +
        '<div class="row1" style="display:flex;justify-content:space-between;align-items:center;"><h2 style="margin:0;">' + N.escapeHtml(p.property_name) + '</h2><span class="status-chip status-' + p.workflow_status + '">' + N.statusLabel(p.workflow_status) + '</span></div>' +
        '<p style="margin-top:8px;">' + N.escapeHtml(p.address) + ', ' + N.escapeHtml(p.city) + ', ' + N.escapeHtml(p.state) + ' ' + N.escapeHtml(p.pincode || '') + ', ' + N.escapeHtml(p.country) + '</p>' +
        '<div class="chip-row">' +
          '<span class="mini-chip">Property Assigned Date: ' + N.fmtDate(p.created_at) + '</span>' +
          '<span class="mini-chip">' + N.propertyTypeLabel(p.property_type) + '</span>' +
          '<span class="mini-chip">' + N.escapeHtml(p.city) + '</span>' +
          '<span class="mini-chip">Manager: ' + (p.users && p.users.name ? N.escapeHtml(p.users.name) : 'Not yet assigned') + '</span>' +
          '<span class="mini-chip">Condition: ' + N.statusLabel(p.status) + '</span>' +
        '</div>' +
        (p.google_map_url ? '<p style="margin-top:12px;"><a href="' + N.escapeHtml(p.google_map_url) + '" target="_blank" rel="noopener">📍 Open in Google Maps →</a></p>' : (p.latitude ? '<p style="margin-top:12px;"><a href="https://www.google.com/maps/search/?api=1&query=' + p.latitude + ',' + p.longitude + '" target="_blank" rel="noopener">📍 Open in Google Maps →</a></p>' : '')) +
      '</div>' +

      '<div class="card"><h3>Status Timeline</h3><ul class="timeline-mini">' + statusTimeline(p.workflow_status) + '</ul></div>' +

      '<div class="card"><h3>Latest Inspection Checklist &amp; Notes</h3>' +
        (latestReport ? (
          '<div class="chip-row"><span class="mini-chip">Condition: ' + N.statusLabel(latestReport.property_condition) + '</span></div>' +
          (checklistHtml || '<p class="help-note">No checklist items recorded on the latest inspection.</p>') +
          (latestReport.notes ? '<p style="margin-top:12px;"><b>Notes:</b> ' + N.escapeHtml(latestReport.notes) + '</p>' : '') +
          (latestReport.observations ? '<p><b>Observations:</b> ' + N.escapeHtml(latestReport.observations) + '</p>' : '')
        ) : emptyState('📋', 'The inspection checklist appears here once your Property Manager approves your first visit.')) +
      '</div>' +

      '<div class="card"><div class="card-head"><h3>Report History</h3></div>' +
        (reports.length ? '<table class="table-simple"><thead><tr><th>Report #</th><th>Version</th><th>Generated</th><th></th></tr></thead><tbody>' +
          reports.map(function (r) { return '<tr><td>' + N.escapeHtml(r.report_number || '—') + '</td><td>v' + r.version + '</td><td>' + N.fmtDate(r.generated_at) + '</td><td><button class="btn btn-outline btn-sm" data-download="inspection-reports" data-path="' + N.escapeHtml(r.storage_path) + '">Download</button></td></tr>'; }).join('') +
          '</tbody></table>' :
          emptyState('📊', 'No report generated yet. A report is created automatically the moment your Property Manager approves your first inspection.')) +
      '</div>' +

      '<div class="card"><h3>Photos <span style="color:var(--ink-500);font-weight:400;font-size:.85rem;">(' + photos.length + ' approved)</span></h3>' +
        (photos.length ? '<div class="media-grid">' + photos.map(mediaTile.bind(null, 'property-images', 'photo')).join('') + '</div>' : emptyState('📸', 'No approved photos yet.')) +
      '</div>' +

      '<div class="card"><h3>Videos <span style="color:var(--ink-500);font-weight:400;font-size:.85rem;">(' + videos.length + ' approved)</span></h3>' +
        (videos.length ? '<div class="media-grid">' + videos.map(mediaTile.bind(null, 'property-videos', 'video')).join('') + '</div>' : emptyState('🎥', 'No approved videos yet.')) +
      '</div>';

    bindDownloadButtons(body);
    bindMediaTiles(body);
  }

  function statusTimeline(status) {
    var order = ['submitted', 'admin_review', 'assigned_to_manager', 'executive_assigned', 'visit_scheduled', 'inspection_started', 'inspection_completed', 'pending_manager_approval', 'approved'];
    var idx = order.indexOf(status);
    var labels = { submitted: 'Submitted by you', admin_review: 'Under Admin review', assigned_to_manager: 'Assigned to Property Manager', executive_assigned: 'Property Executive assigned', visit_scheduled: 'Site visit scheduled', inspection_started: 'Inspection in progress', inspection_completed: 'Inspection completed', pending_manager_approval: 'Awaiting Manager approval', approved: 'Approved & verified' };
    if (status === 'rejected') return '<li>Submitted</li><li>Reviewed</li><li style="color:var(--red-600);">Rejected — see Manager remarks on the latest visit, or raise a support ticket</li>';
    if (status === 'revisit_requested') return '<li>Submitted</li><li>Inspected</li><li style="color:var(--gold-700);">Re-visit requested — your Executive will return</li>';
    return order.map(function (s, i) { return '<li style="' + (i <= idx ? 'font-weight:600;color:var(--navy-900);' : 'color:var(--ink-300);') + '">' + labels[s] + '</li>'; }).join('');
  }

  function mediaTile(bucket, kind, m) {
    // Photos/videos are shown plain, with no timestamp or GPS overlay — that
    // metadata is still captured and stored (captured_at/gps_lat/gps_lng on the
    // row) for the tamper-evident audit trail and inspection report, it's just
    // not rendered on top of the media itself (Phase 8, per explicit request).
    return '<div class="media-tile" data-media-bucket="' + bucket + '" data-media-path="' + N.escapeHtml(m.storage_path) + '" data-media-kind="' + kind + '">' +
      (kind === 'photo' ? '🖼️' : '🎬') + '</div>';
  }

  function bindMediaTiles(scope) {
    scope.querySelectorAll('.media-tile').forEach(function (tile) {
      tile.addEventListener('click', async function () {
        var bucket = tile.dataset.mediaBucket, path = tile.dataset.mediaPath, kind = tile.dataset.mediaKind;
        var signed = await sb.storage.from(bucket).createSignedUrl(path, 600);
        if (signed.error || !signed.data) { N.toast('Could not open this file.', 'error'); return; }
        var url = signed.data.signedUrl;
        document.getElementById('lightboxBody').innerHTML = '<div class="lightbox-media-wrap">' + (kind === 'photo'
          ? '<img src="' + url + '">'
          : '<video src="' + url + '" controls autoplay></video>') + '</div>';
        openModal('lightboxModal');
      });
    });
  }

  function bindDownloadButtons(scope) {
    scope.querySelectorAll('[data-download]').forEach(function (btn) {
      btn.addEventListener('click', async function () {
        var bucket = btn.dataset.download, path = btn.dataset.path;
        if (!path) { N.toast('This file is not available yet.', 'error'); return; }
        var signed = await sb.storage.from(bucket).createSignedUrl(path, 600, { download: true });
        if (signed.error || !signed.data) { N.toast('Could not generate a download link.', 'error'); return; }
        window.open(signed.data.signedUrl, '_blank');
      });
    });
  }

  /* ============================== REGISTER PROPERTY ============================== */
  document.getElementById('openRegisterBtn') && document.getElementById('openRegisterBtn').addEventListener('click', function () { openModal('registerModal'); });

  function bindForms() {
    document.getElementById('openRegisterBtn').addEventListener('click', function () { openModal('registerModal'); });
    document.getElementById('useCurrentLocBtn').addEventListener('click', function () {
      if (!navigator.geolocation) { N.toast('Location is not supported on this browser.', 'error'); return; }
      document.getElementById('latlngNote').textContent = 'Locating…';
      navigator.geolocation.getCurrentPosition(function (pos) {
        State.capturedLat = pos.coords.latitude; State.capturedLng = pos.coords.longitude;
        document.getElementById('latlngNote').textContent = 'Captured: ' + pos.coords.latitude.toFixed(6) + ', ' + pos.coords.longitude.toFixed(6);
        var mapInput = document.querySelector('#registerPropertyForm [name=google_map_url]');
        if (!mapInput.value) mapInput.value = 'https://www.google.com/maps/search/?api=1&query=' + pos.coords.latitude + ',' + pos.coords.longitude;
      }, function () {
        document.getElementById('latlngNote').textContent = 'Could not get your current location — you can still paste a Google Maps link.';
      });
    });

    document.getElementById('registerPropertyForm').addEventListener('submit', onRegisterSubmit);
    document.getElementById('ticketForm').addEventListener('submit', onTicketSubmit);
    document.getElementById('replyForm').addEventListener('submit', onReplySubmit);
    document.getElementById('openTicketBtn').addEventListener('click', function () { openModal('ticketModal'); populateTicketPropertySelect(); });
    document.getElementById('markAllReadBtn').addEventListener('click', markAllNotifsRead);

    document.getElementById('presetChips').innerHTML = PRESET_QUESTIONS.map(function (q, i) {
      return '<button type="button" class="preset-chip" data-idx="' + i + '">' + q.label + '</button>';
    }).join('');
    document.getElementById('presetChips').querySelectorAll('.preset-chip').forEach(function (chip) {
      chip.addEventListener('click', function () {
        var q = PRESET_QUESTIONS[chip.dataset.idx];
        document.getElementById('ticketSubject').value = q.subject;
        document.getElementById('ticketDescription').value = q.body;
      });
    });
  }

  async function onRegisterSubmit(e) {
    e.preventDefault();
    var form = e.target;
    var btn = document.getElementById('submitRegisterBtn');
    var alertEl = document.getElementById('regPropAlert');
    alertEl.className = 'alert';
    btn.disabled = true; btn.innerHTML = '<span class="loading-spin"></span> Submitting…';

    try {
      var fd = new FormData(form);
      var payload = {
        customer_id: State.customerId,
        property_name: fd.get('property_name'),
        property_type: fd.get('property_type'),
        address: fd.get('address'),
        country: fd.get('country') || 'India',
        state: fd.get('state'),
        district: fd.get('district') || '',
        city: fd.get('city'),
        pincode: fd.get('pincode'),
        google_map_url: fd.get('google_map_url') || null,
        latitude: State.capturedLat,
        longitude: State.capturedLng,
        size_sqft: fd.get('size_sqft') ? Number(fd.get('size_sqft')) : null,
        floors: fd.get('floors') ? Number(fd.get('floors')) : null,
        description: fd.get('description') || '',
        preferred_visit_date: fd.get('preferred_visit_date') || null,
        preferred_visit_time: fd.get('preferred_visit_time') || ''
      };
      var ins = await sb.from('properties').insert(payload).select('*').single();
      if (ins.error) throw ins.error;
      var propertyId = ins.data.id;

      var photoFiles = form.querySelector('[name=photos]').files;
      for (var i = 0; i < photoFiles.length; i++) {
        var f = photoFiles[i];
        var path = 'initial/' + propertyId + '/' + Date.now() + '_' + i + '_' + f.name.replace(/[^a-zA-Z0-9._-]/g, '_');
        var up = await sb.storage.from('property-images').upload(path, f);
        if (!up.error) {
          await sb.from('visit_photos').insert({ property_id: propertyId, owner_id: State.customerId, media_source: 'initial', storage_path: path, captured_at: new Date().toISOString(), gps_lat: State.capturedLat, gps_lng: State.capturedLng, file_size_bytes: f.size });
        }
      }

      var docFiles = form.querySelector('[name=documents]').files;
      for (var j = 0; j < docFiles.length; j++) {
        var df = docFiles[j];
        var dpath = State.customerId + '/' + propertyId + '/' + Date.now() + '_' + j + '_' + df.name.replace(/[^a-zA-Z0-9._-]/g, '_');
        var dup = await sb.storage.from('property-documents').upload(dpath, df);
        if (!dup.error) {
          await sb.from('documents').insert({ customer_id: State.customerId, property_id: propertyId, doc_type: 'kyc', title: df.name, storage_path: dpath, file_size_bytes: df.size, created_by_role: 'customer', uploaded_by: State.userId });
        }
      }

      alertEl.textContent = 'Property submitted! Our Admin team will review it shortly.';
      alertEl.className = 'alert success show';
      form.reset(); State.capturedLat = null; State.capturedLng = null;
      document.getElementById('latlngNote').textContent = 'No coordinates captured yet.';
      await loadProperties();
      setTimeout(function () { closeModal('registerModal'); switchView('property'); }, 1200);
    } catch (err) {
      alertEl.textContent = err.message || 'Could not register your property. Please try again.';
      alertEl.className = 'alert error show';
    } finally {
      btn.disabled = false; btn.textContent = 'Submit Property for Review';
    }
  }

  /* ============================== VISITS ============================== */
  async function loadVisits() {
    var res = await sb.from('site_visits').select('*, properties(property_name)').eq('customer_id', State.customerId).order('scheduled_date', { ascending: false });
    State.visits = res.data || [];
    var el = document.getElementById('visitsList');
    if (!State.visits.length) { el.innerHTML = emptyState('📅', 'No site visits yet.'); return; }
    el.innerHTML = '<table class="table-simple"><tbody>' +
      State.visits.map(function (v) {
        return '<tr class="visit-row" data-visit-id="' + v.id + '" style="cursor:pointer;"><td>' + N.escapeHtml((v.properties && v.properties.property_name) || '—') + '</td><td>' + N.fmtDateTime(v.scheduled_date) + '</td><td><span class="status-chip status-' + v.status + '">' + N.statusLabel(v.status) + (v.auto_scheduled ? ' · auto' : '') + '</span></td><td style="text-align:right;color:var(--ink-300);">View →</td></tr>';
      }).join('') + '</tbody></table>';
    el.querySelectorAll('.visit-row').forEach(function (row) {
      row.addEventListener('click', function () { openVisitDetail(row.dataset.visitId); });
    });
  }

  async function openVisitDetail(visitId) {
    State.selectedVisitId = visitId;
    switchView('visitdetail');
    var body = document.getElementById('visitDetailBody');
    body.innerHTML = '<div class="skel" style="height:200px;"></div>';

    var v = State.visits.find(function (x) { return x.id === visitId; });
    if (!v) { var vRes = await sb.from('site_visits').select('*, properties(property_name, address, city, state)').eq('id', visitId).maybeSingle(); v = vRes.data; }
    if (!v) { body.innerHTML = emptyState('📅', 'This visit could not be found.'); return; }
    var propName = (v.properties && v.properties.property_name) || 'Property';

    var photosRes = await sb.from('visit_photos').select('*').eq('site_visit_id', visitId).eq('approval_status', 'approved').order('captured_at', { ascending: false });
    var videosRes = await sb.from('visit_videos').select('*').eq('site_visit_id', visitId).eq('approval_status', 'approved').order('captured_at', { ascending: false });
    var reportRes = await sb.from('inspection_reports').select('*').eq('site_visit_id', visitId).order('submitted_at', { ascending: false }).limit(1).maybeSingle();
    var reviewRes = await sb.from('manager_reviews').select('*').eq('site_visit_id', visitId).order('reviewed_at', { ascending: false }).limit(1).maybeSingle();
    var photos = photosRes.data || [], videos = videosRes.data || [], report = reportRes.data, review = reviewRes.data;

    var checklistHtml = '';
    if (report && report.checklist && report.checklist.length) {
      checklistHtml = '<ul class="checklist-view">' + report.checklist.map(function (c) {
        return '<li>' + (c.checked ? '✅' : '⬜') + ' ' + N.escapeHtml(c.item || '') + '</li>';
      }).join('') + '</ul>';
    }

    body.innerHTML =
      '<div class="card">' +
        '<div class="row1" style="display:flex;justify-content:space-between;align-items:center;"><h2 style="margin:0;">' + N.escapeHtml(propName) + '</h2><span class="status-chip status-' + v.status + '">' + N.statusLabel(v.status) + '</span></div>' +
        (v.properties ? '<p style="margin-top:8px;">' + N.escapeHtml(v.properties.address || '') + ', ' + N.escapeHtml(v.properties.city || '') + ', ' + N.escapeHtml(v.properties.state || '') + '</p>' : '') +
        '<div class="chip-row"><span class="mini-chip">Scheduled: ' + N.fmtDateTime(v.scheduled_date) + '</span></div>' +
      '</div>' +

      '<div class="card"><h3>Inspection Checklist &amp; Notes</h3>' +
        (report ? (
          '<div class="chip-row"><span class="mini-chip">Condition: ' + N.statusLabel(report.property_condition) + '</span></div>' +
          (checklistHtml || '<p class="help-note">No checklist items recorded for this visit.</p>') +
          (report.notes ? '<p style="margin-top:12px;"><b>Notes:</b> ' + N.escapeHtml(report.notes) + '</p>' : '') +
          (report.observations ? '<p><b>Observations:</b> ' + N.escapeHtml(report.observations) + '</p>' : '')
        ) : emptyState('📋', 'The inspection checklist becomes visible here once your Property Manager approves this visit.')) +
      '</div>' +

      (review && review.comments ? '<div class="card"><h3>Manager Remarks</h3><p class="mb-0">' + N.escapeHtml(review.comments) + '</p></div>' : '') +

      '<div class="card"><h3>Photos <span style="color:var(--ink-500);font-weight:400;font-size:.85rem;">(' + photos.length + ' approved)</span></h3>' +
        (photos.length ? '<div class="media-grid">' + photos.map(mediaTile.bind(null, 'property-images', 'photo')).join('') + '</div>' : emptyState('📸', 'No approved photos from this visit yet.')) +
      '</div>' +

      '<div class="card"><h3>Videos <span style="color:var(--ink-500);font-weight:400;font-size:.85rem;">(' + videos.length + ' approved)</span></h3>' +
        (videos.length ? '<div class="media-grid">' + videos.map(mediaTile.bind(null, 'property-videos', 'video')).join('') + '</div>' : emptyState('🎥', 'No approved videos from this visit yet.')) +
      '</div>';

    bindMediaTiles(body);
  }

  /* ============================== REPORTS ============================== */
  async function loadReports() {
    var res = await sb.from('property_reports').select('*, properties(property_name)').eq('customer_id', State.customerId).order('generated_at', { ascending: false });
    State.reports = res.data || [];
    var el = document.getElementById('reportsList');
    if (!State.reports.length) { el.innerHTML = emptyState('📊', 'No reports generated yet. Reports are created automatically once your first inspection is approved.'); return; }
    el.innerHTML = '<div class="card"><table class="table-simple"><thead><tr><th>Property</th><th>Report #</th><th>Version</th><th>Generated</th><th></th></tr></thead><tbody>' +
      State.reports.map(function (r) {
        return '<tr><td>' + N.escapeHtml((r.properties && r.properties.property_name) || '—') + '</td><td>' + N.escapeHtml(r.report_number || '—') + '</td><td>v' + r.version + '</td><td>' + N.fmtDate(r.generated_at) + '</td><td><button class="btn btn-outline btn-sm" data-download="inspection-reports" data-path="' + N.escapeHtml(r.storage_path) + '">Download PDF</button></td></tr>';
      }).join('') + '</tbody></table></div>';
    bindDownloadButtons(el);
  }

  /* ============================== DOCUMENTS & INVOICES ============================== */
  // Card-based layout (documents and invoices each as individual rounded
  // cards, switched via a Documents/Invoices segmented control) matching
  // the mobile app's screens, rather than the plain tables this used to be.
  function bindDocInvTabs() {
    var tabs = document.querySelectorAll('.docinv-tab');
    if (!tabs.length || tabs[0]._bound) return;
    tabs.forEach(function (tab) {
      tab._bound = true;
      tab.addEventListener('click', function () {
        tabs.forEach(function (t) { t.classList.remove('active'); });
        tab.classList.add('active');
        document.querySelectorAll('.docinv-panel').forEach(function (p) { p.classList.remove('active'); });
        document.getElementById('docinvPanel' + (tab.dataset.docinvTab === 'invoices' ? 'Invoices' : 'Documents')).classList.add('active');
      });
    });
  }

  async function loadDocumentsInvoices() {
    bindDocInvTabs();

    var invRes = await sb.from('invoices').select('*').eq('customer_id', State.customerId).order('billing_month', { ascending: false });
    State.invoices = invRes.data || [];
    var invEl = document.getElementById('invoicesList');
    if (!State.invoices.length) {
      invEl.innerHTML = emptyState('🧾', 'No invoices yet — your first invoice is generated automatically once you register a property.');
    } else {
      invEl.innerHTML = State.invoices.map(function (inv) {
        var canPay = inv.status === 'due' || inv.status === 'overdue';
        var title = 'Monitoring subscription — ' + (inv.billing_month ? new Date(inv.billing_month).toLocaleDateString('en-IN', { month: 'long', year: 'numeric' }) : N.fmtDate(inv.created_at));
        return '<div class="invoice-card">' +
          '<div class="invoice-card-head">' +
            '<div><div class="invoice-title">' + N.escapeHtml(title) + '</div><div class="invoice-due">Due ' + N.fmtDate(inv.due_date) + '</div></div>' +
            '<div class="invoice-amount-col"><div class="invoice-amount">' + N.fmtMoney(inv.amount_inr) + '</div><span class="status-chip status-' + N.escapeHtml(inv.status) + '">' + N.statusLabel(inv.status) + '</span></div>' +
          '</div>' +
          (inv.rate_description ? '<div class="invoice-rate">' + N.escapeHtml(inv.rate_description) + '</div>' : '') +
          '<div class="invoice-card-foot">' +
            (inv.storage_path ? '<button class="btn btn-outline btn-sm" data-download="inspection-reports" data-path="' + N.escapeHtml(inv.storage_path) + '">⬇ Download PDF</button>' : '<span></span>') +
            (canPay ? '<button class="btn btn-primary btn-sm" data-pay-invoice data-amount="' + inv.amount_inr + '" data-note="' + N.escapeHtml(title) + '">💳 Pay Now</button>' : '') +
          '</div>' +
        '</div>';
      }).join('');
      bindDownloadButtons(invEl);
      bindPayButtons(invEl);
    }

    var docRes = await sb.from('documents').select('*, properties(property_name)').eq('customer_id', State.customerId).order('created_at', { ascending: false });
    State.documents = docRes.data || [];
    var docEl = document.getElementById('documentsList');
    if (!State.documents.length) { docEl.innerHTML = emptyState('📁', 'No documents uploaded yet.'); return; }
    docEl.innerHTML = State.documents.map(function (d) {
      return '<div class="doc-card">' +
        '<div class="doc-icon">📄</div>' +
        '<div class="doc-info"><div class="doc-title">' + N.escapeHtml(d.title) + '</div>' +
        '<div class="doc-meta">' + N.escapeHtml(d.doc_type) + (d.properties && d.properties.property_name ? ' · ' + N.escapeHtml(d.properties.property_name) : '') + ' · ' + N.fmtDate(d.created_at) + '</div></div>' +
        '<button class="doc-download-btn" data-download="property-documents" data-path="' + N.escapeHtml(d.storage_path) + '" title="Download" aria-label="Download">⬇</button>' +
      '</div>';
    }).join('');
    bindDownloadButtons(docEl);
  }

  // "Pay Now" opens a UPI payment-intent link (see N.buildUpiLink) — no
  // payment gateway is wired up yet, so this hands off to whatever UPI app
  // is on the customer's phone rather than confirming payment back to us.
  // Since there's no gateway to receive a webhook from, the invoice stays
  // "Due" until staff confirm receipt and update it — this button gets a
  // customer to the point of paying, it doesn't complete the loop end to
  // end. It only works from a mobile browser (there's no UPI app for a
  // desktop browser to hand the link to), so desktop visitors get the UPI
  // ID to pay manually instead of a dead click.
  function bindPayButtons(scope) {
    scope.querySelectorAll('[data-pay-invoice]').forEach(function (btn) {
      btn.addEventListener('click', function () {
        var amount = btn.dataset.amount, note = btn.dataset.note;
        if (!N.isLikelyMobile()) {
          N.toast('Pay by UPI to ' + N.UPI_VPA + ' (' + N.fmtMoney(amount) + ') from your phone, or open this on your phone to pay directly.', 'info');
          return;
        }
        var link = N.buildUpiLink(amount, note);
        var a = document.createElement('a');
        a.href = link;
        document.body.appendChild(a);
        a.click();
        document.body.removeChild(a);
      });
    });
  }

  /* ============================== LOCATION & NEARBY ============================== */
  async function loadLocation() {
    var el = document.getElementById('locationList');
    if (!State.properties.length) { el.innerHTML = emptyState('📍', 'Register a property to see its location here.'); return; }
    el.innerHTML = State.properties.map(function (p) {
      var mapUrl = p.google_map_url || (p.latitude ? ('https://www.google.com/maps/search/?api=1&query=' + p.latitude + ',' + p.longitude) : null);
      return '<div class="card"><h3>' + N.escapeHtml(p.property_name) + '</h3>' +
        '<p>' + N.escapeHtml(p.address) + ', ' + N.escapeHtml(p.city) + ', ' + N.escapeHtml(p.state) + '</p>' +
        (mapUrl ? '<a class="btn btn-outline btn-sm" href="' + N.escapeHtml(mapUrl) + '" target="_blank" rel="noopener">Open in Google Maps →</a>' : '<p class="help-note">No GPS location captured yet for this property.</p>') +
        '<p class="help-note" style="margin-top:14px;">Nearby schools, hospitals, transport and more appear automatically inside your generated Property Report once your account owner\'s Google Places API key is configured on the platform.</p>' +
        '</div>';
    }).join('');
  }

  /* ============================== SUPPORT ============================== */
  function populateTicketPropertySelect() {
    var sel = document.getElementById('ticketPropertySelect');
    sel.innerHTML = '<option value="">General question</option>' + State.properties.map(function (p) { return '<option value="' + p.id + '">' + N.escapeHtml(p.property_name) + '</option>'; }).join('');
  }

  async function loadSupport() {
    var res = await sb.from('support_tickets').select('*, properties(property_name)').eq('customer_id', State.customerId).order('created_at', { ascending: false });
    State.tickets = res.data || [];

    // A ticket gets a "Response Received" badge when there's an unread
    // support_ticket_reply notification pointing at it — the same
    // notification reply_support_ticket() already creates whenever staff
    // reply, surfaced here directly on the ticket rather than only in the
    // separate Notifications tab.
    var notifRes = await sb.from('notifications').select('id, related_id').eq('user_id', State.userId).eq('type', 'support_ticket_reply').eq('read', false);
    var unreadReplyTicketIds = {};
    (notifRes.data || []).forEach(function (n) { if (n.related_id) unreadReplyTicketIds[n.related_id] = true; });

    var el = document.getElementById('ticketsList');
    if (!State.tickets.length) { el.innerHTML = emptyState('🎫', "You haven't raised any support tickets yet."); return; }
    el.innerHTML = State.tickets.map(function (t) {
      return '<div class="prop-card" data-ticket-id="' + t.id + '"><div class="row1"><b>' + N.escapeHtml(t.subject) + (unreadReplyTicketIds[t.id] ? ' <span class="new-reply-badge">Response Received</span>' : '') + '</b><span class="status-chip status-' + t.status + '">' + N.statusLabel(t.status) + '</span></div>' +
        '<div class="meta">' + (t.properties ? N.escapeHtml(t.properties.property_name) + ' · ' : '') + N.fmtDateTime(t.created_at) + ' · Priority: ' + t.priority + '</div></div>';
    }).join('');
    el.querySelectorAll('[data-ticket-id]').forEach(function (card) {
      card.addEventListener('click', function () { openThread(card.dataset.ticketId); });
    });
  }

  async function onTicketSubmit(e) {
    e.preventDefault();
    var alertEl = document.getElementById('ticketAlert');
    alertEl.className = 'alert';
    var fd = new FormData(e.target);
    try {
      var res = await sb.rpc('create_support_ticket', {
        p_subject: fd.get('subject'),
        p_description: fd.get('description'),
        p_priority: fd.get('priority') || 'medium',
        p_property_id: fd.get('property_id') || null
      });
      if (res.error) throw res.error;
      alertEl.textContent = 'Ticket submitted! We\'ll reply here.';
      alertEl.className = 'alert success show';
      e.target.reset();
      await loadSupport();
      setTimeout(function () { closeModal('ticketModal'); }, 900);
    } catch (err) {
      alertEl.textContent = err.message || 'Could not submit your ticket.';
      alertEl.className = 'alert error show';
    }
  }

  var currentThreadId = null;
  async function openThread(ticketId) {
    currentThreadId = ticketId;
    var t = State.tickets.find(function (x) { return x.id === ticketId; });
    document.getElementById('threadSubject').textContent = t ? t.subject : 'Ticket';
    document.getElementById('threadStatus').innerHTML = t ? ('<span class="status-chip status-' + t.status + '">' + N.statusLabel(t.status) + '</span> <span style="color:var(--ink-500);font-size:.85rem;">' + N.escapeHtml(t.description) + '</span>') : '';
    document.getElementById('threadReplies').innerHTML = '<div class="skel" style="height:40px;"></div>';
    openModal('threadModal');

    // Clear the "Response Received" badge for this ticket now that the owner
    // has opened it, and refresh the bell badge count to match.
    await sb.from('notifications').update({ read: true }).eq('user_id', State.userId).eq('type', 'support_ticket_reply').eq('related_id', ticketId).eq('read', false);
    loadNotifications();
    loadSupport();

    var res = await sb.from('support_ticket_replies').select('*, users:sender_id(name, role)').eq('ticket_id', ticketId).order('created_at', { ascending: true });
    var replies = res.data || [];
    document.getElementById('threadReplies').innerHTML = replies.length ? replies.map(function (r) {
      var mine = r.sender_id === State.userId;
      return '<div class="reply-bubble ' + (mine ? 'me' : '') + '"><div class="who">' + (mine ? 'You' : N.escapeHtml((r.users && r.users.name) || 'Team') + (r.users ? ' · ' + N.escapeHtml(r.users.role) : '')) + ' · ' + N.fmtDateTime(r.created_at) + '</div>' + N.escapeHtml(r.body) + '</div>';
    }).join('') : '<p class="help-note">No replies yet.</p>';
  }

  async function onReplySubmit(e) {
    e.preventDefault();
    var body = document.getElementById('replyBody').value.trim();
    if (!body || !currentThreadId) return;
    try {
      var res = await sb.rpc('reply_support_ticket', { p_ticket_id: currentThreadId, p_body: body });
      if (res.error) throw res.error;
      document.getElementById('replyBody').value = '';
      openThread(currentThreadId);
    } catch (err) { N.toast(err.message || 'Could not send reply.', 'error'); }
  }

  /* ============================== NOTIFICATIONS ============================== */
  async function loadNotifications() {
    var res = await sb.from('notifications').select('*').eq('user_id', State.userId).order('created_at', { ascending: false }).limit(50);
    State.notifications = res.data || [];
    var el = document.getElementById('notifList');
    el.innerHTML = State.notifications.length ? State.notifications.map(notifRow).join('') : emptyState('🔔', 'No notifications yet.');
    updateNotifBadge();
  }
  function updateNotifBadge() {
    var unread = State.notifications.filter(function (n) { return !n.read; }).length;
    var badge = document.getElementById('notifBadge');
    if (unread > 0) { badge.style.display = ''; badge.textContent = unread; } else { badge.style.display = 'none'; }
  }
  async function markAllNotifsRead() {
    await sb.from('notifications').update({ read: true }).eq('user_id', State.userId).eq('read', false);
    await loadNotifications();
    N.toast('All notifications marked as read.', 'success');
  }

  /* ============================== EMERGENCY SOS ============================== */
  var SOS_TYPES = [
    { key: 'break_in', label: 'Break-in / Trespassing', em: '🚨' },
    { key: 'encroachment', label: 'Encroachment', em: '🏗️' },
    { key: 'fire', label: 'Fire / Smoke', em: '🔥' },
    { key: 'flood', label: 'Flood / Water Damage', em: '💧' },
    { key: 'utility', label: 'Utility / Structural', em: '⚡' },
    { key: 'other', label: 'Other Urgent Concern', em: '❓' }
  ];
  var selectedSosType = null;

  function loadSOS() {
    var el = document.getElementById('sosArea');
    if (State.packageTier !== 'prime') {
      el.innerHTML = '<div class="upgrade-banner"><div><h3>Emergency SOS is a Prime feature</h3><p class="mb-0">Upgrade for +₹2,000/month to raise instant, high-priority emergency alerts straight to our Admin team and your Property Manager.</p></div><button class="btn btn-gold" id="sosUpgradeBtn">Upgrade to Prime</button></div>';
      document.getElementById('sosUpgradeBtn').addEventListener('click', upgradeToPrime);
      return;
    }
    if (!State.properties.length) { el.innerHTML = emptyState('🚨', 'Register a property before raising an emergency alert.'); return; }
    el.innerHTML =
      '<div class="card"><div class="field"><label>Which property?</label><select id="sosProperty">' + State.properties.map(function (p) { return '<option value="' + p.id + '">' + N.escapeHtml(p.property_name) + '</option>'; }).join('') + '</select></div>' +
      '<div class="field"><label>Emergency type</label><div class="sos-grid">' + SOS_TYPES.map(function (t) { return '<div class="sos-opt" data-sos="' + t.key + '"><span class="em">' + t.em + '</span>' + t.label + '</div>'; }).join('') + '</div></div>' +
      '<div class="field"><label>Notes</label><textarea id="sosNotes" placeholder="Briefly describe what\'s happening"></textarea></div>' +
      '<button class="btn btn-primary btn-block" id="sosSubmitBtn" style="background:var(--red-600);">🚨 Raise Emergency Alert</button></div>';

    document.querySelectorAll('.sos-opt').forEach(function (opt) {
      opt.addEventListener('click', function () {
        document.querySelectorAll('.sos-opt').forEach(function (o) { o.classList.remove('selected'); });
        opt.classList.add('selected');
        selectedSosType = SOS_TYPES.find(function (t) { return t.key === opt.dataset.sos; });
      });
    });
    document.getElementById('sosSubmitBtn').addEventListener('click', submitSOS);
  }

  async function submitSOS() {
    if (!selectedSosType) { N.toast('Choose an emergency type first.', 'error'); return; }
    var propertyId = document.getElementById('sosProperty').value;
    var notes = document.getElementById('sosNotes').value;
    var btn = document.getElementById('sosSubmitBtn');
    btn.disabled = true; btn.innerHTML = '<span class="loading-spin"></span> Sending alert…';
    try {
      var res = await sb.rpc('create_support_ticket', {
        p_subject: 'EMERGENCY SOS: ' + selectedSosType.label,
        p_description: notes || (selectedSosType.label + ' reported by owner via Emergency SOS.'),
        p_priority: 'high',
        p_property_id: propertyId
      });
      if (res.error) throw res.error;
      N.toast('Emergency alert sent — our Admin team and your Property Manager have been notified.', 'success');
      selectedSosType = null;
      loadSOS();
    } catch (err) {
      N.toast(err.message || 'Could not send the alert. Please call us directly.', 'error');
    } finally {
      btn.disabled = false; btn.innerHTML = '🚨 Raise Emergency Alert';
    }
  }

  async function upgradeToPrime() {
    try {
      var res = await sb.rpc('customer_set_package_tier', { p_tier: 'prime' });
      if (res.error) throw res.error;
      State.packageTier = 'prime';
      N.toast('Upgraded to Prime! Emergency SOS is now active.', 'success');
      loadSOS();
    } catch (err) { N.toast(err.message || 'Could not upgrade right now.', 'error'); }
  }

  /* ============================== PROFILE ============================== */
  function loadProfile() {
    var el = document.getElementById('profileArea');
    el.innerHTML =
      '<div class="card"><h3>Account Details</h3>' +
        '<div class="form-grid-2"><div class="field"><label>Full name</label><input id="pf-name" value="' + N.escapeHtml(State.profile.name || '') + '"></div>' +
        '<div class="field"><label>Phone</label><input id="pf-phone" value="' + N.escapeHtml(State.profile.phone_number || '') + '"></div></div>' +
        '<div class="field"><label>Email</label><input value="' + N.escapeHtml(State.session.user.email) + '" disabled></div>' +
        '<button class="btn btn-primary btn-sm" id="pf-save">Save Changes</button></div>' +

      '<div class="card"><h3>Membership Plan</h3>' +
        '<p>Current plan: <b>' + (State.packageTier === 'prime' ? 'Prime — 2 inspection visits/month' : 'Regular — 1 inspection visit/month') + '</b></p>' +
        '<p class="help-note">' + N.pricingRateDescription(State.properties.length, State.packageTier) + '</p>' +
        '<p style="margin:10px 0;font-size:1.05rem;"><b>Estimated monthly cost: ' + N.fmtMoney(N.calcMonthlyAmount(State.properties.length, State.packageTier)) + '</b></p>' +
        '<p class="help-note">Your actual monthly invoice is confirmed on the Documents &amp; Invoices tab.</p>' +
        (State.packageTier === 'prime' ? '<p class="help-note">Emergency SOS is active on your account.</p>' : '<button class="btn btn-gold btn-sm" id="pf-upgrade">Upgrade to Prime — 2 visits/month</button>') +
      '</div>' +

      '<div class="card"><h3>Change Password</h3>' +
        '<div class="alert" id="pf-pass-alert"></div>' +
        '<div class="form-grid-2"><div class="field"><label>New password</label><input id="pf-newpass" type="password" minlength="6"></div>' +
        '<div class="field"><label>Confirm new password</label><input id="pf-newpass2" type="password" minlength="6"></div></div>' +
        '<button class="btn btn-outline btn-sm" id="pf-changepass">Update Password</button></div>';

    document.getElementById('pf-save').addEventListener('click', async function () {
      var name = document.getElementById('pf-name').value.trim();
      var phone = document.getElementById('pf-phone').value.trim();
      var res = await sb.from('users').update({ name: name, phone_number: phone }).eq('id', State.userId);
      if (res.error) { N.toast(res.error.message, 'error'); return; }
      State.profile.name = name; State.profile.phone_number = phone;
      document.getElementById('avatarChip').textContent = N.initials(name);
      N.toast('Profile updated.', 'success');
    });

    var upgradeBtn = document.getElementById('pf-upgrade');
    if (upgradeBtn) upgradeBtn.addEventListener('click', async function () { await upgradeToPrime(); loadProfile(); });

    document.getElementById('pf-changepass').addEventListener('click', async function () {
      var alertEl = document.getElementById('pf-pass-alert');
      var p1 = document.getElementById('pf-newpass').value, p2 = document.getElementById('pf-newpass2').value;
      if (p1.length < 6 || p1 !== p2) { alertEl.textContent = 'Passwords must match and be at least 6 characters.'; alertEl.className = 'alert error show'; return; }
      var res = await sb.auth.updateUser({ password: p1 });
      if (res.error) { alertEl.textContent = res.error.message; alertEl.className = 'alert error show'; return; }
      alertEl.textContent = 'Password updated successfully.'; alertEl.className = 'alert success show';
      document.getElementById('pf-newpass').value = ''; document.getElementById('pf-newpass2').value = '';
    });
  }

  /* ============================== MODALS ============================== */
  function bindModals() {
    document.querySelectorAll('[data-close-modal]').forEach(function (btn) {
      btn.addEventListener('click', function () { closeModal(btn.dataset.closeModal); });
    });
    document.querySelectorAll('[data-maximize-modal]').forEach(function (btn) {
      btn.addEventListener('click', function () {
        var backdrop = document.getElementById(btn.dataset.maximizeModal);
        N.toggleModalMaximize(backdrop.querySelector('.lightbox-modal') || backdrop);
      });
    });
    document.querySelectorAll('.modal-backdrop').forEach(function (backdrop) {
      backdrop.addEventListener('click', function (e) { if (e.target === backdrop) closeModal(backdrop.id); });
    });
  }
  function openModal(id) { document.getElementById(id).classList.add('open'); }
  function closeModal(id) {
    var el = document.getElementById(id);
    el.classList.remove('open');
    N.resetModalMaximize(el.querySelector('.lightbox-modal') || el);
  }

  /* ============================== REALTIME ============================== */
  function setupRealtime() {
    State.channel = sb.channel('owner-portal-' + State.customerId)
      .on('postgres_changes', { event: '*', schema: 'public', table: 'site_visits', filter: 'customer_id=eq.' + State.customerId }, onLiveChange)
      .on('postgres_changes', { event: '*', schema: 'public', table: 'properties', filter: 'customer_id=eq.' + State.customerId }, onLiveChange)
      .on('postgres_changes', { event: '*', schema: 'public', table: 'invoices', filter: 'customer_id=eq.' + State.customerId }, onLiveChange)
      .subscribe();
  }
  var liveRefreshT;
  function onLiveChange() {
    clearTimeout(liveRefreshT);
    liveRefreshT = setTimeout(function () { refreshCurrentView(false); }, 700);
  }

  // Shared by the Realtime push handler above (silent=false, shows a toast —
  // the user just saw something change) and the 30s auto-refresh timer set up
  // in boot() (silent=true, routine polling shouldn't toast every 30 seconds).
  // Always reloads properties/home regardless of view, same as before, plus
  // whichever detail/list view is currently open so nothing on this dashboard
  // depends on a manual reload to catch up with the backend.
  function refreshCurrentView(silent) {
    loadProperties();
    loadHome();
    if (State.currentView === 'propdetail' && State.selectedPropertyId) openPropertyDetail(State.selectedPropertyId);
    if (State.currentView === 'visits') loadVisits();
    if (State.currentView === 'visitdetail' && State.selectedVisitId) openVisitDetail(State.selectedVisitId);
    if (State.currentView === 'reports') loadReports();
    if (State.currentView === 'documents') loadDocumentsInvoices();
    if (State.currentView === 'location') loadLocation();
    if (State.currentView === 'support') loadSupport();
    if (State.currentView === 'notifications') loadNotifications();
    if (State.currentView === 'sos') loadSOS();
    if (State.currentView === 'profile') loadProfile();
    if (!silent) N.toast('Your dashboard just updated.', 'success');
  }

  boot();
})();
