export const ASK_USER_TAG = "ask_user";
export const ASK_USER_OPEN = `<${ASK_USER_TAG}>`;
export const ASK_USER_CLOSE = `</${ASK_USER_TAG}>`;
export const ASK_USER_RESPONSE_TAG = "ask_user_response";
export const ASK_USER_RESPONSE_OPEN = `<${ASK_USER_RESPONSE_TAG}>`;
export const ASK_USER_RESPONSE_CLOSE = `</${ASK_USER_RESPONSE_TAG}>`;

export type IAskUserQuestionType = "text" | "textarea" | "boolean" | "singleSelect" | "multiSelect";
export type IAskUserAnswerValue = string | boolean | string[];

export interface IAskUserOption {
	label: string;
	value: string;
}

export interface IAskUserQuestion {
	id: string;
	label: string;
	description?: string;
	type: IAskUserQuestionType;
	required?: boolean;
	options?: IAskUserOption[];
	placeholder?: string;
	defaultValue?: IAskUserAnswerValue;
}

export interface IAskUserRequest {
	title: string;
	message: string;
	questions: IAskUserQuestion[];
}

export interface IAskUserResponse {
	action: "submit" | "cancel" | "decline";
	answers: Record<string, IAskUserAnswerValue>;
}

function hashString(value: string): string {
  let hash = 0;
  for (let index = 0; index < value.length; index += 1) {
    hash = ((hash << 5) - hash) + value.charCodeAt(index);
    hash |= 0;
  }
  return Math.abs(hash).toString(36);
}

function isRecord(value: unknown): value is Record<string, unknown> {
  return typeof value === "object" && value !== null && !Array.isArray(value);
}

function isNonEmptyString(value: unknown): value is string {
  return typeof value === "string" && value.trim().length > 0;
}

function normalizeTrimmedString(value: unknown): string | undefined {
  return typeof value === "string" && value.trim().length > 0 ? value.trim() : undefined;
}

function normalizeQuestionType(value: unknown): IAskUserQuestionType | undefined {
  return value === "text" ||
		value === "textarea" ||
		value === "boolean" ||
		value === "singleSelect" ||
		value === "multiSelect"
    ? value
    : undefined;
}

function normalizeDefaultValue(value: unknown): IAskUserAnswerValue | undefined {
  if (typeof value === "string" || typeof value === "boolean") {
    return value;
  }
  if (Array.isArray(value) && value.every((item) => typeof item === "string")) {
    return [...value];
  }
  return undefined;
}

function normalizeOptions(value: unknown): IAskUserOption[] | undefined {
  if (!Array.isArray(value)) {
    return undefined;
  }

  const options: IAskUserOption[] = [];
  for (const item of value) {
    if (!isRecord(item) || !isNonEmptyString(item.label) || !isNonEmptyString(item.value)) {
      return undefined;
    }
    options.push({
      label: item.label.trim(),
      value: item.value.trim(),
    });
  }

  return options;
}

export function normalizeAskUserQuestion(value: unknown): IAskUserQuestion | undefined {
  if (!isRecord(value)) {
    return undefined;
  }

  const id = normalizeTrimmedString(value.id);
  const label = normalizeTrimmedString(value.label);
  const type = normalizeQuestionType(value.type);
  if (!id || !label || !type) {
    return undefined;
  }

  const question: IAskUserQuestion = {
    id,
    label,
    type,
  };

  const description = normalizeTrimmedString(value.description);
  if (description) {
    question.description = description;
  }

  if (typeof value.required === "boolean") {
    question.required = value.required;
  }

  const options = normalizeOptions(value.options);
  if (type === "singleSelect" || type === "multiSelect") {
    if (!options || options.length === 0) {
      return undefined;
    }
    question.options = options;
  }

  const placeholder = normalizeTrimmedString(value.placeholder);
  if (placeholder) {
    question.placeholder = placeholder;
  }

  const defaultValue = normalizeDefaultValue(value.defaultValue);
  if (defaultValue !== undefined) {
    question.defaultValue = defaultValue;
  }

  return question;
}

export function normalizeAskUserRequest(value: unknown): IAskUserRequest | undefined {
  if (!isRecord(value)) {
    return undefined;
  }

  const title = normalizeTrimmedString(value.title);
  const message = normalizeTrimmedString(value.message);
  if (!title || !message || !Array.isArray(value.questions) || value.questions.length === 0) {
    return undefined;
  }

  const questions: IAskUserQuestion[] = [];
  for (const question of value.questions) {
    const normalized = normalizeAskUserQuestion(question);
    if (!normalized) {
      return undefined;
    }
    questions.push(normalized);
  }

  return {
    title,
    message,
    questions,
  };
}

export function normalizeAskUserResponse(value: unknown): IAskUserResponse | undefined {
  if (!isRecord(value)) {
    return undefined;
  }

  const action = value.action;
  if (action !== "submit" && action !== "cancel" && action !== "decline") {
    return undefined;
  }

  const answersRecord = isRecord(value.answers) ? value.answers : {};
  const answers: Record<string, IAskUserAnswerValue> = {};
  for (const [key, answerValue] of Object.entries(answersRecord)) {
    const normalized = normalizeDefaultValue(answerValue);
    if (normalized !== undefined) {
      answers[key] = normalized;
    }
  }

  return {
    action,
    answers,
  };
}

export function buildAskUserProtocolInstructions(): string {
  return [
    "When clarification is required, do not ask free-form prose questions.",
    `Instead, output only a single ${ASK_USER_OPEN}...${ASK_USER_CLOSE} block and nothing else in the assistant message.`,
    "Inside that block, emit strict JSON with this shape:",
    '{"title":"...","message":"...","questions":[{"id":"...","label":"...","description":"...","type":"text|textarea|boolean|singleSelect|multiSelect","required":true,"options":[{"label":"...","value":"..."}],"placeholder":"...","defaultValue":"..."}]}',
    "Use batch questions when possible, keep ids stable, and limit the batch to the minimum needed to continue planning.",
  ].join("\n");
}

export function serializeAskUserRequest(request: IAskUserRequest): string {
  return `${ASK_USER_OPEN}\n${JSON.stringify(request, null, 2)}\n${ASK_USER_CLOSE}`;
}

export function createAskUserToolCallId(request: IAskUserRequest): string {
  return `ask_user_${hashString(JSON.stringify(request))}`;
}

export function parseAskUserRequestFromText(text: string): IAskUserRequest | undefined {
  const trimmed = text.trim();
  if (!trimmed.startsWith(ASK_USER_OPEN) || !trimmed.endsWith(ASK_USER_CLOSE)) {
    return undefined;
  }

  const body = trimmed.slice(ASK_USER_OPEN.length, trimmed.length - ASK_USER_CLOSE.length).trim();
  if (!body) {
    return undefined;
  }

  try {
    return normalizeAskUserRequest(JSON.parse(body));
  } catch {
    return undefined;
  }
}

export function formatAskUserResponsePrompt(response: IAskUserResponse): string {
  return [
    "The user submitted structured clarification answers.",
    "",
    `${ASK_USER_RESPONSE_OPEN}`,
    JSON.stringify(response, null, 2),
    `${ASK_USER_RESPONSE_CLOSE}`,
    "",
    "Continue the planning workflow using these answers.",
  ].join("\n");
}

export function parseAskUserResponseFromToolOutput(output: unknown): IAskUserResponse | undefined {
  return normalizeAskUserResponse(output);
}
