import { stableDigest } from "@best-agent/foundation";

export type RuntimeToolName = string & { readonly __brand: "RuntimeToolName" };
export type ProviderToolName = string & { readonly __brand: "ProviderToolName" };

export type ProviderToolNameBinding = Readonly<{
  providerName: ProviderToolName;
  runtimeName: RuntimeToolName;
}>;

export interface ProviderToolNameCodec {
  bindings(): readonly ProviderToolNameBinding[];
  toProvider(runtimeName: RuntimeToolName): ProviderToolName;
  toRuntime(providerName: ProviderToolName): RuntimeToolName | undefined;
}

export const PLAN_UPDATE_TOOL_NAME = "__best_agent_plan_update";
export const PHASE_RESULT_TOOL_NAME = "__best_agent_phase_result";
export const DELEGATE_TASKS_TOOL_NAME = "delegate_tasks";

export const PROVIDER_TOOL_NAME_RE = /^[A-Za-z0-9_-]{1,64}$/u;
const RESERVED_PROVIDER_TOOL_NAMES = new Set([PLAN_UPDATE_TOOL_NAME, PHASE_RESULT_TOOL_NAME]);

export function isProviderToolName(value: string): value is ProviderToolName {
  return PROVIDER_TOOL_NAME_RE.test(value);
}

export function toRuntimeToolName(value: string): RuntimeToolName {
  return value as RuntimeToolName;
}

export function isReservedProviderToolName(value: string): boolean {
  return RESERVED_PROVIDER_TOOL_NAMES.has(value);
}

export function isPersistableProviderToolName(value: string): value is ProviderToolName {
  return isProviderToolName(value) && !isReservedProviderToolName(value);
}

export function toProviderToolName(runtimeName: string): ProviderToolName {
  if (isProviderToolName(runtimeName)) {
    return runtimeName as ProviderToolName;
  }

  const slug = runtimeName
    .replace(/[^A-Za-z0-9_-]+/gu, "_")
    .replace(/^_+|_+$/gu, "")
    .slice(0, 48);
  return buildHashedProviderToolName(slug || "tool", runtimeName);
}

function buildHashedProviderToolName(slug: string, hashInput: string): ProviderToolName {
  const hash = stableDigest(hashInput).slice(0, 8);
  const prefix = slug.slice(0, 64 - hash.length - 1) || "tool";
  return `${prefix}_${hash}` as ProviderToolName;
}

export function toUniqueProviderToolName(runtimeName: string, used: Set<string>): ProviderToolName {
  let providerName = toProviderToolName(runtimeName);
  let collisionIndex = 1;
  while (used.has(providerName)) {
    providerName = buildHashedProviderToolName(providerName, `${runtimeName}:${collisionIndex}`);
    collisionIndex += 1;
  }
  return providerName;
}

export function prepareProviderToolNameBindings(
  tools: readonly { name: string }[],
): ProviderToolNameBinding[] {
  return [...createProviderToolNameCodec(tools).bindings()];
}

export function createProviderToolNameCodec(
  tools: readonly { name: string }[],
): ProviderToolNameCodec {
  const usedProviderNames = new Set<string>(RESERVED_PROVIDER_TOOL_NAMES);
  const bindings = tools.map((tool) => {
    const runtimeName = toRuntimeToolName(tool.name);
    const providerName = toUniqueProviderToolName(runtimeName, usedProviderNames);
    usedProviderNames.add(providerName);
    return {
      providerName,
      runtimeName,
    };
  });
  return {
    bindings: () => bindings,
    toProvider(runtimeName) {
      return (
        bindings.find((binding) => binding.runtimeName === runtimeName)?.providerName ??
        toProviderToolName(runtimeName)
      );
    },
    toRuntime(providerName) {
      return bindings.find((binding) => binding.providerName === providerName)?.runtimeName;
    },
  };
}

export function toProviderHistoryToolName(
  toolName: string,
  bindings: readonly ProviderToolNameBinding[],
): string {
  const matched = bindings.find(
    (binding) => binding.runtimeName === toolName || binding.providerName === toolName,
  );
  if (matched) {
    return matched.providerName;
  }
  if (isProviderToolName(toolName) && !isReservedProviderToolName(toolName)) {
    return toolName;
  }
  return toUniqueProviderToolName(toolName, new Set(RESERVED_PROVIDER_TOOL_NAMES));
}
