import type { PilotDeckHookInput } from "../protocol/input.js";
import type { PilotDeckHookCommand } from "../protocol/settings.js";
import { parseHookOutput } from "./parseHookOutput.js";
import type { CommandHookExecutionResult } from "./CommandHookExecutor.js";

export type HttpHookFetch = typeof fetch;

export class HttpHookExecutor {
  constructor(private readonly fetchImpl: HttpHookFetch = fetch) {}

  async execute(options: {
    hook: Extract<PilotDeckHookCommand, { type: "http" }>;
    hookInput: PilotDeckHookInput;
    env?: NodeJS.ProcessEnv;
    signal?: AbortSignal;
  }): Promise<CommandHookExecutionResult> {
    try {
      const response = await this.fetchImpl(options.hook.url, {
        method: "POST",
        headers: {
          "content-type": "application/json",
          ...resolveHeaders(options.hook.headers, options.hook.allowedEnvVars, options.env),
        },
        body: JSON.stringify(options.hookInput),
        signal: options.signal,
      });
      const stdout = await response.text();
      return {
        stdout,
        stderr: response.ok ? "" : `HTTP hook returned ${response.status}.`,
        exitCode: response.ok ? 0 : 1,
        outcome: response.ok ? "success" : "non_blocking_error",
        output: parseHookOutput(stdout),
      };
    } catch (error) {
      return {
        stdout: "",
        stderr: error instanceof Error ? error.message : String(error),
        outcome: "non_blocking_error",
        output: { type: "sync" },
      };
    }
  }
}

function resolveHeaders(
  headers: Record<string, string> | undefined,
  allowedEnvVars: string[] | undefined,
  env: NodeJS.ProcessEnv = process.env,
): Record<string, string> {
  const allowed = new Set(allowedEnvVars ?? []);
  const output: Record<string, string> = {};
  for (const [key, value] of Object.entries(headers ?? {})) {
    output[key] = value.replace(/\$(\w+)|\$\{(\w+)\}/gu, (_match, bare: string | undefined, braced: string | undefined) => {
      const name = bare ?? braced ?? "";
      return allowed.has(name) ? env[name] ?? "" : "";
    });
  }
  return output;
}