// app/lib/doctor/generated/checks-from-shape.mjs
// AUTO-GENERATED by livingcode. Do not edit by hand.
// Regenerate with `npm run livingcode:refresh`.

import { getSql } from '../../db.js';
import { getSetupStatus } from '../../setupStatus.mjs';

const TABLES = [
  "action_embeddings",
  "action_records",
  "activity_logs",
  "agent_connections",
  "agent_identities",
  "agent_messages",
  "agent_pairings",
  "agent_presence",
  "agent_schedules",
  "agent_sessions",
  "api_keys",
  "assumptions",
  "calendar_events",
  "code_optimal_file_manifests",
  "code_projects",
  "code_session_alerts",
  "code_session_handoffs",
  "code_session_memos",
  "code_session_messages",
  "code_session_signals",
  "code_session_tool_uses",
  "code_sessions",
  "compliance_snapshots",
  "contacts",
  "content",
  "context_entries",
  "context_points",
  "daily_totals",
  "decisions",
  "drift_alerts",
  "drift_baselines",
  "drift_snapshots",
  "entities",
  "eval_runs",
  "eval_scores",
  "executions",
  "feedback",
  "goals",
  "governed_secrets",
  "guard_decisions",
  "guard_policies",
  "guardrails_test_runs",
  "health_snapshots",
  "ideas",
  "interactions",
  "jwt_replay_log",
  "learning_curves",
  "learning_episodes",
  "learning_recommendation_events",
  "learning_recommendations",
  "learning_velocity",
  "message_threads",
  "milestones",
  "notification_preferences",
  "open_loops",
  "organizations",
  "profile_scores",
  "prompt_runs",
  "prompt_templates",
  "prompt_versions",
  "risk_templates",
  "routing_agent_metrics",
  "routing_agents",
  "routing_decisions",
  "routing_tasks",
  "scheduled_jobs",
  "scoring_dimensions",
  "scoring_profiles",
  "session_events",
  "shared_docs",
  "skill_scan_results",
  "snippets",
  "token_budgets",
  "token_snapshots",
  "topics",
  "usage_meters",
  "user_approaches",
  "user_moods",
  "user_observations",
  "user_preferences",
  "users",
  "waitlist",
  "webhook_deliveries",
  "webhooks",
  "workflows",
];
const REQUIRED_ENV_VARS = [
  "DASHCLAW_API_KEY",
  "DATABASE_URL",
  "ENCRYPTION_KEY",
  "NEXTAUTH_SECRET",
];
const FIXABLE_ENV_VARS = {
  "NEXTAUTH_SECRET": { action: "generate_secret", description: "Generate a random NEXTAUTH_SECRET" },
  "ENCRYPTION_KEY": { action: "generate_encryption_key", description: "Generate a 32-character ENCRYPTION_KEY" },
  "DASHCLAW_API_KEY": { action: "generate_api_key", description: "Generate a new DashClaw API key" },
};

/**
 * Shape-derived doctor checks. Signature matches the other check runners in
 * `app/lib/doctor/checks/*.mjs` so it can be plugged into the engine's
 * CHECK_RUNNERS map under the `shape` category.
 *
 * @param { { env?: object, host?: string } } options
 */
export async function runShapeChecks({ env = process.env } = {}) {
  const checks = [];

  // Table presence checks — only if the DB is reachable. Table names come
  // from the committed generated code (schema.js → shape.json), so they are
  // safe to interpolate into a SQL literal (Neon driver has no sql.identifier).
  let sql = null;
  try {
    const dbStatus = await getSetupStatus(env);
    if (dbStatus.configured) sql = getSql();
  } catch {
    // db unreachable — fall through, surfaced elsewhere
  }

  if (sql && TABLES.length > 0) {
    // Single round trip: to_regclass each table name inside unnest() so this
    // stays O(1) DB calls regardless of schema size. The tagged template form
    // binds TABLES as a text[] parameter — the Neon serverless driver's
    // `sql.unsafe(rawString)` does NOT execute, it just builds a query object,
    // so the tagged form is required for real results.
    let present = null;
    try {
      const rows = await sql`
        SELECT t AS name, to_regclass('public.' || t) AS oid
        FROM unnest(${TABLES}::text[]) AS t
      `;
      present = new Set(rows.filter((r) => r.oid != null).map((r) => r.name));
    } catch {
      // Query-level failure — skip table checks; database check module surfaces it.
    }

    if (present) {
      for (const table of TABLES) {
        const exists = present.has(table);
        checks.push({
          id: `shape_table_${table}`,
          category: 'shape',
          status: exists ? 'pass' : 'fail',
          title: `Table: ${table}`,
          message: exists
            ? 'Present'
            : 'Missing from database — run migrations to create it',
          fix: exists
            ? null
            : {
                type: 'auto',
                description: 'Run database migrations',
                action: 'migrate',
              },
        });
      }
    }
  }

  // Required env-var presence checks — fixable vars wire to existing fix
  // actions; everything else directs the operator to set it manually.
  for (const name of REQUIRED_ENV_VARS) {
    const present = Boolean(env?.[name]);
    const fixInfo = FIXABLE_ENV_VARS[name];
    checks.push({
      id: `shape_env_${name}`,
      category: 'shape',
      status: present ? 'pass' : 'fail',
      title: `Env: ${name}`,
      message: present
        ? 'Set'
        : fixInfo
          ? 'Not set — auto-fix available'
          : `Not set — set ${name} in the environment (no auto-fix)`,
      fix:
        present || !fixInfo
          ? null
          : {
              type: 'auto',
              description: fixInfo.description,
              action: fixInfo.action,
            },
    });
  }

  return checks;
}
