// Coach admin orchestrator endpoints — ECO-15 / ECO-38.
//
// HTTP handlers called by the coach admin dashboard at
// vianexu.com/clients/ecobe/admin. The dashboard renders the UI; these
// endpoints provide the data + drive the WhatsApp side-effects.
//
// Endpoints (all bearer-auth via WHATSAPP_ORCHESTRATOR_TOKEN):
//
//   GET  /admin/silver/pending-unlock
//        --> { items: [...] } list users at the gate (dossier_pending +
//            dossier:ready_for_review + dossier:approved-but-not-unlocked).
//
//   GET  /admin/silver/users/:slug
//        --> full silver.json + journey events + recent escalations + Q&A
//            snippets, for the dashboard detail pane.
//
//   POST /admin/silver/users/:slug/trigger-dossier
//        body: { coachEmail }
//        --> calls silverCourse.startDossierCollection(); bot starts
//            multi-turn dossier collection in the user's WhatsApp group.
//
//   POST /admin/silver/users/:slug/unlock-jobbot
//        body: { coachEmail }
//        --> registers the token with Job-bot via jobbotClient, then calls
//            silverCourse.unlockJobbotForUser() to mint+send link in WhatsApp,
//            flips accessLevel = 'full'.
//
// All handlers return { status, body } following the lib/hotmartWebhook.js
// shape so they integrate with the http.createServer dispatcher in index.js.

import { existsSync, readFileSync, readdirSync } from 'fs';
import { join } from 'path';
import { loadSilver, ACCESS_LEVELS } from './silverStore.js';
import { startDossierCollection, unlockJobbotForUser, applyCoachJoin, markStrategyCompleted } from './silverCourse.js';
import { registerToken } from './jobbotClient.js';

// Auth

export function verifyAuth(headers) {
  const expected = process.env.WHATSAPP_ORCHESTRATOR_TOKEN;
  if (!expected) return { ok: false, reason: 'orchestrator-token-not-configured', status: 503 };
  const auth = headers.authorization || headers.Authorization || '';
  const m = /^Bearer\s+(.+)$/i.exec(auth);
  if (!m) return { ok: false, reason: 'no-bearer', status: 401 };
  if (m[1] !== expected) return { ok: false, reason: 'bad-token', status: 401 };
  return { ok: true };
}

// List of users at the unlock gate

export function listPendingUnlock(scriptDir) {
  const root = join(scriptDir, 'mentees');
  if (!existsSync(root)) return [];
  const items = [];
  for (const slug of readdirSync(root)) {
    const sp = join(root, slug, 'silver.json');
    if (!existsSync(sp)) continue;
    const state = loadSilver(scriptDir, slug);
    if (!state) continue;

    const atGate = state.accessLevel === ACCESS_LEVELS.DOSSIER_PENDING;
    const inCollection = state.dossier?.status === 'collecting';
    const readyForReview = state.dossier?.status === 'ready_for_review';
    if (!atGate && !inCollection && !readyForReview) continue;

    const completedLessons = Object.values(state.lessons || {}).filter(l => l.completedAt).length;
    items.push({
      slug,
      leadName: state.leadName,
      accessLevel: state.accessLevel,
      coachJid: state.coachJid,
      module1CompletedAt: state.module1CompletedAt,
      coachTriggeredAt: state.coachTriggeredAt,
      coachUnlockedAt: state.coachUnlockedAt,
      dossierStatus: state.dossier?.status || null,
      dossierGeneratedAt: state.dossier?.generatedAt,
      lastInteractionAt: state.lastInteractionAt,
      completedLessons,
      escalationCount: (state.escalations || []).length,
    });
  }
  items.sort((a, b) => (b.module1CompletedAt || '').localeCompare(a.module1CompletedAt || ''));
  return items;
}

// Detail view

export function userDetail(scriptDir, slug) {
  const state = loadSilver(scriptDir, slug);
  if (!state) return null;
  const journeyPath = join(scriptDir, 'mentees', slug, 'journey.json');
  let journey = null;
  if (existsSync(journeyPath)) {
    try { journey = JSON.parse(readFileSync(journeyPath, 'utf8')); } catch { journey = null; }
  }
  return {
    slug,
    state,
    journey,
    qaHistoryRecent: (state.qaHistory || []).slice(-10),
    escalationsRecent: (state.escalations || []).slice(-10),
    weeklyCheckins: state.weeklyCheckins || [],
  };
}

// HTTP dispatcher (called from index.js http.createServer)

export async function handleCoachAdminRequest(scriptDir, req, rawBody) {
  const auth = verifyAuth(req.headers);
  if (!auth.ok) {
    return { status: auth.status || 401, body: { error: auth.reason } };
  }

  const url = new URL(req.url, `http://internal`);
  const m = url.pathname.match(/^\/admin\/silver\/(.+)$/);
  if (!m) return { status: 404, body: { error: 'not-found' } };
  const sub = m[1];

  if (req.method === 'GET' && sub === 'pending-unlock') {
    const items = listPendingUnlock(scriptDir);
    return { status: 200, body: { items, count: items.length } };
  }

  let userMatch = sub.match(/^users\/([^/]+)$/);
  if (req.method === 'GET' && userMatch) {
    const detail = userDetail(scriptDir, userMatch[1]);
    if (!detail) return { status: 404, body: { error: 'user-not-found' } };
    return { status: 200, body: detail };
  }

  // ECO-39: force-coach-join (manual override when the Baileys event was missed
  // or the coach was added at group creation rather than as a later join).
  userMatch = sub.match(/^users\/([^/]+)\/force-coach-join$/);
  if (req.method === 'POST' && userMatch) {
    const slug = userMatch[1];
    const body = parseJsonBody(rawBody);
    const coachEmail = body?.coachEmail || null;
    const coachJid = body?.coachJid || null;
    const coachName = body?.coachName || coachEmail || null;
    const result = await applyCoachJoin(scriptDir, slug, {
      coachJid,
      coachName,
      source: 'force-admin',
    });
    if (!result.ok) {
      return { status: result.reason === 'no-state' ? 404 : 409, body: result };
    }
    return { status: 200, body: { ...result, coachEmail } };
  }

  // ECO-41: website pings this when the user submits the career-strategy form.
  // Flips dossier.status='ready_for_review', sends ack to the WhatsApp group,
  // surfaces in coach pending-unlock queue.
  userMatch = sub.match(/^users\/([^/]+)\/strategy-completed$/);
  if (req.method === 'POST' && userMatch) {
    const slug = userMatch[1];
    const body = parseJsonBody(rawBody);
    const coachEmail = body?.coachEmail || null;
    const result = await markStrategyCompleted(scriptDir, slug, { coachEmail });
    if (!result.ok) return { status: result.reason === 'no-state' ? 404 : 409, body: result };
    return { status: 200, body: result };
  }

  userMatch = sub.match(/^users\/([^/]+)\/trigger-dossier$/);
  if (req.method === 'POST' && userMatch) {
    const slug = userMatch[1];
    const body = parseJsonBody(rawBody);
    const coachEmail = body?.coachEmail || null;
    const result = await startDossierCollection(scriptDir, slug, { coachEmail });
    if (!result.ok) {
      return { status: result.reason === 'no-state' ? 404 : 409, body: result };
    }
    return { status: 200, body: result };
  }

  userMatch = sub.match(/^users\/([^/]+)\/unlock-jobbot$/);
  if (req.method === 'POST' && userMatch) {
    const slug = userMatch[1];
    const body = parseJsonBody(rawBody);
    const coachEmail = body?.coachEmail || null;

    // Mint token + send WhatsApp link first (the user-visible bit the coach
    // is paying for). If Job-bot registration fails afterwards we DON'T roll
    // back; the user has the link. We surface the warning in response body.
    const unlockResult = await unlockJobbotForUser(scriptDir, slug, { coachEmail });
    if (!unlockResult.ok) {
      return { status: unlockResult.reason === 'no-state' ? 404 : 409, body: unlockResult };
    }

    let jobbotRegistration = null;
    let jobbotError = null;
    try {
      const state = loadSilver(scriptDir, slug);
      jobbotRegistration = await registerToken({
        phone: state.clientPhone,
        userId: slug,
        token: unlockResult.token,
        ttlMinutes: 30,
      });
    } catch (err) {
      jobbotError = { message: err.message, status: err.status, body: err.body };
    }

    return {
      status: 200,
      body: {
        ...unlockResult,
        jobbotRegistration,
        jobbotError,
      },
    };
  }

  return { status: 404, body: { error: 'unknown-route' } };
}

function parseJsonBody(rawBody) {
  if (!rawBody) return null;
  const text = Buffer.isBuffer(rawBody) ? rawBody.toString('utf8') : String(rawBody);
  if (!text) return null;
  try { return JSON.parse(text); } catch { return null; }
}
