import { ApplicationFailure, Context } from "@temporalio/activity";

import type { WorkbenchActivityConfig, WorkbenchApplication, WorkbenchEventPublisher, WorkbenchTurnInput } from "./contracts.ts";

export function createWorkbenchActivities(application: WorkbenchApplication, options: {
  activity: WorkbenchActivityConfig;
  eventPublisher?: WorkbenchEventPublisher | null;
  attempt?: () => number;
}) {
  return {
    async dispatchWorkbenchTurn(input: WorkbenchTurnInput) {
      try {
        return await application.dispatchTurn(input);
      } catch (error) {
        const attempt = positiveInteger(options.attempt?.() ?? currentActivityAttempt(), 1);
        const retryable = (error as any)?.retryable !== false;
        const exhausted = !retryable || attempt >= options.activity.retryMaximumAttempts;
        await publishDispatchFailure(options.eventPublisher, input, error, {
          attempt,
          maxAttempts: options.activity.retryMaximumAttempts,
          retryable,
          exhausted,
          backoffMs: exhausted ? null : retryDelayMs(options.activity, attempt)
        });
        throw ApplicationFailure.create({
          message: errorMessage(error),
          type: errorCode(error),
          nonRetryable: !retryable,
          details: [{
            code: errorCode(error),
            layer: (error as any)?.layer ?? null,
            status: (error as any)?.status ?? null,
            retryable,
            attempt,
            maxAttempts: options.activity.retryMaximumAttempts,
            valuesPrinted: false
          }]
        });
      }
    },
    async cancelWorkbenchTurn(input: { actor: WorkbenchTurnInput["actor"]; traceId: string; params: Record<string, unknown> }) {
      return application.cancelTurn(input);
    }
  };
}

async function publishDispatchFailure(publisher: WorkbenchEventPublisher | null | undefined, input: WorkbenchTurnInput, error: unknown, state: { attempt: number; maxAttempts: number; retryable: boolean; exhausted: boolean; backoffMs: number | null }) {
  if (!publisher) return;
  const code = errorCode(error);
  const message = userErrorMessage(error);
  const createdAt = new Date().toISOString();
  await publisher.publish({
    traceId: input.traceId,
    sessionId: input.sessionId,
    event: {
      sourceEventId: `workbench:${input.traceId}:dispatch-failure:${state.attempt}`,
      type: state.exhausted ? "error" : "backend_status",
      phase: state.exhausted ? "dispatch-failed" : "dispatch-retry-scheduled",
      failureDomain: (error as any)?.layer === "billing" ? "billing" : "infrastructure",
      component: "hwlab-cloud-api",
      code,
      failureKind: code,
      summary: message,
      message,
      retryable: state.retryable,
      willRetry: !state.exhausted,
      retryPhase: state.exhausted ? "retryExhausted" : "retryScheduled",
      attempt: state.attempt,
      maxAttempts: state.maxAttempts,
      backoffMs: state.backoffMs,
      createdAt,
      terminal: false,
      valuesPrinted: false
    }
  });
  if (!state.exhausted) return;
  await publisher.publish({
    traceId: input.traceId,
    sessionId: input.sessionId,
    event: {
      sourceEventId: `workbench:${input.traceId}:terminal-failed`,
      type: "terminal_status",
      terminalStatus: "failed",
      status: "failed",
      failureDomain: (error as any)?.layer === "billing" ? "billing" : "infrastructure",
      code,
      failureKind: code,
      errorCode: code,
      message,
      retryable: state.retryable,
      attempt: state.attempt,
      maxAttempts: state.maxAttempts,
      createdAt,
      terminal: true,
      valuesPrinted: false
    }
  });
}

function currentActivityAttempt() {
  try { return Context.current().info.attempt; } catch { return 1; }
}
function retryDelayMs(activity: WorkbenchActivityConfig, attempt: number) { return Math.min(activity.retryMaximumIntervalMs, activity.retryInitialIntervalMs * (2 ** Math.max(0, attempt - 1))); }
function positiveInteger(value: unknown, fallback: number) { const parsed = Number(value); return Number.isSafeInteger(parsed) && parsed > 0 ? parsed : fallback; }
function errorCode(error: unknown) { return String((error as any)?.code ?? "workbench_activity_failed").trim() || "workbench_activity_failed"; }
function errorMessage(error: unknown) { return String((error as any)?.message ?? "Workbench activity failed").trim() || "Workbench activity failed"; }
function userErrorMessage(error: unknown) { return String((error as any)?.userMessage ?? (error as any)?.message ?? "Workbench activity failed").trim() || "Workbench activity failed"; }
