import type { CheckFailure } from "../types";
import type { ExpectationResult, KeyedExpectations, RawKeyedExpectations } from "./types";

import { mismatchFailure } from "./failure";
import { applyValueMatcher, displayValueExpectation, resolveValueExpectation } from "./value";

export function checkKeyedExpectations(
  actual: Record<string, unknown>,
  expectations: KeyedExpectations | undefined,
  options: { normalizeKey?: (key: string) => string; path?: string; phase: CheckFailure["phase"] },
): ExpectationResult {
  if (!expectations || expectations.length === 0) return { failure: null, matched: true };

  const normalizeKey = options.normalizeKey ?? ((key: string) => key);
  const basePath = options.path ?? options.phase;
  const actualMap = new Map<string, unknown>();
  for (const [key, value] of Object.entries(actual)) {
    actualMap.set(normalizeKey(key), value);
  }

  for (const expectation of expectations) {
    const actualValue = actualMap.get(normalizeKey(expectation.key));
    if (!applyValueMatcher(actualValue, expectation.matcher)) {
      return {
        failure: mismatchFailure(
          options.phase,
          `${basePath}.${expectation.key}`,
          displayValueExpectation(expectation.matcher),
          actualValue,
          `${expectation.key} mismatch`,
        ),
        matched: false,
      };
    }
  }

  return { failure: null, matched: true };
}

export function resolveKeyedExpectations(raw: RawKeyedExpectations | undefined): KeyedExpectations | undefined {
  if (raw === undefined) return undefined;
  return Object.entries(raw).map(([key, value]) => ({
    key,
    matcher: resolveValueExpectation(value),
  }));
}
