'use node';

/**
 * Start a workflow from a file-based JSON definition.
 *
 * This is the action→mutation two-phase pattern:
 * 1. Action reads the workflow JSON file from disk
 * 2. Action calls an internal mutation to create the execution record and start the engine
 *
 * The action context is required because file reads are only possible in actions.
 */

import { v } from 'convex/values';

import { internal } from '../../../_generated/api';
import type { Id } from '../../../_generated/dataModel';
import { internalAction } from '../../../_generated/server';
import { createDebugLog } from '../../../lib/debug_log';
import { jsonValueValidator } from '../../../lib/validators/json';
import { preflightTestRunInput } from '../validation/preflight_test_run_input';

const debugLog = createDebugLog('DEBUG_WORKFLOW', '[Workflow]');

export const startWorkflowFromFile = internalAction({
  args: {
    organizationId: v.string(),
    orgSlug: v.string(),
    workflowSlug: v.string(),
    input: v.optional(jsonValueValidator),
    triggeredBy: v.string(),
    triggerData: v.optional(jsonValueValidator),
    userId: v.optional(v.string()),
    debugMode: v.optional(v.boolean()),
  },
  returns: v.union(v.id('wfExecutions'), v.null()),
  handler: async (ctx, args): Promise<Id<'wfExecutions'> | null> => {
    debugLog('startWorkflowFromFile Reading workflow file', {
      orgSlug: args.orgSlug,
      workflowSlug: args.workflowSlug,
    });

    // Step 1: Read workflow JSON from filesystem
    const result = await ctx.runAction(
      internal.workflows.file_actions.readWorkflowForExecution,
      {
        orgSlug: args.orgSlug,
        workflowSlug: args.workflowSlug,
      },
    );

    if (!result.ok) {
      console.error(
        '[startWorkflowFromFile] Failed to read workflow file',
        result,
      );
      return null;
    }

    const config = result.config;

    // Step 2: Check the workflow is installed in this deployment for this org.
    // The DB row in `wfInstallations` is the single source of truth for
    // "deployed and runnable" — the JSON file no longer carries that flag.
    const installed = await ctx.runQuery(
      internal.workflows.installations.getInstallationInternal,
      {
        organizationId: args.organizationId,
        workflowSlug: args.workflowSlug,
      },
    );

    if (!installed) {
      debugLog('startWorkflowFromFile Workflow is not installed, skipping', {
        workflowSlug: args.workflowSlug,
      });
      return null;
    }

    // Server-side input preflight for test runs only: reject before an
    // execution record exists so the tester gets the field-specific reasons
    // immediately (client-side gating is the first line, this is the backstop).
    // Production trigger paths (webhook/schedule/event) are deliberately NOT
    // enforced — live payloads may predate the declared schema.
    if (args.triggeredBy === 'test') {
      const preflightError = preflightTestRunInput(config.steps, args.input);
      if (preflightError) {
        throw new Error(preflightError);
      }
    }

    debugLog('startWorkflowFromFile Starting execution', {
      workflowName: config.name,
      workflowSlug: args.workflowSlug,
    });

    // Step 3: Call mutation to create execution and start the engine
    const executionId: Id<'wfExecutions'> = await ctx.runMutation(
      internal.wf_executions.internal_mutations.startWorkflowFromFileConfig,
      {
        organizationId: args.organizationId,
        workflowSlug: args.workflowSlug,
        workflowConfig: config,
        input: args.input,
        triggeredBy: args.triggeredBy,
        triggerData: args.triggerData,
        userId: args.userId,
        debugMode: args.debugMode,
      },
    );

    debugLog('startWorkflowFromFile Execution started', {
      executionId,
      workflowSlug: args.workflowSlug,
    });

    return executionId;
  },
});
