import { createHash } from "node:crypto";
import type {
  ProviderGenerateParams,
  ProviderGenerateResult,
  ToolCall,
} from "../types";

type OpenAIResponse = {
  id: string;
  output_text?: string;
  output?: Array<{
    type: string;
    name?: string;
    arguments?: string;
    call_id?: string;
    content?: Array<{ type: string; text?: string }>;
  }>;
};

function hashIdentifier(value: string) {
  return createHash("sha256").update(value).digest("hex");
}

async function callOpenAI(body: Record<string, unknown>, apiKey: string) {
  const response = await fetch("https://api.openai.com/v1/responses", {
    method: "POST",
    headers: {
      "Content-Type": "application/json",
      Authorization: `Bearer ${apiKey}`,
    },
    body: JSON.stringify(body),
  });

  if (!response.ok) {
    const text = await response.text();
    throw new Error(`OpenAI error: ${response.status} ${text}`);
  }

  return (await response.json()) as OpenAIResponse;
}

function extractText(response: OpenAIResponse) {
  if (response.output_text?.trim()) return response.output_text.trim();

  return (
    (response.output || [])
      .flatMap((item) => item.content || [])
      .filter((item) => item.type === "output_text" && item.text)
      .map((item) => item.text)
      .join("\n")
      .trim() || ""
  );
}

export async function generateWithOpenAI(
  params: ProviderGenerateParams
): Promise<ProviderGenerateResult> {
  const response = await callOpenAI(
    {
      model: params.model,
      input: params.toolResults
        ? params.toolResults.map((item) => ({
            type: "function_call_output",
            call_id: item.callId,
            output: JSON.stringify(item.output),
          }))
        : params.messages,
      previous_response_id: params.providerState,
      instructions: params.systemPrompt,
      tools: params.tools.map((tool) => ({
        type: "function",
        name: tool.name,
        description: tool.description,
        parameters: tool.input_schema,
      })),
      parallel_tool_calls: true,
      reasoning: { effort: "medium" },
      max_output_tokens: 1200,
      text: { format: { type: "text" } },
      safety_identifier: hashIdentifier(
        params.messages.map((message) => message.content).join("|").slice(0, 500)
      ),
    },
    params.apiKey
  );

  const toolCalls: ToolCall[] = (response.output || [])
    .filter((item) => item.type === "function_call" && item.name && item.call_id)
    .map((item) => ({
      id: item.call_id!,
      name: item.name!,
      arguments: item.arguments?.trim() ? JSON.parse(item.arguments) : {},
    }));

  return {
    text: extractText(response),
    toolCalls,
    providerState: response.id,
  };
}
