/**
 * app/admin/page.js — Admin console (server component).
 *
 * Requires: session + role 'owner'.
 * Local mode: synthetic user has role 'owner' — renders for dev.
 * Supabase mode: 403 for non-owner roles.
 *
 * Sections:
 *   1. Org Settings    — MFA, IP allowlist, session timeout, share email verification
 *   2. Members         — list, invite, role change, remove
 *   3. Audit Log       — client-side paginated table with filters
 *   4. Share Link Mgmt — list + bulk revoke
 *   5. Trust Mode      — read/update main-agent trust mode
 */

import { redirect } from 'next/navigation';
import { AdminShell, StatCard, StatusPill, EmptyState } from '@/app/components/admin/admin-page.jsx';
import { requireSession } from '@/lib/auth.js';
import { requireRole } from '@/lib/authz.js';
import { getOrgById, listMembers } from '@/lib/org.js';
import { getMainAgentTrustMode } from '@/lib/tools/registry.js';
import { getDb } from '@/lib/db.js';
import AuditTable from './audit-table.jsx';

function getAuditSpamStats() {
  try {
    const db = getDb();
    const row = db.prepare(`
      SELECT
        (SELECT COUNT(*) FROM audit_logs) AS total,
        (SELECT COUNT(*) FROM audit_logs WHERE type = 'agent.create') AS agent_create,
        (SELECT COUNT(*) FROM audit_logs WHERE type = 'disk_encryption.health') AS disk_enc,
        (SELECT page_count * page_size FROM pragma_page_count(), pragma_page_size()) AS db_bytes
    `).get();
    return row || { total: 0, agent_create: 0, disk_enc: 0, db_bytes: 0 };
  } catch {
    return { total: 0, agent_create: 0, disk_enc: 0, db_bytes: 0 };
  }
}

function formatBytes(n) {
  if (!n || n === 0) return '0 B';
  if (n < 1024) return `${n} B`;
  if (n < 1024 * 1024) return `${(n / 1024).toFixed(1)} KB`;
  if (n < 1024 * 1024 * 1024) return `${(n / (1024 * 1024)).toFixed(1)} MB`;
  return `${(n / (1024 * 1024 * 1024)).toFixed(2)} GB`;
}

export const runtime = 'nodejs';
export const dynamic = 'force-dynamic';

// ── Auth guard ────────────────────────────────────────────────────────────────

async function getAdminSession() {
  // In server components we have no Request object; build a minimal stand-in.
  // In local mode, requireSession() ignores the request entirely.
  try {
    const session = await requireSession(null);
    requireRole(session, 'owner');
    return session;
  } catch (err) {
    if (err && typeof err === 'object' && (err.status === 401 || err.status === 403)) {
      return null;
    }
    throw err;
  }
}

// ── Page ─────────────────────────────────────────────────────────────────────

export default async function AdminPage() {
  const session = await getAdminSession();
  if (!session) {
    redirect('/login');
  }

  const orgId = session.org_id;
  const org = orgId ? getOrgById(orgId) : null;
  const members = orgId ? listMembers(orgId) : [];
  const trustMode = getMainAgentTrustMode();

  const cidrList = org?.ip_allowlist_json
    ? (() => { try { return JSON.parse(org.ip_allowlist_json); } catch { return []; } })()
    : [];

  return (
    <AdminShell
      kicker="Admin · madarX OS"
      title="Admin Console"
      description="Organization security settings, membership, audit log, and share link management."
      hideProjectSwitcher
    >
      <div className="mkt-page" data-page="admin">

      {/* ── Stats strip — mkt style, matches /projects + /marketplace ──── */}
      <section className="mkt-stats">
        <div className="mkt-stat">
          <b>{members.length}</b>
          <span>Members</span>
        </div>
        <div className="mkt-stat">
          <b>{org?.mfa_required ? 'Required' : 'Optional'}</b>
          <span>MFA policy</span>
        </div>
        <div className="mkt-stat">
          <b>{trustMode === 'auto' ? 'Auto' : 'Approval'}</b>
          <span>Trust mode</span>
        </div>
        <div className="mkt-stat">
          <b>{cidrList.length > 0 ? cidrList.length : 'Open'}</b>
          <span>IP allowlist</span>
        </div>
      </section>

      {/* ── 1. Org Settings ──────────────────────────────────────────────── */}
      <section className="admin-panel">
        <div className="admin-panel-head">
          <span>Org Settings</span>
          <small>Changes take effect immediately. Each change is written to the compliance audit log.</small>
        </div>
        <div className="admin-card-list">
          <form className="admin-record" action={`/api/orgs/${orgId}`} method="PATCH" id="org-settings-form">
            <div className="admin-form-grid">

              {/* MFA */}
              <label className="admin-field">
                <span>MFA required</span>
                <select name="mfa_required" defaultValue={org?.mfa_required ? 'true' : 'false'}
                  onChange="this.form.dispatchEvent(new Event('change'))">
                  <option value="false">Not required</option>
                  <option value="true">Required</option>
                </select>
                <small>When enabled, all members must complete MFA before accessing the org.</small>
              </label>

              {/* Session timeout */}
              <label className="admin-field">
                <span>Session timeout (seconds)</span>
                <input
                  type="number"
                  name="session_timeout_seconds"
                  defaultValue={org?.session_timeout_seconds ?? ''}
                  min="60"
                  placeholder="No timeout"
                />
                <small>Minimum 60 seconds. Leave blank for no timeout.</small>
              </label>

              {/* Share email verification */}
              <label className="admin-field">
                <span>Require email verification for share links</span>
                <select name="require_share_email_verification"
                  defaultValue={org?.require_share_email_verification ? 'true' : 'false'}>
                  <option value="false">Not required</option>
                  <option value="true">Required (OTP sent to viewer)</option>
                </select>
                <small>When enabled, share link viewers must verify their email via one-time code.</small>
              </label>

              {/* IP allowlist */}
              <label className="admin-field admin-field-wide">
                <span>IP allowlist (CIDR per line, IPv4 only)</span>
                <textarea
                  name="ip_allowlist"
                  rows="4"
                  defaultValue={cidrList.join('\n')}
                  placeholder={'10.0.0.0/8\n192.168.1.0/24'}
                  style={{ fontFamily: 'var(--font-mono-brand, monospace)', fontSize: '0.85em' }}
                />
                <small>Leave blank to allow all IPs. Changes are written to the audit log.</small>
              </label>

            </div>

            <OrgSettingsSubmit orgId={orgId} />
          </form>
        </div>
      </section>

      {/* ── 2. Members & invites → moved to /settings/team ───────────────── */}
      <section className="admin-panel">
        <div className="admin-panel-head">
          <span>Members &amp; invites</span>
          <small>Roster, role changes, removals, and shareable invite links now live in Team settings.</small>
        </div>
        <div className="admin-card-list">
          <div className="admin-record">
            <div className="admin-record-top">
              <b>{members.length} member{members.length === 1 ? '' : 's'}</b>
              <a href="/settings/team" className="admin-btn-primary admin-btn-sm">Open Team settings</a>
            </div>
            <p className="admin-muted" style={{ fontSize: '0.9em', marginTop: '0.4rem' }}>
              Manage who's in this organization and invite teammates with one shareable link.
            </p>
          </div>
        </div>
      </section>

      {/* ── 3. Audit Log ─────────────────────────────────────────────────── */}
      <section className="admin-panel">
        <div className="admin-panel-head">
          <span>Audit Log</span>
          <div style={{ display: 'flex', gap: '0.5rem', alignItems: 'center' }}>
            <a href="/api/admin/audit-export?format=ndjson" className="admin-btn-sm">Export NDJSON</a>
            <a href="/api/admin/audit-export?format=csv" className="admin-btn-sm">Export CSV</a>
          </div>
        </div>
        <AuditTable orgId={orgId} />
      </section>

      {/* ── 4. Share Link Management ──────────────────────────────────────── */}
      <section className="admin-panel">
        <div className="admin-panel-head">
          <span>Share Link Management</span>
          <small>Active (non-revoked) share links for this org. Bulk-revoke by creator.</small>
        </div>
        <ShareLinksPanel orgId={orgId} members={members} />
      </section>

      {/* ── 5. Trust Mode ─────────────────────────────────────────────────── */}
      <section className="admin-panel">
        <div className="admin-panel-head">
          <span>Main Agent Trust Mode</span>
          <small>Controls whether risky tool calls require owner approval. Irreversible-external tools always require approval regardless of this setting.</small>
        </div>
        <div className="admin-card-list">
          <div className="admin-record">
            <div className="admin-record-top">
              <b>Current mode</b>
              <StatusPill tone={trustMode === 'auto' ? 'warn' : 'ok'}>
                {trustMode === 'auto' ? 'Auto (trust all)' : 'Approval required'}
              </StatusPill>
            </div>
            <p style={{ marginBottom: '1rem', color: 'var(--color-muted, #666)', fontSize: '0.9em' }}>
              {trustMode === 'auto'
                ? 'The main agent may execute high-risk tool calls without owner approval. Irreversible-external tools (email, GitHub merge, SQL) still require approval.'
                : 'The main agent must request owner approval before executing high-risk tool calls.'}
            </p>
            <TrustModeToggle current={trustMode} />
          </div>
        </div>
      </section>

      {/* ── 6. Maintenance ──────────────────────────────────────────────── */}
      <MaintenancePanel stats={getAuditSpamStats()} />

      </div>
    </AdminShell>
  );
}

function MaintenancePanel({ stats }) {
  const spamCount = (stats.agent_create || 0) + (stats.disk_enc || 0);
  const spamRatio = stats.total > 0 ? Math.round((spamCount / stats.total) * 100) : 0;
  return (
    <section className="admin-panel">
      <div className="admin-panel-head">
        <span>Maintenance</span>
        <small>SQLite database health and one-shot cleanup tools. The audit log is append-only; maintenance scripts drop and recreate the triggers inside a transaction.</small>
      </div>
      <section className="mkt-stats">
        <div className="mkt-stat">
          <b>{formatBytes(stats.db_bytes)}</b>
          <span>DB size</span>
        </div>
        <div className="mkt-stat">
          <b>{stats.total.toLocaleString()}</b>
          <span>audit_logs rows</span>
        </div>
        <div className="mkt-stat">
          <b>{stats.agent_create.toLocaleString()}</b>
          <span>agent.create</span>
        </div>
        <div className="mkt-stat">
          <b>{stats.disk_enc.toLocaleString()}</b>
          <span>disk_encryption</span>
        </div>
      </section>
      {spamRatio > 50 ? (
        <div className="admin-record needs-action">
          <div className="admin-record-top">
            <b>Audit log contains pre-fix logger spam</b>
            <StatusPill tone="warn">{spamRatio}% spam</StatusPill>
          </div>
          <p>
            Earlier versions wrote one <code>agent.create</code> row per re-seed and one <code>disk_encryption.health</code> row per dev-mode worker boot.
            Both loggers are now fixed in code; the existing rows are inert but bloat the database.
          </p>
          <pre>{`node scripts/cleanup-audit-spam.mjs              # dry-run, prints counts
MADARX_AUDIT_CLEANUP_CONFIRM=1 \\
  node scripts/cleanup-audit-spam.mjs --apply   # delete + VACUUM`}</pre>
          <small>
            The script keeps the earliest <code>agent.create</code> per agent_id and the most recent <code>disk_encryption.health</code> row. Everything else in <code>audit_logs</code> is untouched. A new <code>audit.cleanup</code> row is written to record what was removed.
          </small>
        </div>
      ) : (
        <EmptyState title="Audit log is clean">
          No legacy logger spam detected. Database health looks normal.
        </EmptyState>
      )}
    </section>
  );
}

// ── Sub-components (server, no client state needed) ───────────────────────────

function OrgSettingsSubmit({ orgId }) {
  async function handleSubmit(formData) {
    'use server';
    // This Server Action translates the HTML form into the PATCH API call.
    // The form can't PATCH natively; we do it server-side.
    const { revalidatePath } = await import('next/cache');

    const mfa_required = formData.get('mfa_required') === 'true';
    const session_timeout_raw = formData.get('session_timeout_seconds');
    const session_timeout_seconds = session_timeout_raw && session_timeout_raw.trim() !== ''
      ? parseInt(session_timeout_raw, 10)
      : null;
    const require_share_email_verification = formData.get('require_share_email_verification') === 'true';
    const ip_raw = String(formData.get('ip_allowlist') || '').trim();
    const ip_allowlist_json = ip_raw
      ? ip_raw.split(/\r?\n/).map(s => s.trim()).filter(Boolean)
      : null;

    // Direct domain call (server action runs in Node context with DB access).
    const { getDb, nowIso, newId } = await import('@/lib/db.js');
    const { appendComplianceAudit, ComplianceActions } = await import('@/lib/compliance-audit.js');
    const { getOrgById } = await import('@/lib/org.js');
    const { getLocalDevUser, getAuthMode } = await import('@/lib/auth.js');

    const db = getDb();
    const session = getAuthMode() === 'local' ? getLocalDevUser() : null;
    const userId = session?.user_id || 'server-action';

    const org = getOrgById(orgId);
    if (!org) return;

    // Validate CIDRs.
    if (ip_allowlist_json) {
      const net = await import('node:net');
      for (const cidr of ip_allowlist_json) {
        const slash = cidr.lastIndexOf('/');
        if (slash < 0) continue;
        const host = cidr.slice(0, slash);
        const bits = parseInt(cidr.slice(slash + 1), 10);
        if (!net.isIPv4(host) || isNaN(bits) || bits < 0 || bits > 32) {
          // Skip invalid CIDR silently in server action context.
          return;
        }
      }
    }

    const updates = {
      mfa_required: mfa_required ? 1 : 0,
      session_timeout_seconds,
      require_share_email_verification: require_share_email_verification ? 1 : 0,
      ip_allowlist_json: ip_allowlist_json ? JSON.stringify(ip_allowlist_json) : null,
    };
    const setClauses = Object.keys(updates).map(k => `${k} = ?`).join(', ');
    db.prepare(`UPDATE organizations SET ${setClauses}, updated_at = ? WHERE id = ?`)
      .run(...Object.values(updates), nowIso(), orgId);

    // Audit MFA change if it changed.
    if (Boolean(org.mfa_required) !== mfa_required) {
      try {
        appendComplianceAudit({
          user_id: userId,
          org_id: orgId,
          action: ComplianceActions.ORG_MFA_POLICY_CHANGED,
          action_target_id: orgId,
          action_target_type: 'org',
          status: 'success',
          summary: `MFA policy changed to ${mfa_required ? 'required' : 'not required'}`,
          meta: { mfa_required, previous: Boolean(org.mfa_required) },
        });
      } catch {}
    }

    // Audit IP allowlist change.
    try {
      appendComplianceAudit({
        user_id: userId,
        org_id: orgId,
        action: ComplianceActions.ORG_IP_ALLOWLIST_CHANGED,
        action_target_id: orgId,
        action_target_type: 'org',
        status: 'success',
        summary: `IP allowlist updated (${ip_allowlist_json ? ip_allowlist_json.length : 0} entries)`,
        meta: { entry_count: ip_allowlist_json ? ip_allowlist_json.length : 0 },
      });
    } catch {}

    revalidatePath('/admin');
  }

  return (
    <div className="admin-form-actions">
      <button type="submit" formAction={handleSubmit} className="admin-btn-primary">Save settings</button>
    </div>
  );
}

// (Removed the old direct-add InviteForm: it created a placeholder users row +
// immediate membership — the superseded model. Invites now flow through
// <InvitesPanel> → /api/orgs/[id]/invites, which creates an email pre-invite
// (claimed on the invitee's verified login) or a hashed shareable link.)

function ShareLinksPanel({ orgId, members }) {
  return (
    <div className="admin-card-list">
      <div className="admin-record">
        <p style={{ color: 'var(--color-muted, #666)', fontSize: '0.9em', marginBottom: '1rem' }}>
          Share links are loaded client-side. Use the filter and bulk-revoke controls below.
        </p>
        <ShareLinksList orgId={orgId} members={members} />
      </div>
    </div>
  );
}

function ShareLinksList({ orgId, members }) {
  // Inline static table — client component (audit-table.jsx) handles the interactive share-links tab.
  // For now, provide direct links to the API and a static revoke-by-user form.
  return (
    <div style={{ display: 'flex', flexDirection: 'column', gap: '1rem' }}>
      <div style={{ display: 'flex', gap: '0.5rem', flexWrap: 'wrap' }}>
        <a href="/api/admin/share-links" target="_blank" className="admin-btn-sm">View active links (JSON)</a>
      </div>

      {members.length > 0 && (
        <div>
          <p style={{ fontSize: '0.85em', fontWeight: 600, marginBottom: '0.5rem' }}>Bulk revoke by member:</p>
          <div style={{ display: 'flex', flexDirection: 'column', gap: '0.5rem' }}>
            {members.map(member => (
              <RevokeByUserForm key={member.user_id} member={member} />
            ))}
          </div>
        </div>
      )}
    </div>
  );
}

function RevokeByUserForm({ member }) {
  async function handleRevoke(formData) {
    'use server';
    const { revalidatePath } = await import('next/cache');
    const userId = String(formData.get('user_id') || '');
    const orgId = String(formData.get('org_id') || '');
    if (!userId || !orgId) return;

    const { getDb, nowIso } = await import('@/lib/db.js');
    const { appendComplianceAudit, ComplianceActions } = await import('@/lib/compliance-audit.js');
    const { getLocalDevUser, getAuthMode } = await import('@/lib/auth.js');

    const db = getDb();
    const session = getAuthMode() === 'local' ? getLocalDevUser() : null;

    const toRevoke = db.prepare(
      `SELECT id FROM artifact_share_links WHERE org_id = ? AND created_by = ? AND revoked = 0`
    ).all(orgId, userId);

    if (toRevoke.length > 0) {
      const placeholders = toRevoke.map(() => '?').join(', ');
      db.prepare(`UPDATE artifact_share_links SET revoked = 1 WHERE id IN (${placeholders})`)
        .run(...toRevoke.map(r => r.id));

      for (const link of toRevoke) {
        try {
          appendComplianceAudit({
            user_id: session?.user_id || 'server-action',
            org_id: orgId,
            action: ComplianceActions.ARTIFACT_SHARE_LINK_REVOKED,
            action_target_id: link.id,
            action_target_type: 'share_link',
            status: 'success',
            summary: `Share link ${link.id} bulk-revoked (target user: ${userId})`,
            meta: { share_link_id: link.id, revoked_user_id: userId, bulk: true },
          });
        } catch {}
      }
    }

    revalidatePath('/admin');
  }

  return (
    <form action={handleRevoke} style={{ display: 'flex', alignItems: 'center', gap: '0.75rem' }}>
      <input type="hidden" name="user_id" value={member.user_id} />
      <input type="hidden" name="org_id" value={member.org_id} />
      <span style={{ fontSize: '0.9em' }}>{member.email || member.display_name || member.user_id}</span>
      <button type="submit" className="admin-btn-danger-sm"
        onClick="return confirm('Revoke all share links for this user?')">
        Revoke all links
      </button>
    </form>
  );
}

function TrustModeToggle({ current }) {
  async function handleToggle(formData) {
    'use server';
    const { revalidatePath } = await import('next/cache');
    const next = String(formData.get('mode') || '');
    if (next !== 'auto' && next !== 'approval_required') return;

    const { setConfig } = await import('@/lib/config.js');
    const { TRUST_MODE_CONFIG_KEY } = await import('@/lib/tools/registry.js');
    const { appendAudit } = await import('@/lib/runtime.js');

    setConfig(TRUST_MODE_CONFIG_KEY, next);
    appendAudit({
      type: next === 'auto' ? 'settings.trust_mode.enabled' : 'settings.trust_mode.disabled',
      summary: `Main agent trust mode set to ${next} via admin console`,
      status: next,
      meta: { previous: current, next, actor: 'admin-console' },
    });

    revalidatePath('/admin');
  }

  const next = current === 'auto' ? 'approval_required' : 'auto';

  return (
    <form action={handleToggle}>
      <input type="hidden" name="mode" value={next} />
      <button
        type="submit"
        className={current === 'auto' ? 'admin-btn-danger-sm' : 'admin-btn-primary'}
        onClick={current === 'auto' ? "return confirm('Switch to approval_required mode?')" : "return confirm('Switch to auto trust mode? The agent will execute high-risk tools without per-call approval.')"}
      >
        {current === 'auto' ? 'Switch to approval required' : 'Switch to auto trust'}
      </button>
    </form>
  );
}
