import {
  unsafeOpaqueId,
  type ConnectionId,
  type CredentialReferenceId,
  type FactoryRunId,
  type MutationContext,
  type OperationId,
  type TaskId,
} from "@awp/contracts";
import type { DurableWorkflowProvider, FactoryProvider } from "./ports/providers.js";
import type { UnitOfWork } from "./ports/repositories.js";
import type { ExecutionDispatcher, ExecutionSelection } from "./execution.js";

export const I1_DURABLE_DISPATCH_WORKFLOW_KIND = "i1-task-dispatch";

export interface DurableExecutionWorkflowInput {
  readonly factoryRunId: string;
  readonly taskId: string;
  readonly context: MutationContext;
  readonly selection?: ExecutionSelection;
}

export interface ProviderExecutionWorkerConfiguration {
  readonly factoryConnectionId: ConnectionId;
  readonly factoryCredentialReferenceId: CredentialReferenceId;
}

export class ProviderExecutionWorker {
  constructor(
    private readonly uow: UnitOfWork,
    private readonly factoryProvider: FactoryProvider,
    private readonly downstream: ExecutionDispatcher,
    private readonly configuration: ProviderExecutionWorkerConfiguration,
  ) {}

  async execute(input: DurableExecutionWorkflowInput): Promise<Readonly<Record<string, unknown>>> {
    const factoryRunId = unsafeOpaqueId<FactoryRunId>(input.factoryRunId);
    const taskId = unsafeOpaqueId<TaskId>(input.taskId);
    const state = await this.uow.transaction(async (tx) => {
      const factoryRun = await tx.factoryRuns.getById(factoryRunId);
      const task = await tx.tasks.getById(taskId);
      if (!factoryRun || !task) throw new Error("Durable execution hierarchy does not exist");
      if (
        task.projectId !== factoryRun.projectId ||
        task.planRevisionId !== factoryRun.planRevisionId
      ) {
        throw new Error("Durable execution input does not match the FactoryRun work graph");
      }
      return { factoryRun, task };
    });

    const factoryOperationId = unsafeOpaqueId<OperationId>(`factory:${state.factoryRun.id}`);
    const factoryResult = await this.factoryProvider.start(
      {
        operationId: factoryOperationId,
        idempotencyKey: `factory:${state.factoryRun.id}`,
        correlationId: input.context.correlationId,
        authority: input.context.authority,
        connectionId: this.configuration.factoryConnectionId,
        credentialReferenceId: this.configuration.factoryCredentialReferenceId,
      },
      state.factoryRun.id,
      state.factoryRun.planRevisionId,
    );
    if (["failed", "cancelled"].includes(factoryResult.value.state)) {
      throw new Error(`Factory provider refused execution: ${factoryResult.value.state}`);
    }

    await this.downstream.dispatch({
      factoryRun: state.factoryRun,
      task: state.task,
      context: input.context,
      ...(input.selection === undefined ? {} : { selection: input.selection }),
    });

    return {
      factoryRunId: state.factoryRun.id,
      taskId: state.task.id,
      factoryState: factoryResult.value.state,
      factoryReferences: factoryResult.references,
    };
  }
}

export interface DurableExecutionDispatcherConfiguration {
  readonly workflowConnectionId: ConnectionId;
  readonly workflowCredentialReferenceId: CredentialReferenceId;
  readonly workflowKind?: string;
}

export class DurableExecutionDispatcher implements ExecutionDispatcher {
  constructor(
    private readonly workflowProvider: DurableWorkflowProvider,
    private readonly configuration: DurableExecutionDispatcherConfiguration,
  ) {}

  async dispatch(request: Parameters<ExecutionDispatcher["dispatch"]>[0]): Promise<void> {
    const operationId = unsafeOpaqueId<OperationId>(
      `dispatch:${request.factoryRun.id}:${request.task.id}`,
    );
    await this.workflowProvider.start(
      {
        operationId,
        idempotencyKey: `dispatch:${request.factoryRun.id}:${request.task.id}`,
        correlationId: request.context.correlationId,
        authority: request.context.authority,
        connectionId: this.configuration.workflowConnectionId,
        credentialReferenceId: this.configuration.workflowCredentialReferenceId,
      },
      operationId,
      this.configuration.workflowKind ?? I1_DURABLE_DISPATCH_WORKFLOW_KIND,
      {
        factoryRunId: request.factoryRun.id,
        taskId: request.task.id,
        context: request.context,
        ...(request.selection === undefined ? {} : { selection: request.selection }),
      } satisfies DurableExecutionWorkflowInput,
    );
  }
}
