/* NRI Property Guard — Admin web dashboard logic (v1)
   Covers: overview stats, Properties (with assign-to-manager), Property Managers
   and Property Executives (list + create via the same create-staff-user Edge
   Function the mobile app uses), Customers, Reports/Analytics (with CSV export),
   and an Audit Log viewer. Roles & Permissions / Settings are a documented v2
   follow-up — see the "Roles & Settings" view in admin-dashboard.html. */
(function () {
  var sb = window.sbClient;
  var N = window.NRIPG;

  var State = {
    session: null, userId: null, profile: null,
    properties: [], managers: [], executives: [], customers: [],
    analytics: null, auditLogs: [], auditOffset: 0, auditSearch: '',
    propFilter: 'all', currentView: 'home', selectedCustomerId: null
  };

  var PROP_FILTERS = [
    { key: 'all', label: 'All' },
    { key: 'new', label: 'New Requests' },
    { key: 'unassigned', label: 'Pending Assignment' },
    { key: 'assigned', label: 'Assigned Properties' },
    { key: 'completed', label: 'Completed' }
  ];
  var ASSIGNED_STATUSES = ['assigned_to_manager', 'executive_assigned', 'visit_scheduled', 'inspection_started', 'inspection_completed', 'pending_manager_approval', 'revisit_requested'];

  /* ============================== 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;

    // Admin dashboard is for admin/super_admin accounts only — anyone else
    // (including a customer who bookmarked this URL) is bounced back through
    // index.html, which routes each role to its own correct screen.
    var roleRes = await sb.from('users').select('role').eq('id', State.userId).maybeSingle();
    var role = (roleRes.data && roleRes.data.role) || 'customer';
    if (role !== 'admin' && role !== 'super_admin') { location.href = 'index.html'; return; }

    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();
    renderPropFilterChips();

    await Promise.all([loadAnalytics(), loadProperties()]);
    // Silent 30s backstop, same as every other role's dashboard, so Admin's
    // stats/lists/financial totals stay current without a manual reload.
    N.startAutoRefresh(function () { refreshCurrentView(); }, 30000);
  }

  async function refreshCurrentView() {
    await loadAnalytics();
    if (State.currentView === 'properties') loadProperties();
    if (State.currentView === 'managers') loadManagers();
    if (State.currentView === 'executives') loadExecutives();
    if (State.currentView === 'customers') loadCustomers();
    if (State.currentView === 'customerdetail' && State.selectedCustomerId) openCustomerDetail(State.selectedCustomerId);
    if (State.currentView === 'reports') renderAnalyticsViews();
    if (State.currentView === 'auditlogs') loadAuditLogs(true);
  }

  /* ============================== NAV ============================== */
  function bindNav() {
    document.querySelectorAll('.side-nav a[data-view]').forEach(function (a) {
      a.addEventListener('click', function () { switchView(a.dataset.view); });
    });
    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('backToCustomers').addEventListener('click', function () { switchView('customers'); });
  }

  var TITLES = { home: 'Dashboard', properties: 'Properties', managers: 'Property Managers', executives: 'Property Executives', customers: 'Customers', customerdetail: 'Customer Statement', reports: 'Reports & Analytics', auditlogs: 'Audit Logs', more: 'Roles & Settings' };

  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;

    if (name === 'managers' && !State.managers.length) loadManagers();
    if (name === 'executives' && !State.executives.length) loadExecutives();
    // Always reload (not just "if empty") since this view now shows financial
    // totals (paid/due) that should reflect the latest invoice data each time
    // the Admin opens it, not a stale snapshot from the first visit.
    if (name === 'customers') loadCustomers();
    if (name === 'reports') renderAnalyticsViews();
    if (name === 'auditlogs' && !State.auditLogs.length) loadAuditLogs(true);
  }

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

  /* ============================== ANALYTICS / OVERVIEW ============================== */
  async function loadAnalytics() {
    var res = await sb.rpc('admin_analytics_summary');
    if (res.error) { N.toast(res.error.message || 'Could not load analytics.', 'error'); return; }
    State.analytics = res.data || {};
    renderOverviewStats();
  }

  function renderOverviewStats() {
    var a = State.analytics || {};
    var byWf = a.properties_by_workflow_status || {};
    var totalProps = Object.keys(byWf).reduce(function (s, k) { return s + byWf[k]; }, 0);
    var byTix = a.support_tickets_by_status || {};
    var openTickets = (byTix.open || 0) + (byTix.in_progress || 0);
    var pendingApproval = (a.site_visits_by_status || {}).pending_approval || 0;

    document.getElementById('homeStats').innerHTML = [
      statCard('Total Properties', totalProps),
      statCard('Open Support Tickets', openTickets),
      statCard('Pending Manager Approval', pendingApproval),
      statCard('Executives Tracked', (a.executive_performance || []).length)
    ].join('');

    document.getElementById('workflowStatusTable').innerHTML = kvTable(byWf, N.statusLabel);
    document.getElementById('visitStatusTable').innerHTML = kvTable(a.site_visits_by_status || {}, N.statusLabel);
  }

  function kvTable(obj, labelFn) {
    var keys = Object.keys(obj || {});
    if (!keys.length) return emptyState('📊', 'No data yet.');
    return '<table class="kv-table"><tbody>' + keys.map(function (k) {
      return '<tr><td>' + N.escapeHtml(labelFn ? labelFn(k) : k) + '</td><td>' + obj[k] + '</td></tr>';
    }).join('') + '</tbody></table>';
  }

  function renderAnalyticsViews() {
    var a = State.analytics || {};
    var inv = a.invoices || {};
    document.getElementById('invoiceSummary').innerHTML =
      '<div class="dash-grid" style="margin-bottom:0;">' +
      statCard('Paid', N.fmtMoney(inv.paid_amount) + ' (' + (inv.paid_count || 0) + ')') +
      statCard('Due', N.fmtMoney(inv.due_amount) + ' (' + (inv.due_count || 0) + ')') +
      statCard('Total Invoices', inv.total_count || 0) +
      '</div>';

    var execPerf = a.executive_performance || [];
    document.getElementById('execPerfTable').innerHTML = execPerf.length
      ? '<table class="table-simple"><thead><tr><th>Executive</th><th>Completed Visits</th></tr></thead><tbody>' +
        execPerf.map(function (e) { return '<tr><td>' + N.escapeHtml(e.name) + '</td><td>' + e.completed_visits + '</td></tr>'; }).join('') + '</tbody></table>'
      : emptyState('🧑‍🔧', 'No Property Executives yet.');

    var mgrLoad = a.manager_workload || [];
    document.getElementById('mgrWorkloadTable').innerHTML = mgrLoad.length
      ? '<table class="table-simple"><thead><tr><th>Manager</th><th>Region</th><th>Executives</th><th>Properties</th></tr></thead><tbody>' +
        mgrLoad.map(function (m) { return '<tr><td>' + N.escapeHtml(m.name) + '</td><td>' + N.escapeHtml(m.region || '—') + '</td><td>' + m.executive_count + '</td><td>' + m.property_count + '</td></tr>'; }).join('') + '</tbody></table>'
      : emptyState('🧑‍💼', 'No Property Managers yet.');
  }

  /* ============================== PROPERTIES ============================== */
  function renderPropFilterChips() {
    var el = document.getElementById('propFilterChips');
    el.innerHTML = PROP_FILTERS.map(function (f) {
      return '<button type="button" class="filter-chip' + (f.key === State.propFilter ? ' active' : '') + '" data-filter="' + f.key + '">' + f.label + '</button>';
    }).join('');
    el.querySelectorAll('.filter-chip').forEach(function (chip) {
      chip.addEventListener('click', function () { State.propFilter = chip.dataset.filter; renderPropFilterChips(); renderPropertiesList(); });
    });
  }

  async function loadProperties() {
    var res = await sb.from('properties').select('*, owner:customer_id(user_id), manager:manager_id(id, user_id)').order('created_at', { ascending: false });
    if (res.error) { N.toast(res.error.message || 'Could not load properties.', 'error'); State.properties = []; }
    else State.properties = res.data || [];

    // Resolve display names in a second pass (owner/manager come back as nested
    // customer/property_manager rows above; we still need their users.name).
    var userIds = [];
    State.properties.forEach(function (p) {
      if (p.owner && p.owner.user_id) userIds.push(p.owner.user_id);
      if (p.manager && p.manager.user_id) userIds.push(p.manager.user_id);
    });
    var names = {};
    if (userIds.length) {
      var uRes = await sb.from('users').select('id, name').in('id', userIds);
      (uRes.data || []).forEach(function (u) { names[u.id] = u.name; });
    }
    State.properties.forEach(function (p) {
      p._ownerName = p.owner && p.owner.user_id ? (names[p.owner.user_id] || '—') : '—';
      p._managerName = p.manager && p.manager.user_id ? (names[p.manager.user_id] || '—') : null;
    });

    // Most recent/next scheduled visit per property, so the reassignment
    // decision (and the properties list generally) shows schedule date/time
    // alongside owner and property name, not just the workflow status.
    var propIds = State.properties.map(function (p) { return p.id; });
    var nextVisitByProperty = {};
    if (propIds.length) {
      var svRes = await sb.from('site_visits').select('property_id, scheduled_date, status').in('property_id', propIds).order('scheduled_date', { ascending: false });
      (svRes.data || []).forEach(function (v) { if (!nextVisitByProperty[v.property_id]) nextVisitByProperty[v.property_id] = v; });
    }
    State.properties.forEach(function (p) { p._nextVisit = nextVisitByProperty[p.id] || null; });

    renderPropertiesList();
  }

  function filteredProperties() {
    var list = State.properties;
    if (State.propFilter === 'new') return list.filter(function (p) { return ['submitted', 'admin_review'].indexOf(p.workflow_status) !== -1; });
    if (State.propFilter === 'unassigned') return list.filter(function (p) { return !p.manager_id; });
    if (State.propFilter === 'assigned') return list.filter(function (p) { return ASSIGNED_STATUSES.indexOf(p.workflow_status) !== -1; });
    if (State.propFilter === 'completed') return list.filter(function (p) { return ['approved', 'rejected'].indexOf(p.workflow_status) !== -1; });
    return list;
  }

  function renderPropertiesList() {
    var list = filteredProperties();
    var el = document.getElementById('propertiesList');
    if (!list.length) { el.innerHTML = emptyState('🏘️', 'No properties in this filter.'); return; }
    el.innerHTML = list.map(function (p) {
      var nv = p._nextVisit;
      var scheduleChip = nv ? '<span class="mini-chip">Visit: ' + N.fmtDateTime(nv.scheduled_date) + ' (' + N.escapeHtml(N.statusLabel(nv.status)) + ')</span>' : '';
      var btnLabel = p.manager_id ? 'Reassign' : 'Assign to Manager';
      return '<div class="prop-card" style="cursor:default;">' +
        '<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">Owner: ' + N.escapeHtml(p._ownerName) + ' · ' + N.escapeHtml(p.city) + ', ' + N.escapeHtml(p.state) + ' · ' + N.propertyTypeLabel(p.property_type) + '</div>' +
        '<div class="chip-row">' +
          '<span class="mini-chip">Manager: ' + (p._managerName ? N.escapeHtml(p._managerName) : 'Unassigned') + '</span>' +
          '<span class="mini-chip">Priority: ' + N.escapeHtml(p.priority || 'medium') + '</span>' +
          '<span class="mini-chip">Added ' + N.fmtDate(p.created_at) + '</span>' +
          scheduleChip +
          '<button class="btn btn-primary btn-sm" data-assign="' + p.id + '" style="margin-left:auto;">' + btnLabel + '</button>' +
        '</div></div>';
    }).join('');
    el.querySelectorAll('[data-assign]').forEach(function (btn) {
      btn.addEventListener('click', function () { openAssignModal(btn.dataset.assign); });
    });
  }

  async function openAssignModal(propertyId) {
    var p = State.properties.filter(function (x) { return x.id === propertyId; })[0];
    document.querySelector('#assignForm [name=property_id]').value = propertyId;
    var sel = document.getElementById('assignManagerSelect');
    if (!State.managers.length) await loadManagers();
    sel.innerHTML = State.managers.map(function (m) { return '<option value="' + m.id + '">' + N.escapeHtml(m._name) + (m.region ? ' — ' + N.escapeHtml(m.region) : '') + '</option>'; }).join('');

    var isReassign = !!(p && p.manager_id);
    document.getElementById('assignModalTitle').textContent = isReassign ? 'Reassign Property to a Different Manager' : 'Assign Property to a Manager';
    var ctxEl = document.getElementById('assignContextInfo');
    if (p) {
      var scheduleText = p._nextVisit ? N.fmtDateTime(p._nextVisit.scheduled_date) + ' (' + N.statusLabel(p._nextVisit.status) + ')' : 'Not yet scheduled';
      ctxEl.innerHTML =
        '<span class="mini-chip">Owner: ' + N.escapeHtml(p._ownerName) + '</span>' +
        '<span class="mini-chip">Property: ' + N.escapeHtml(p.property_name) + '</span>' +
        '<span class="mini-chip">Visit schedule: ' + N.escapeHtml(scheduleText) + '</span>' +
        (isReassign ? '<span class="mini-chip">Currently: ' + N.escapeHtml(p._managerName || '—') + '</span>' : '');
      if (isReassign) sel.value = p.manager_id;
    } else {
      ctxEl.innerHTML = '';
    }
    document.querySelector('#assignForm button[type=submit]').textContent = isReassign ? 'Reassign' : 'Assign';
    document.getElementById('assignAlert').className = 'alert';
    openModal('assignModal');
  }

  /* ============================== PROPERTY MANAGERS ============================== */
  async function loadManagers() {
    var res = await sb.from('property_managers').select('*, users:user_id(name, email, phone_number, status)').order('created_at', { ascending: false });
    if (res.error) { N.toast(res.error.message || 'Could not load Property Managers.', 'error'); State.managers = []; }
    else State.managers = (res.data || []).map(function (m) { m._name = (m.users && m.users.name) || '—'; return m; });
    renderManagersList();
  }

  function renderManagersList() {
    var el = document.getElementById('managersList');
    if (!State.managers.length) { el.innerHTML = emptyState('🧑‍💼', 'No Property Managers yet.'); return; }
    el.innerHTML = State.managers.map(function (m) {
      var propCount = State.properties.filter(function (p) { return p.manager_id === m.id; }).length;
      return '<div class="prop-card" style="cursor:default;"><div class="row1"><b>' + N.escapeHtml(m._name) + '</b><span class="status-chip status-' + ((m.users && m.users.status) || 'active') + '">' + N.statusLabel((m.users && m.users.status) || 'active') + '</span></div>' +
        '<div class="meta">' + N.escapeHtml((m.users && m.users.email) || '—') + (m.users && m.users.phone_number ? ' · ' + N.escapeHtml(m.users.phone_number) : '') + '</div>' +
        '<div class="chip-row"><span class="mini-chip">Region: ' + N.escapeHtml(m.region || '—') + '</span><span class="mini-chip">' + propCount + ' properties assigned</span></div></div>';
    }).join('');
  }

  /* ============================== PROPERTY EXECUTIVES ============================== */
  async function loadExecutives() {
    var res = await sb.from('property_executives').select('*, users:user_id(name, email, phone_number, status), manager:manager_id(id, user_id)').order('created_at', { ascending: false });
    if (res.error) { N.toast(res.error.message || 'Could not load Property Executives.', 'error'); State.executives = []; }
    else State.executives = res.data || [];

    if (!State.managers.length) await loadManagers();
    var mgrNameById = {};
    State.managers.forEach(function (m) { mgrNameById[m.id] = m._name; });
    State.executives.forEach(function (e) { e._name = (e.users && e.users.name) || '—'; e._managerName = e.manager_id ? (mgrNameById[e.manager_id] || '—') : '—'; });

    renderExecutivesList();
  }

  function renderExecutivesList() {
    var el = document.getElementById('executivesList');
    if (!State.executives.length) { el.innerHTML = emptyState('🧑‍🔧', 'No Property Executives yet.'); return; }
    el.innerHTML = State.executives.map(function (e) {
      return '<div class="prop-card" style="cursor:default;"><div class="row1"><b>' + N.escapeHtml(e._name) + '</b><span class="status-chip status-' + ((e.users && e.users.status) || 'active') + '">' + N.statusLabel((e.users && e.users.status) || 'active') + '</span></div>' +
        '<div class="meta">' + N.escapeHtml((e.users && e.users.email) || '—') + (e.users && e.users.phone_number ? ' · ' + N.escapeHtml(e.users.phone_number) : '') + '</div>' +
        '<div class="chip-row"><span class="mini-chip">Region: ' + N.escapeHtml(e.region || '—') + '</span><span class="mini-chip">Reports to: ' + N.escapeHtml(e._managerName) + '</span></div></div>';
    }).join('');
  }

  /* ============================== CUSTOMERS ============================== */
  async function loadCustomers() {
    var res = await sb.from('customers').select('*, users:user_id(name, email, phone_number)').order('created_at', { ascending: false });
    if (res.error) { N.toast(res.error.message || 'Could not load customers.', 'error'); State.customers = []; }
    else State.customers = res.data || [];

    // Bulk-fetch every invoice once and aggregate per customer in JS, rather than
    // one invoices query per customer row — same one-query-then-group-in-JS
    // pattern used for the next-scheduled-visit lookup in loadProperties() above.
    // Admin's RLS policy on invoices (is_admin_or_above()) already permits
    // reading every customer's invoices, not just the caller's own.
    var invRes = await sb.from('invoices').select('customer_id, amount_inr, status');
    var financeByCustomer = {};
    (invRes.data || []).forEach(function (inv) {
      var f = financeByCustomer[inv.customer_id] || (financeByCustomer[inv.customer_id] = { paid: 0, due: 0, invoiceCount: 0 });
      f.invoiceCount++;
      var amt = Number(inv.amount_inr) || 0;
      if (inv.status === 'paid') f.paid += amt;
      else f.due += amt; // 'due' or 'overdue' — both are money still owed
    });
    State.customers.forEach(function (c) { c._finance = financeByCustomer[c.id] || { paid: 0, due: 0, invoiceCount: 0 }; });

    renderCustomersList();
  }

  function renderCustomersList() {
    var el = document.getElementById('customersList');
    if (!State.customers.length) { el.innerHTML = emptyState('👥', 'No customers yet.'); return; }
    el.innerHTML = '<table class="table-simple"><thead><tr><th>Name</th><th>Email</th><th>Phone</th><th>Plan</th><th>Properties</th><th>Paid</th><th>Due</th><th></th></tr></thead><tbody>' +
      State.customers.map(function (c) {
        var propCount = State.properties.filter(function (p) { return p.customer_id === c.id; }).length;
        var tier = c.package_tier || 'regular';
        var f = c._finance || { paid: 0, due: 0 };
        return '<tr>' +
          '<td>' + N.escapeHtml((c.users && c.users.name) || '—') + '</td>' +
          '<td>' + N.escapeHtml((c.users && c.users.email) || '—') + '</td>' +
          '<td>' + N.escapeHtml((c.users && c.users.phone_number) || '—') + '</td>' +
          '<td><span class="tier-badge tier-' + tier + '">' + (tier === 'prime' ? 'Prime' : 'Regular') + '</span></td>' +
          '<td>' + propCount + '</td>' +
          '<td>' + N.fmtMoney(f.paid) + '</td>' +
          '<td>' + (f.due > 0 ? '<b style="color:var(--red-600);">' + N.fmtMoney(f.due) + '</b>' : N.fmtMoney(0)) + '</td>' +
          '<td><button class="btn btn-outline btn-sm" data-customer-detail="' + c.id + '">View Statement</button></td>' +
        '</tr>';
      }).join('') + '</tbody></table>';
    el.querySelectorAll('[data-customer-detail]').forEach(function (btn) {
      btn.addEventListener('click', function () { openCustomerDetail(btn.dataset.customerDetail); });
    });
  }

  async function openCustomerDetail(customerId) {
    State.selectedCustomerId = customerId;
    switchView('customerdetail');
    var body = document.getElementById('customerDetailBody');
    body.innerHTML = '<div class="skel" style="height:220px;"></div>';

    var c = State.customers.find(function (x) { return x.id === customerId; });
    if (!c) { body.innerHTML = emptyState('👤', 'Customer not found.'); return; }

    var custProperties = State.properties.filter(function (p) { return p.customer_id === customerId; });

    // Latest-visit Executive per property: reuse each property's _nextVisit (the
    // most-recently-scheduled site visit, already computed in loadProperties())
    // to look up who was assigned to it. Named "Latest Visit Executive" in the
    // UI rather than "Assigned Executive" because — unlike a Manager, which is a
    // standing assignment on the property itself — an Executive is only ever
    // assigned per-visit in this data model, so this is the best available
    // proxy for "who is currently handling this property," not a fixed field.
    var visitIds = custProperties.filter(function (p) { return p._nextVisit; }).map(function (p) { return p._nextVisit.id; });
    var execByVisit = {};
    if (visitIds.length) {
      var vaRes = await sb.from('visit_assignments').select('site_visit_id, executive:executive_id(id, user_id)').in('site_visit_id', visitIds).neq('status', 'cancelled');
      var execUserIds = (vaRes.data || []).filter(function (a) { return a.executive && a.executive.user_id; }).map(function (a) { return a.executive.user_id; });
      var names = {};
      if (execUserIds.length) {
        var uRes = await sb.from('users').select('id, name').in('id', execUserIds);
        (uRes.data || []).forEach(function (u) { names[u.id] = u.name; });
      }
      (vaRes.data || []).forEach(function (a) { execByVisit[a.site_visit_id] = (a.executive && a.executive.user_id) ? (names[a.executive.user_id] || '—') : '—'; });
    }

    var invRes = await sb.from('invoices').select('*').eq('customer_id', customerId).order('billing_month', { ascending: false });
    var invoices = invRes.data || [];
    var totalInvoiced = 0, totalPaid = 0, totalDue = 0;
    invoices.forEach(function (inv) {
      var amt = Number(inv.amount_inr) || 0;
      totalInvoiced += amt;
      if (inv.status === 'paid') totalPaid += amt; else totalDue += amt;
    });

    var tier = c.package_tier || 'regular';
    body.innerHTML =
      '<div class="card">' +
        '<div class="row1" style="display:flex;justify-content:space-between;align-items:center;">' +
          '<h2 style="margin:0;">' + N.escapeHtml((c.users && c.users.name) || 'Customer') + '</h2>' +
          '<span class="tier-badge tier-' + tier + '">' + (tier === 'prime' ? 'Prime' : 'Regular') + '</span>' +
        '</div>' +
        '<div class="chip-row">' +
          '<span class="mini-chip">' + N.escapeHtml((c.users && c.users.email) || '—') + '</span>' +
          '<span class="mini-chip">' + N.escapeHtml((c.users && c.users.phone_number) || '—') + '</span>' +
          '<span class="mini-chip">Customer since ' + N.fmtDate(c.created_at) + '</span>' +
          '<span class="mini-chip">' + custProperties.length + ' ' + (custProperties.length === 1 ? 'property' : 'properties') + '</span>' +
        '</div>' +
      '</div>' +

      '<div class="card"><h3>Properties, Manager &amp; Executive</h3>' +
        (custProperties.length ? '<table class="table-simple"><thead><tr><th>Property</th><th>Status</th><th>Manager</th><th>Latest Visit Executive</th></tr></thead><tbody>' +
          custProperties.map(function (p) {
            var execName = (p._nextVisit && execByVisit[p._nextVisit.id]) ? execByVisit[p._nextVisit.id] : '—';
            return '<tr><td>' + N.escapeHtml(p.property_name) + '<div style="color:var(--ink-500);font-size:.78rem;">' + N.escapeHtml(p.city) + ', ' + N.escapeHtml(p.state) + '</div></td>' +
              '<td><span class="status-chip status-' + p.workflow_status + '">' + N.statusLabel(p.workflow_status) + '</span></td>' +
              '<td>' + (p._managerName ? N.escapeHtml(p._managerName) : 'Unassigned') + '</td>' +
              '<td>' + N.escapeHtml(execName) + '</td></tr>';
          }).join('') + '</tbody></table>' :
          emptyState('🏘️', 'This customer has not registered any properties yet.')) +
      '</div>' +

      '<div class="card"><h3>Financial Statement</h3>' +
        '<div class="fin-summary-grid">' +
          statCard('Total Invoiced', N.fmtMoney(totalInvoiced)) +
          '<div class="stat-card"><div class="lbl">Total Paid</div><div class="val paid">' + N.fmtMoney(totalPaid) + '</div></div>' +
          '<div class="stat-card"><div class="lbl">Total Due</div><div class="val due">' + N.fmtMoney(totalDue) + '</div></div>' +
          statCard('Invoices', invoices.length) +
        '</div>' +
        (invoices.length ? '<table class="table-simple"><thead><tr><th>Billing Month</th><th>Description</th><th>Amount</th><th>Status</th><th>Due Date</th></tr></thead><tbody>' +
          invoices.map(function (inv) {
            return '<tr><td>' + N.fmtDate(inv.billing_month) + '</td><td>' + N.escapeHtml(inv.rate_description || inv.title || '—') + '</td><td>' + N.fmtMoney(inv.amount_inr) + '</td><td><span class="status-chip status-' + inv.status + '">' + N.statusLabel(inv.status) + '</span></td><td>' + N.fmtDate(inv.due_date) + '</td></tr>';
          }).join('') + '</tbody></table>' :
          emptyState('🧾', 'No invoices generated yet for this customer.')) +
      '</div>';
  }

  /* ============================== AUDIT LOGS ============================== */
  var AUDIT_PAGE_SIZE = 50;
  async function loadAuditLogs(reset) {
    if (reset) { State.auditOffset = 0; State.auditLogs = []; }
    var res = await sb.from('activity_logs').select('*, users:user_id(name)').order('created_at', { ascending: false }).range(State.auditOffset, State.auditOffset + AUDIT_PAGE_SIZE - 1);
    if (res.error) { N.toast(res.error.message || 'Could not load audit logs.', 'error'); return; }
    var rows = res.data || [];
    State.auditLogs = State.auditLogs.concat(rows);
    State.auditOffset += rows.length;
    renderAuditLogs();
  }

  function renderAuditLogs() {
    var el = document.getElementById('auditLogsList');
    var q = State.auditSearch.toLowerCase();
    var list = !q ? State.auditLogs : State.auditLogs.filter(function (l) {
      return (l.action || '').toLowerCase().indexOf(q) !== -1 ||
        (l.entity_type || '').toLowerCase().indexOf(q) !== -1 ||
        ((l.users && l.users.name) || '').toLowerCase().indexOf(q) !== -1;
    });
    if (!list.length) { el.innerHTML = emptyState('📜', 'No matching audit log entries.'); return; }
    el.innerHTML = '<table class="table-simple"><thead><tr><th>When</th><th>Actor</th><th>Action</th><th>Entity</th></tr></thead><tbody>' +
      list.map(function (l) {
        return '<tr><td>' + N.fmtDateTime(l.created_at) + '</td><td>' + N.escapeHtml((l.users && l.users.name) || '—') + '</td><td>' + N.escapeHtml(l.action) + '</td><td>' + N.escapeHtml(l.entity_type || '—') + '</td></tr>';
      }).join('') + '</tbody></table>';
  }

  /* ============================== FORMS ============================== */
  function bindForms() {
    document.getElementById('assignForm').addEventListener('submit', onAssignSubmit);
    document.getElementById('openAddManagerBtn').addEventListener('click', function () {
      document.getElementById('addManagerForm').reset();
      document.getElementById('managerPassReveal').style.display = 'none';
      document.getElementById('addManagerAlert').className = 'alert';
      openModal('addManagerModal');
    });
    document.getElementById('addManagerForm').addEventListener('submit', onAddManagerSubmit);

    document.getElementById('openAddExecutiveBtn').addEventListener('click', async function () {
      document.getElementById('addExecutiveForm').reset();
      document.getElementById('executivePassReveal').style.display = 'none';
      document.getElementById('addExecutiveAlert').className = 'alert';
      if (!State.managers.length) await loadManagers();
      document.getElementById('execManagerSelect').innerHTML = State.managers.map(function (m) { return '<option value="' + m.id + '">' + N.escapeHtml(m._name) + (m.region ? ' — ' + N.escapeHtml(m.region) : '') + '</option>'; }).join('');
      openModal('addExecutiveModal');
    });
    document.getElementById('addExecutiveForm').addEventListener('submit', onAddExecutiveSubmit);

    document.getElementById('auditSearch').addEventListener('input', function (e) { State.auditSearch = e.target.value; renderAuditLogs(); });
    document.getElementById('loadMoreAuditBtn').addEventListener('click', function () { loadAuditLogs(false); });

    document.getElementById('refreshAnalyticsBtn').addEventListener('click', async function () { await loadAnalytics(); renderAnalyticsViews(); N.toast('Analytics refreshed.', 'success'); });
    document.getElementById('exportExecCsv').addEventListener('click', function () { N.downloadCsv('executive_performance.csv', (State.analytics && State.analytics.executive_performance) || []); });
    document.getElementById('exportMgrCsv').addEventListener('click', function () { N.downloadCsv('manager_workload.csv', (State.analytics && State.analytics.manager_workload) || []); });
  }

  async function onAssignSubmit(e) {
    e.preventDefault();
    var alertEl = document.getElementById('assignAlert');
    alertEl.className = 'alert';
    var fd = new FormData(e.target);
    try {
      var res = await sb.rpc('admin_assign_property_to_manager', {
        p_property_id: fd.get('property_id'),
        p_manager_id: fd.get('manager_id'),
        p_instructions: fd.get('instructions') || '',
        p_priority: fd.get('priority') || 'medium',
        p_visit_deadline: fd.get('visit_deadline') || null
      });
      if (res.error) throw res.error;
      alertEl.textContent = 'Property assigned.'; alertEl.className = 'alert success show';
      await loadProperties();
      setTimeout(function () { closeModal('assignModal'); }, 800);
    } catch (err) {
      alertEl.textContent = err.message || 'Could not assign this property.'; alertEl.className = 'alert error show';
    }
  }

  async function onAddManagerSubmit(e) {
    e.preventDefault();
    var alertEl = document.getElementById('addManagerAlert');
    alertEl.className = 'alert';
    var fd = new FormData(e.target);
    var btn = e.target.querySelector('button[type=submit]');
    btn.disabled = true; btn.innerHTML = '<span class="loading-spin"></span> Creating…';
    try {
      var res = await sb.functions.invoke('create-staff-user', {
        body: { email: fd.get('email'), name: fd.get('name'), phone: fd.get('phone') || '', region: fd.get('region'), targetRole: 'property_manager' }
      });
      if (res.error) throw res.error;
      if (res.data && res.data.error) throw new Error(res.data.error);
      var reveal = document.getElementById('managerPassReveal');
      reveal.style.display = 'block';
      reveal.innerHTML = 'Account created for <b>' + N.escapeHtml(res.data.email) + '</b>. Temporary password (share securely, shown once): <br><code>' + N.escapeHtml(res.data.temporaryPassword) + '</code>';
      e.target.reset();
      await loadManagers();
    } catch (err) {
      alertEl.textContent = err.message || 'Could not create this account.'; alertEl.className = 'alert error show';
    } finally {
      btn.disabled = false; btn.textContent = 'Create Account';
    }
  }

  async function onAddExecutiveSubmit(e) {
    e.preventDefault();
    var alertEl = document.getElementById('addExecutiveAlert');
    alertEl.className = 'alert';
    var fd = new FormData(e.target);
    var btn = e.target.querySelector('button[type=submit]');
    btn.disabled = true; btn.innerHTML = '<span class="loading-spin"></span> Creating…';
    try {
      var res = await sb.functions.invoke('create-staff-user', {
        body: { email: fd.get('email'), name: fd.get('name'), phone: fd.get('phone') || '', region: fd.get('region'), targetRole: 'property_executive', managerId: fd.get('manager_id') }
      });
      if (res.error) throw res.error;
      if (res.data && res.data.error) throw new Error(res.data.error);
      var reveal = document.getElementById('executivePassReveal');
      reveal.style.display = 'block';
      reveal.innerHTML = 'Account created for <b>' + N.escapeHtml(res.data.email) + '</b>. Temporary password (share securely, shown once): <br><code>' + N.escapeHtml(res.data.temporaryPassword) + '</code>';
      e.target.reset();
      await loadExecutives();
    } catch (err) {
      alertEl.textContent = err.message || 'Could not create this account.'; alertEl.className = 'alert error show';
    } finally {
      btn.disabled = false; btn.textContent = 'Create Account';
    }
  }

  /* ============================== MODALS ============================== */
  function bindModals() {
    document.querySelectorAll('[data-close-modal]').forEach(function (btn) {
      btn.addEventListener('click', function () { closeModal(btn.dataset.closeModal); });
    });
    document.querySelectorAll('.modal-backdrop').forEach(function (backdrop) {
      backdrop.addEventListener('click', function (e) { if (e.target === backdrop) backdrop.classList.remove('open'); });
    });
  }
  function openModal(id) { document.getElementById(id).classList.add('open'); }
  function closeModal(id) { document.getElementById(id).classList.remove('open'); }

  boot();
})();
