/**
 * app/api/orgs/[id]/route.js — PATCH org settings (owner-only).
 *
 * Validates: mfa_required, ip_allowlist_json, session_timeout_seconds,
 *            require_share_email_verification.
 * Each changed field emits a typed compliance audit row.
 */

import net from 'node:net';
import { getDb, nowIso } from '@/lib/db.js';
import { requireSession, requireFreshMembership } from '@/lib/auth.js';
import { requireRole } from '@/lib/authz.js';
import { appendComplianceAudit, ComplianceActions, requestIp, requestUserAgent } from '@/lib/compliance-audit.js';
import { getOrgById } from '@/lib/org.js';

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

// ── CIDR validation helper (no external dep) ─────────────────────────────────

function isValidCidr(cidr) {
  if (typeof cidr !== 'string') return false;
  const slash = cidr.lastIndexOf('/');
  if (slash < 0) return false;
  const host = cidr.slice(0, slash);
  const bits = cidr.slice(slash + 1);
  if (!net.isIPv4(host)) return false;
  const prefix = parseInt(bits, 10);
  if (isNaN(prefix) || prefix < 0 || prefix > 32) return false;
  return true;
}

function validateCidrList(list) {
  if (!Array.isArray(list)) return { ok: false, error: 'ip_allowlist_json must be an array of CIDR strings' };
  for (const cidr of list) {
    if (!isValidCidr(cidr)) return { ok: false, error: `Invalid CIDR: "${cidr}" (only IPv4 /0–/32 supported)` };
  }
  return { ok: true };
}

// ── PATCH /api/orgs/[id] ─────────────────────────────────────────────────────

export async function PATCH(req, { params }) {
  let session;
  const { id: orgId } = await params;
  try {
    session = await requireSession(req);
    await requireFreshMembership(session, orgId);
    requireRole(session, 'owner');
  } catch (err) {
    if (err && typeof err === 'object' && err.status) {
      return Response.json({ error: err.error || 'unauthorized' }, { status: err.status });
    }
    throw err;
  }

  // Verify the org exists and caller belongs to it.
  const org = getOrgById(orgId);
  if (!org) {
    return Response.json({ error: 'org_not_found' }, { status: 404 });
  }
  if (session.org_id !== orgId) {
    return Response.json({ error: 'insufficient_role' }, { status: 403 });
  }

  let body;
  try {
    body = await req.json();
  } catch {
    return Response.json({ error: 'Invalid JSON body' }, { status: 400 });
  }

  const db = getDb();
  const now = nowIso();
  const ip = requestIp(req);
  const ua = requestUserAgent(req);

  // Accumulate validated updates and audit actions.
  const updates = {};
  const auditRows = [];

  // ── name ─────────────────────────────────────────────────────────────────────
  if ('name' in body) {
    const raw = typeof body.name === 'string' ? body.name.trim() : '';
    if (!raw) {
      return Response.json({ error: 'name must be a non-empty string' }, { status: 400 });
    }
    if (raw.length > 80) {
      return Response.json({ error: 'name must be 80 characters or fewer' }, { status: 400 });
    }
    if (raw !== org.name) {
      updates.name = raw;
      auditRows.push({
        action: 'org.name.changed',
        summary: `Organization renamed to "${raw}"`,
        meta: { org_id: orgId, name: raw, previous: org.name },
      });
    }
  }

  // ── mfa_required ────────────────────────────────────────────────────────────
  if ('mfa_required' in body) {
    const v = body.mfa_required;
    if (typeof v !== 'boolean') {
      return Response.json({ error: 'mfa_required must be a boolean' }, { status: 400 });
    }
    updates.mfa_required = v ? 1 : 0;
    auditRows.push({
      action: ComplianceActions.ORG_MFA_POLICY_CHANGED,
      summary: `MFA policy changed to ${v ? 'required' : 'not required'}`,
      meta: { org_id: orgId, mfa_required: v, previous: Boolean(org.mfa_required) },
    });
  }

  // ── ip_allowlist_json ────────────────────────────────────────────────────────
  if ('ip_allowlist_json' in body) {
    const v = body.ip_allowlist_json;
    // Accept null (clear) or array.
    if (v !== null) {
      const valid = validateCidrList(v);
      if (!valid.ok) {
        return Response.json({ error: valid.error }, { status: 400 });
      }
      updates.ip_allowlist_json = JSON.stringify(v);
    } else {
      updates.ip_allowlist_json = null;
    }
    auditRows.push({
      action: ComplianceActions.ORG_IP_ALLOWLIST_CHANGED,
      summary: `IP allowlist updated (${v ? v.length : 0} entries)`,
      meta: { org_id: orgId, entry_count: v ? v.length : 0 },
    });
  }

  // ── session_timeout_seconds ──────────────────────────────────────────────────
  if ('session_timeout_seconds' in body) {
    const v = body.session_timeout_seconds;
    if (v !== null && (typeof v !== 'number' || !Number.isInteger(v) || v < 60)) {
      return Response.json({ error: 'session_timeout_seconds must be an integer >= 60 or null' }, { status: 400 });
    }
    updates.session_timeout_seconds = v;
    auditRows.push({
      action: 'org.session_timeout.changed',
      summary: `Session timeout changed to ${v ?? 'none'}s`,
      meta: { org_id: orgId, session_timeout_seconds: v },
    });
  }

  // ── require_share_email_verification ────────────────────────────────────────
  if ('require_share_email_verification' in body) {
    const v = body.require_share_email_verification;
    if (typeof v !== 'boolean') {
      return Response.json({ error: 'require_share_email_verification must be a boolean' }, { status: 400 });
    }
    updates.require_share_email_verification = v ? 1 : 0;
    auditRows.push({
      action: 'org.share_email_verification.changed',
      summary: `Share email verification ${v ? 'enabled' : 'disabled'}`,
      meta: { org_id: orgId, require_share_email_verification: v },
    });
  }

  if (Object.keys(updates).length === 0) {
    return Response.json({ ok: true, message: 'No changes', org: _safeOrg(org) });
  }

  // Build SET clause.
  const setClauses = Object.keys(updates).map(k => `${k} = ?`).join(', ');
  const values = [...Object.values(updates), now, orgId];
  db.prepare(`UPDATE organizations SET ${setClauses}, updated_at = ? WHERE id = ?`).run(...values);

  // Write compliance audit rows for each changed field.
  for (const row of auditRows) {
    try {
      appendComplianceAudit({
        user_id: session.user_id,
        org_id: orgId,
        action: row.action,
        action_target_id: orgId,
        action_target_type: 'org',
        status: 'success',
        summary: row.summary,
        meta: row.meta,
        ip,
        user_agent: ua,
      });
    } catch (auditErr) {
      console.error('[orgs/[id]] Failed to write audit row:', auditErr.message);
    }
  }

  const updated = getOrgById(orgId);

  // Propagate a rename (and ensure the org exists at all) to the cloud so every
  // member's view of the org name stays in sync and invite/membership FKs hold.
  // Best-effort: the local update already committed.
  if ('name' in updates) {
    try {
      const { ensureCloudOrg } = await import('@/lib/shared-cloud.js');
      await ensureCloudOrg(updated);
    } catch (cloudErr) {
      console.warn('[orgs/[id]] cloud org sync failed (local update succeeded):', cloudErr.message);
    }
  }

  return Response.json({ ok: true, org: _safeOrg(updated) });
}

// ── GET /api/orgs/[id] ───────────────────────────────────────────────────────

export async function GET(req, { params }) {
  let session;
  try {
    session = await requireSession(req);
    requireRole(session, 'admin');
  } catch (err) {
    if (err && typeof err === 'object' && err.status) {
      return Response.json({ error: err.error || 'unauthorized' }, { status: err.status });
    }
    throw err;
  }

  const { id: orgId } = await params;
  const org = getOrgById(orgId);
  if (!org) return Response.json({ error: 'org_not_found' }, { status: 404 });
  if (session.org_id !== orgId) return Response.json({ error: 'insufficient_role' }, { status: 403 });

  return Response.json({ ok: true, org: _safeOrg(org) });
}

function _safeOrg(org) {
  if (!org) return null;
  return {
    id: org.id,
    slug: org.slug,
    name: org.name,
    plan: org.plan,
    mfa_required: Boolean(org.mfa_required),
    ip_allowlist_json: org.ip_allowlist_json ? JSON.parse(org.ip_allowlist_json) : null,
    session_timeout_seconds: org.session_timeout_seconds ?? null,
    require_share_email_verification: Boolean(org.require_share_email_verification),
    created_at: org.created_at,
    updated_at: org.updated_at,
  };
}
