import { stableDigest } from "@best-agent/foundation";
import type { ApprovalGrant, ApprovalGrantScope, CapabilityRequest } from "@best-agent/protocol";
import {
  redactSecretLikeTextForHistory,
  renderShellArgvForHistory,
} from "../../policy/capability-history.js";
import { isSensitiveCapabilityRequest } from "../../policy/runtime-policy.js";
import type { ApprovalCheckpointPayload, ApprovalGrantActionSource } from "../checkpoint-codec.js";

export function resolveCapabilityKind(request: CapabilityRequest): ApprovalGrant["kind"] {
  return request.kind ?? request.group;
}

export function approvalScopesForRequest(request: CapabilityRequest): ApprovalGrantScope[] {
  if (isSensitiveCapabilityRequest(request)) {
    return ["once"];
  }
  return request.approvalScopes ?? ["once", "session", "thread", "workspace"];
}

export function describeApprovalActionForDisplay(request: CapabilityRequest): string {
  switch (request.group) {
    case "tool":
      return `tool:${request.toolName}`;
    case "filesystem":
      return `${request.action}:${redactSecretLikeTextForHistory(request.path)}`;
    case "shell":
      return renderShellArgvForHistory(request.argv);
    case "network":
      return redactSecretLikeTextForHistory(request.host);
    default:
      return "unknown";
  }
}

export function resolveApprovalGrantAction(request: CapabilityRequest, toolName?: string): string {
  if (request.group === "shell") {
    if (isSensitiveCapabilityRequest(request)) {
      return `shell:sensitive:${semanticCapabilityRequestDigest(request)}`;
    }
    return `shell:${resolveShellApprovalClass(request, toolName)}`;
  }
  const action = describeApprovalAction(request);
  if (redactSecretLikeTextForHistory(action) !== action) {
    return `${request.group}:${resolveCapabilityKind(request)}:${semanticCapabilityRequestDigest(request)}`;
  }
  return action;
}

export function resolveApprovalGrantActionSource(
  request: CapabilityRequest,
): ApprovalGrantActionSource | undefined {
  if (request.group === "shell") {
    return isSensitiveCapabilityRequest(request) ? "raw-request-digest" : undefined;
  }
  const action = describeApprovalAction(request);
  return redactSecretLikeTextForHistory(action) !== action ? "raw-request-digest" : undefined;
}

export function resolvePersistedApprovalGrantAction(
  request: CapabilityRequest,
  toolName?: string,
  approvalGrantAction?: string,
  approvalGrantActionSource?: ApprovalGrantActionSource,
): string {
  if (
    approvalGrantAction &&
    isReusablePersistedApprovalGrantAction(
      approvalGrantAction,
      request,
      toolName,
      approvalGrantActionSource,
    )
  ) {
    return approvalGrantAction;
  }
  return resolveApprovalGrantAction(request, toolName);
}

export function isSameApprovalCheckpointRequest(
  checkpoint: ApprovalCheckpointPayload,
  request: CapabilityRequest,
): boolean {
  if (isSameCapabilityRequest(checkpoint.request, request)) {
    return true;
  }
  if (
    checkpoint.request.group !== request.group ||
    resolveCapabilityKind(checkpoint.request) !== resolveCapabilityKind(request) ||
    checkpoint.approvalGrantAction === undefined
  ) {
    return false;
  }
  return isReusablePersistedApprovalGrantAction(
    checkpoint.approvalGrantAction,
    request,
    checkpoint.toolName,
    checkpoint.approvalGrantActionSource,
  );
}

export function isReusablePersistedApprovalGrantAction(
  action: string,
  request: CapabilityRequest,
  toolName?: string,
  source?: ApprovalGrantActionSource,
): boolean {
  if (request.group !== "shell") {
    if (source === "raw-request-digest" && isNonShellRawRequestDigestGrantAction(action, request)) {
      return true;
    }
    return action === resolveApprovalGrantAction(request, toolName);
  }
  if (action === resolveApprovalGrantAction(request, toolName)) {
    return true;
  }
  if (!isHistorySafeShellOmittedRequest(request)) {
    return false;
  }
  if (SHELL_CLASS_GRANT_ACTION_RE.test(action)) {
    return true;
  }
  if (source === "raw-request-digest") {
    return SHELL_SENSITIVE_GRANT_ACTION_RE.test(action);
  }
  return false;
}

export function uniqueApprovalGrantActions(...actions: string[]): string[] {
  return Array.from(new Set(actions));
}

function describeApprovalAction(request: CapabilityRequest): string {
  switch (request.group) {
    case "tool":
      return `tool:${request.toolName}`;
    case "filesystem":
      return `${request.action}:${request.path}`;
    case "shell":
      return request.argv.join(" ");
    case "network":
      return request.host;
    default:
      return "unknown";
  }
}

const SHELL_SENSITIVE_GRANT_ACTION_RE = /^shell:sensitive:[a-f0-9]{64}$/u;
const SHELL_CLASS_GRANT_ACTION_RE = /^shell:(?:shell:)?[A-Za-z0-9._-]+:non-sensitive$/u;
const GRANT_ACTION_DIGEST_RE = /^[a-f0-9]{64}$/u;

function resolveShellApprovalClass(
  request: Extract<CapabilityRequest, { group: "shell" }>,
  toolName?: string,
): string {
  const commandClass = inferShellPrimaryCommandClass(request.argv);
  if (toolName === "shell") {
    return `shell:${commandClass}:non-sensitive`;
  }
  return `${commandClass}:non-sensitive`;
}

function inferShellPrimaryCommandClass(argv: string[]): string {
  const shellCommand = extractShellEvalCommand(argv);
  const command = shellCommand ? stripLeadingCdAndEnvAssignments(shellCommand) : argv.join(" ");
  const token = command.trim().split(/\s+/u)[0] ?? "";
  const base = token.split(/[\\/]/u).at(-1) ?? "";
  return /^[A-Za-z0-9._-]+$/u.test(base) && base ? base : "command";
}

function extractShellEvalCommand(argv: string[]): string | undefined {
  const executable = argv[0]?.split(/[\\/]/u).at(-1);
  if (executable !== "sh" && executable !== "bash" && executable !== "zsh") {
    return undefined;
  }
  const evalArgIndex = argv.findIndex((arg) => arg === "-c" || arg === "-lc");
  const command = evalArgIndex >= 0 ? argv[evalArgIndex + 1] : undefined;
  return typeof command === "string" && command.trim() ? command : undefined;
}

function isHistorySafeShellOmittedRequest(
  request: CapabilityRequest,
): request is Extract<CapabilityRequest, { group: "shell" }> {
  return request.group === "shell" && request.argv[0] === "[shell command omitted:";
}

function isNonShellRawRequestDigestGrantAction(
  action: string,
  request: CapabilityRequest,
): boolean {
  const [group, kind, digest, extra] = action.split(":");
  return (
    extra === undefined &&
    group === request.group &&
    kind === resolveCapabilityKind(request) &&
    digest !== undefined &&
    GRANT_ACTION_DIGEST_RE.test(digest)
  );
}

function isSameCapabilityRequest(left: CapabilityRequest, right: CapabilityRequest): boolean {
  return stableDigest(left) === stableDigest(right);
}

function semanticCapabilityRequestDigest(value: CapabilityRequest): string {
  return stableDigest(value);
}

function stripLeadingCdAndEnvAssignments(command: string): string {
  let normalized = command.trim().replace(/^set\s+-o\s+pipefail\s*;\s*/u, "");
  for (;;) {
    const next = normalized.replace(/^cd\s+(?:"[^"]+"|'[^']+'|[^;&|`$]+)\s+&&\s*/u, "");
    if (next === normalized) {
      break;
    }
    normalized = next.trim();
  }
  normalized = normalized.replace(/^(?:env\s+)?(?:[A-Za-z_][A-Za-z0-9_]*=\S+\s+)*/u, "");
  return normalized.trim();
}
