/**
 * T4 — host-key fingerprint (enroll_fpr) + TOFU pin resolution.
 *
 * `computeEnrollFpr` MUST match the §4.2 `enroll_fpr` format ('sha256:' + base64url(SHA-256(pubkey)))
 * so P3's stored value and the browser's recomputed value are the same string. `resolvePin` takes
 * the RAW agent pubkey (fetched over P3's independent TLS channel — see T8) and recomputes the
 * fingerprint LOCALLY; it never accepts a precomputed fpr string, so the pin decision can never be
 * short-circuited into a relay-influenced 'presented == pinned' comparison.
 */
import { sha256 } from '@noble/hashes/sha2'
import type { DevicePinStore } from 'relay-contracts'
import { encodeBase64UrlBytes } from 'relay-contracts'
import { timingSafeEqual } from './crypto-provider.js'

const FPR_PREFIX = 'sha256:'
const encoder = new TextEncoder()

/** 'sha256:' + base64url(SHA-256(agentPubkey)) — equals the value P3 stores as §4.2 enroll_fpr. */
export function computeEnrollFpr(agentPubkey: Uint8Array): string {
  return FPR_PREFIX + encodeBase64UrlBytes(sha256(agentPubkey))
}

/** Timing-safe equality of two fingerprint strings (byte compare, no length-ordered short-circuit). */
export function verifyPinnedFingerprint(presentedPubkey: Uint8Array, pinnedFpr: string): boolean {
  const computed = computeEnrollFpr(presentedPubkey)
  return timingSafeEqual(encoder.encode(computed), encoder.encode(pinnedFpr))
}

export type PinOutcome = 'tofu-first-use' | 'match' | 'mismatch'

export interface ResolvePinResult {
  readonly outcome: PinOutcome
  readonly computedFpr: string
  readonly pinnedFpr: string | null
}

/**
 * Resolve the TOFU pin for a host using the RAW pubkey (recomputes the fpr itself). Never trusts a
 * caller-supplied fpr string; keys its decision only off `computedFpr`. A `mismatch` NEVER auto-repins.
 */
export async function resolvePin(
  store: DevicePinStore,
  hostId: string,
  agentPubkey: Uint8Array,
): Promise<ResolvePinResult> {
  const computedFpr = computeEnrollFpr(agentPubkey)
  const pinnedFpr = await store.get(hostId)
  if (pinnedFpr === null) {
    return { outcome: 'tofu-first-use', computedFpr, pinnedFpr: null }
  }
  const matches = timingSafeEqual(encoder.encode(computedFpr), encoder.encode(pinnedFpr))
  return { outcome: matches ? 'match' : 'mismatch', computedFpr, pinnedFpr }
}
