import { createHmac, timingSafeEqual, createHash } from 'node:crypto';

export const INTEGRATION_HEADERS = {
  integrationId: 'x-hgc-integration-id',
  timestamp: 'x-hgc-timestamp',
  nonce: 'x-hgc-nonce',
  idempotencyKey: 'x-hgc-idempotency-key',
  signature: 'x-hgc-signature',
  correlationId: 'x-correlation-id',
} as const;

export interface SignedRequestParts {
  method: string;
  path: string;
  query: string;
  body: string;
  timestamp: string;
  nonce: string;
  idempotencyKey: string;
}

export function hashBody(body: string): string {
  return createHash('sha256').update(body).digest('hex');
}

export function buildSignatureBase(parts: SignedRequestParts): string {
  return [
    parts.method.toUpperCase(),
    parts.path,
    parts.query,
    hashBody(parts.body),
    parts.timestamp,
    parts.nonce,
    parts.idempotencyKey,
  ].join('\n');
}

export function signRequest(secret: string, parts: SignedRequestParts): string {
  const base = buildSignatureBase(parts);
  return createHmac('sha256', secret).update(base).digest('hex');
}

export function verifyRequestSignature(
  secret: string,
  parts: SignedRequestParts,
  providedSignature: string,
  options?: { maxSkewMs?: number; now?: number },
): boolean {
  const maxSkewMs = options?.maxSkewMs ?? 5 * 60 * 1000;
  const now = options?.now ?? Date.now();
  const timestampMs = Number.parseInt(parts.timestamp, 10);

  if (!Number.isFinite(timestampMs)) {
    return false;
  }

  if (Math.abs(now - timestampMs) > maxSkewMs) {
    return false;
  }

  const expected = signRequest(secret, parts);
  const expectedBuffer = Buffer.from(expected, 'utf8');
  const providedBuffer = Buffer.from(providedSignature, 'utf8');

  if (expectedBuffer.length !== providedBuffer.length) {
    return false;
  }

  return timingSafeEqual(expectedBuffer, providedBuffer);
}

export function canonicalQuery(query: Record<string, string | string[] | undefined>): string {
  return Object.entries(query)
    .filter(([, value]) => value !== undefined)
    .map(([key, value]) => {
      const normalized = Array.isArray(value) ? value[0] : value;
      return `${encodeURIComponent(key)}=${encodeURIComponent(normalized ?? '')}`;
    })
    .sort()
    .join('&');
}
