/**
 * Shared i18n utilities for config form templates and fields.
 *
 * These functions handle translation key path building and label normalization
 * for RJSF form fields.
 */

import type { ConfigFormContext } from "@/types/configForm";
import { getEffectiveAttributeLabels } from "@/utils/configUtil";

const isRecord = (value: unknown): value is Record<string, unknown> =>
  typeof value === "object" && value !== null;

const resolveDetectorType = (
  detectorConfig: unknown,
  detectorKey?: string,
): string | undefined => {
  if (!detectorKey || !isRecord(detectorConfig)) {
    return undefined;
  }

  const entry = detectorConfig[detectorKey];
  if (!isRecord(entry)) {
    return undefined;
  }

  const typeValue = entry.type;
  return typeof typeValue === "string" && typeValue.length > 0
    ? typeValue
    : undefined;
};

const resolveDetectorTypeFromContext = (
  formContext: ConfigFormContext | undefined,
  detectorKey?: string,
): string | undefined => {
  const formData = formContext?.formData;
  if (!detectorKey || !isRecord(formData)) {
    return undefined;
  }

  const detectorConfig = isRecord(formData.detectors)
    ? formData.detectors
    : formData;

  return resolveDetectorType(detectorConfig, detectorKey);
};

/**
 * Build the i18n translation key path for nested fields using the field path
 * provided by RJSF. This avoids ambiguity with underscores in field names and
 * normalizes dynamic segments like filter object names or detector names.
 *
 * @param segments Array of path segments (strings and/or numbers)
 * @param sectionI18nPrefix Optional section prefix for specialized sections
 * @param formContext Optional form context for resolving detector types
 * @returns Normalized translation key path as a dot-separated string
 *
 * @example
 * buildTranslationPath(["filters", "person", "threshold"]) => "filters.threshold"
 * buildTranslationPath(["detectors", "ov1", "type"]) => "detectors.openvino.type"
 * buildTranslationPath(["ov1", "type"], "detectors") => "openvino.type"
 */
export function buildTranslationPath(
  segments: Array<string | number>,
  sectionI18nPrefix?: string,
  formContext?: ConfigFormContext,
): string {
  // Filter out numeric indices to get string segments only
  const stringSegments = segments.filter(
    (segment): segment is string => typeof segment === "string",
  );

  // Handle filters section - skip the dynamic filter object name. Route
  // to `filters_attribute.<field>` when the dynamic key is an attribute
  // label (face, license_plate, courier logos) so attribute filter fields
  // pick up the attribute-worded translations emitted by
  // generate_config_translations.py.
  // Example: filters.person.threshold      -> filters.threshold
  // Example: filters.face.min_area         -> filters_attribute.min_area
  const filtersIndex = stringSegments.indexOf("filters");
  if (filtersIndex !== -1 && stringSegments.length > filtersIndex + 2) {
    const filterKey = stringSegments[filtersIndex + 1];
    const allAttributes = getEffectiveAttributeLabels(
      formContext?.fullConfig,
      formContext?.fullCameraConfig,
      formContext?.level,
    );
    const sectionWord = allAttributes.includes(filterKey)
      ? "filters_attribute"
      : "filters";
    const normalized = [
      ...stringSegments.slice(0, filtersIndex),
      sectionWord,
      ...stringSegments.slice(filtersIndex + 2),
    ];
    return normalized.join(".");
  }

  // Handle detectors section - resolve the detector type when available
  // Example: detectors.ov1.type -> detectors.openvino.type
  const detectorsIndex = stringSegments.indexOf("detectors");
  if (detectorsIndex !== -1 && stringSegments.length > detectorsIndex + 2) {
    const detectorKey = stringSegments[detectorsIndex + 1];
    const detectorType = resolveDetectorTypeFromContext(
      formContext,
      detectorKey,
    );
    if (detectorType) {
      const normalized = [
        ...stringSegments.slice(0, detectorsIndex + 1),
        detectorType,
        ...stringSegments.slice(detectorsIndex + 2),
      ];
      return normalized.join(".");
    }

    const normalized = [
      ...stringSegments.slice(0, detectorsIndex + 1),
      ...stringSegments.slice(detectorsIndex + 2),
    ];
    return normalized.join(".");
  }

  // Handle specialized sections like detectors where the first segment is dynamic
  // Example: (sectionI18nPrefix="detectors") "ov1.type" -> "openvino.type"
  if (sectionI18nPrefix === "detectors" && stringSegments.length > 1) {
    const detectorKey = stringSegments[0];
    const detectorType = resolveDetectorTypeFromContext(
      formContext,
      detectorKey,
    );
    if (detectorType) {
      return [detectorType, ...stringSegments.slice(1)].join(".");
    }

    return stringSegments.slice(1).join(".");
  }

  return stringSegments.join(".");
}

/**
 * Resolve a translated label or description for a config form field.
 *
 * Tries keys in priority order:
 *   1. Type-specific prefixed key (e.g. "detectors.edgetpu.device.label")
 *   2. Shared prefixed key with type stripped (e.g. "detectors.device.label")
 *   3. Unprefixed key (e.g. "device.label")
 *
 * @returns The translated string, or undefined if no key matched.
 */
export function resolveConfigTranslation(
  i18n: { exists: (key: string, opts?: Record<string, unknown>) => boolean },
  t: (key: string, opts?: Record<string, unknown>) => string,
  translationPath: string,
  suffix: "label" | "description",
  sectionI18nPrefix?: string,
  ns?: string,
): string | undefined {
  const opts = ns ? { ns } : undefined;

  if (
    sectionI18nPrefix &&
    !translationPath.startsWith(`${sectionI18nPrefix}.`)
  ) {
    // 1. Type-specific prefixed key (e.g. detectors.edgetpu.device.label)
    const prefixed = `${sectionI18nPrefix}.${translationPath}.${suffix}`;
    if (i18n.exists(prefixed, opts)) return t(prefixed, opts);

    // 2. Shared prefixed key — strip leading type segment
    //    e.g. detectors.edgetpu.model.path → detectors.model.path
    const dot = translationPath.indexOf(".");
    if (dot !== -1) {
      const shared = `${sectionI18nPrefix}.${translationPath.substring(dot + 1)}.${suffix}`;
      if (i18n.exists(shared, opts)) return t(shared, opts);
    }
  }

  // 3. Unprefixed key
  const base = `${translationPath}.${suffix}`;
  if (i18n.exists(base, opts)) return t(base, opts);

  return undefined;
}

/**
 * Extract the filter object label from a path containing "filters" segment.
 * Returns the segment immediately after "filters".
 *
 * @param pathSegments Array of path segments
 * @returns The filter object label or undefined if not found
 *
 * @example
 * getFilterObjectLabel(["filters", "person", "threshold"]) => "person"
 * getFilterObjectLabel(["detect", "enabled"]) => undefined
 */
export function getFilterObjectLabel(
  pathSegments: Array<string | number>,
): string | undefined {
  const filtersIndex = pathSegments.indexOf("filters");
  if (filtersIndex === -1 || pathSegments.length <= filtersIndex + 1) {
    return undefined;
  }
  const objectLabel = pathSegments[filtersIndex + 1];
  return typeof objectLabel === "string" && objectLabel.length > 0
    ? objectLabel
    : undefined;
}

/**
 * Convert snake_case string to Title Case with spaces.
 * Useful for generating human-readable labels from schema property names.
 *
 * @param value The snake_case string to convert
 * @returns Title Case string
 *
 * @example
 * humanizeKey("detect_fps") => "Detect Fps"
 * humanizeKey("min_initialized") => "Min Initialized"
 */
export function humanizeKey(value: string): string {
  return value
    .replace(/_/g, " ")
    .replace(/\b\w/g, (char) => char.toUpperCase());
}

/**
 * Extract domain name from an i18n namespace string.
 * Handles config/* namespace format by stripping the prefix.
 *
 * @param ns The i18n namespace (e.g., "config/audio", "config/global")
 * @returns The domain portion (e.g., "audio", "global") or empty string
 *
 * @example
 * getDomainFromNamespace("config/audio") => "audio"
 * getDomainFromNamespace("common") => ""
 */
export function getDomainFromNamespace(ns?: string): string {
  if (!ns || !ns.startsWith("config/")) return "";
  return ns.replace("config/", "");
}
