// lib/hail/engagements.js — HAIL engagement domain.
//
// An engagement is the lifecycle container for one principal/institution. It
// owns a project (where its artifacts live) and a process-engine case (the
// hail-engagement-lifecycle definition) that is the SOLE source of truth for
// "which stage gate is this engagement at". HAIL code here NEVER writes
// process_cases.* — it only calls the engine's startCase and reads back the id.
//
// See specs/active/hail-operating-model.md.

import { getDb, nowIso, newId } from '../db.js';
import { createProject, getProject } from '../projects.js';
import { appendAudit } from '../runtime.js';
import { startCase } from '../process/engine.js';
import { HAIL_ENGAGEMENT_LIFECYCLE_SLUG } from '../process/seeds/hail-engagement-lifecycle.js';
import { LIFECYCLE_STAGE_SET } from './constants.js';

const VALID_STATUSES = new Set(['active', 'paused', 'declined', 'exited']);

function rowToEngagement(row) {
  if (!row) return null;
  return { ...row };
}

export function getEngagement(id) {
  if (!id) return null;
  return rowToEngagement(getDb().prepare('SELECT * FROM hail_engagements WHERE id = ?').get(id));
}

export function getEngagementByCaseId(case_id) {
  if (!case_id) return null;
  return rowToEngagement(getDb().prepare('SELECT * FROM hail_engagements WHERE process_case_id = ?').get(case_id));
}

// Each engagement owns its own project (1:1), so a project id resolves to one
// engagement. Used to link a quarterly-review case (which runs in the
// engagement's project) back to its engagement.
export function getEngagementByProjectId(project_id) {
  if (!project_id) return null;
  return rowToEngagement(getDb().prepare('SELECT * FROM hail_engagements WHERE project_id = ? ORDER BY created_at ASC LIMIT 1').get(project_id));
}

export function listEngagements({ status = null, limit = 100 } = {}) {
  const db = getDb();
  const rows = status
    ? db.prepare('SELECT * FROM hail_engagements WHERE status = ? ORDER BY created_at DESC LIMIT ?').all(status, limit)
    : db.prepare('SELECT * FROM hail_engagements ORDER BY created_at DESC LIMIT ?').all(limit);
  return rows.map(rowToEngagement);
}

// Create an engagement: provision a project, start the lifecycle case, persist
// the engagement row with the case id. PRD: a named accountable human is
// required — HAIL does not proceed without one.
export function createEngagement(input = {}) {
  const principal_name = String(input.principal_name || '').trim();
  if (!principal_name) throw new Error('createEngagement: principal_name is required');
  const accountable_human = String(input.accountable_human || '').trim();
  if (!accountable_human) {
    const err = new Error('createEngagement: accountable_human is required (HAIL will not proceed without a named accountable counterparty)');
    err.code = 'HAIL_NO_ACCOUNTABLE_HUMAN';
    throw err;
  }

  const db = getDb();
  const now = nowIso();
  const id = newId('hailengg');

  // Provision (or reuse) the engagement's project. Artifacts land here.
  // engagement↔project is 1:1 (a quarterly-review case is linked back to its
  // engagement by project), so refuse to reuse a project another engagement owns.
  let project;
  if (input.project_id && getProject(input.project_id)) {
    if (getEngagementByProjectId(input.project_id)) {
      const err = new Error(`createEngagement: project "${input.project_id}" already owns an engagement`);
      err.code = 'HAIL_PROJECT_IN_USE';
      throw err;
    }
    project = getProject(input.project_id);
  } else {
    project = createProject({
      name: `HAIL — ${principal_name}`,
      description: `HAIL operating-model engagement for ${principal_name}.`,
      color: '#c2452a',
    });
  }

  // Start the lifecycle case. The engine owns case state from here on.
  const caseRow = startCase({
    definition_slug: HAIL_ENGAGEMENT_LIFECYCLE_SLUG,
    project_id: project.id,
    title: `HAIL engagement — ${principal_name}`,
    created_by_user_id: input.created_by_user_id || null,
  });

  db.prepare(`
    INSERT INTO hail_engagements
      (id, principal_name, institution_type, accountable_human, confidentiality_posture,
       project_id, process_case_id, current_stage, status, org_id, created_by_user_id,
       created_at, updated_at)
    VALUES (?, ?, ?, ?, ?, ?, ?, 'diagnostic', 'active', ?, ?, ?, ?)
  `).run(
    id,
    principal_name,
    input.institution_type || null,
    accountable_human,
    input.confidentiality_posture || 'local_first_strict',
    project.id,
    caseRow.id,
    input.org_id || null,
    input.created_by_user_id || null,
    now,
    now
  );

  appendAudit({
    type: 'hail.engagement.create',
    summary: `HAIL engagement created for ${principal_name}`,
    status: 'ok',
    meta: { engagement_id: id, project_id: project.id, process_case_id: caseRow.id },
  });

  return getEngagement(id);
}

// Advance the engagement's lifecycle marker. This is a HAIL-local denormalised
// pointer for the UI/dashboard; the engine's case remains the source of truth
// for the actual gate position.
export function updateEngagementStage(id, stage) {
  if (!LIFECYCLE_STAGE_SET.has(stage)) {
    throw new Error(`updateEngagementStage: unknown stage "${stage}"`);
  }
  const db = getDb();
  const existing = getEngagement(id);
  if (!existing) throw new Error(`updateEngagementStage: engagement "${id}" not found`);
  db.prepare('UPDATE hail_engagements SET current_stage = ?, updated_at = ? WHERE id = ?')
    .run(stage, nowIso(), id);
  appendAudit({
    type: 'hail.engagement.stage',
    summary: `HAIL engagement ${id} → ${stage}`,
    status: 'ok',
    meta: { engagement_id: id, stage },
  });
  return getEngagement(id);
}

export function updateEngagementStatus(id, status) {
  if (!VALID_STATUSES.has(status)) {
    throw new Error(`updateEngagementStatus: unknown status "${status}"`);
  }
  const db = getDb();
  const existing = getEngagement(id);
  if (!existing) throw new Error(`updateEngagementStatus: engagement "${id}" not found`);
  db.prepare('UPDATE hail_engagements SET status = ?, updated_at = ? WHERE id = ?')
    .run(status, nowIso(), id);
  return getEngagement(id);
}
