import { Injectable } from "@nestjs/common";
import { extractErrorMessage } from "@bff/core/utils/error.util.js";
import type {
  WorkflowStep,
  WorkflowStepConfig,
  WorkflowStepContext,
  WorkflowStepStatus,
} from "./workflow-step-tracker.types.js";

/**
 * Workflow Step Tracker Service
 *
 * Provides reusable step tracking for any multi-step workflow.
 * Creates a context that tracks step status, timing, and errors.
 *
 * Usage:
 * ```typescript
 * const tracker = workflowStepTracker.createContext([
 *   { id: 'validation', label: 'Validate request' },
 *   { id: 'process', label: 'Process data' },
 *   { id: 'cleanup', label: 'Cleanup', condition: needsCleanup },
 * ]);
 *
 * // Option 1: Wrap executors for automatic tracking
 * const wrappedExecutor = tracker.wrap('validation', async () => {
 *   return await validateRequest();
 * });
 *
 * // Option 2: Manual status updates
 * tracker.updateStatus('validation', 'in_progress');
 * try {
 *   await validateRequest();
 *   tracker.updateStatus('validation', 'completed');
 * } catch (error) {
 *   tracker.updateStatus('validation', 'failed', error.message);
 *   throw error;
 * }
 * ```
 */
@Injectable()
export class WorkflowStepTrackerService {
  /**
   * Create a new workflow step context from step configurations
   * Steps with `condition: false` are automatically excluded
   */
  createContext(configs: WorkflowStepConfig[]): WorkflowStepContext {
    const steps: WorkflowStep[] = configs
      .filter(config => config.condition !== false)
      .map(config => ({
        id: config.id,
        label: config.label,
        status: "pending" as const,
      }));

    const context: WorkflowStepContext = {
      steps,

      updateStatus(stepId: string, status: WorkflowStepStatus, error?: string): void {
        const step = steps.find(s => s.id === stepId);
        if (!step) return;

        const timestamp = new Date();

        if (status === "in_progress") {
          step.status = "in_progress";
          step.startedAt = timestamp;
          delete step.error;
          return;
        }

        step.status = status;
        step.completedAt = timestamp;

        if (status === "failed" && error !== undefined) {
          step.error = error;
        } else {
          delete step.error;
        }
      },

      wrap<T>(stepId: string, executor: () => Promise<T>): () => Promise<T> {
        return async () => {
          context.updateStatus(stepId, "in_progress");
          try {
            const result = await executor();
            context.updateStatus(stepId, "completed");
            return result;
          } catch (error) {
            context.updateStatus(stepId, "failed", extractErrorMessage(error));
            throw error;
          }
        };
      },

      getFailedStep(): WorkflowStep | undefined {
        return steps.find(s => s.status === "failed");
      },

      isComplete(): boolean {
        return steps.every(s => s.status === "completed" || s.status === "skipped");
      },

      skip(stepId: string): void {
        context.updateStatus(stepId, "skipped");
      },
    };

    return context;
  }

  /**
   * Create step configurations for order fulfillment workflow
   * This is a convenience method for the common fulfillment use case
   */
  createFulfillmentStepConfigs(orderType?: string): WorkflowStepConfig[] {
    return [
      { id: "validation", label: "Validate fulfillment request" },
      { id: "sf_status_update", label: "Update Salesforce order status" },
      { id: "order_details", label: "Load order details" },
      { id: "mapping", label: "Map order items to WHMCS format" },
      { id: "whmcs_create", label: "Create order in WHMCS" },
      { id: "whmcs_accept", label: "Accept/provision WHMCS order" },
      { id: "sim_fulfillment", label: "SIM-specific fulfillment", condition: orderType === "SIM" },
      { id: "sf_success_update", label: "Update Salesforce with success" },
      { id: "opportunity_update", label: "Update linked Opportunity" },
    ];
  }
}
