import { matchesNavigationConditions, type UnleashChecker } from "./navigation";
import type {
  FunnelAnswers,
  ScreenDefinition,
  ScreenVariantDefinition,
} from "./types";

function cloneScreen<T>(screen: T): T {
  if (typeof globalThis.structuredClone === "function") {
    return globalThis.structuredClone(screen);
  }

  return JSON.parse(JSON.stringify(screen)) as T;
}

function isPlainObject(value: unknown): value is Record<string, unknown> {
  return typeof value === "object" && value !== null && !Array.isArray(value);
}

function deepMerge<T>(target: T, source: Partial<T> | undefined): T {
  if (!source) {
    return target;
  }

  const result: T = Array.isArray(target)
    ? ([...target] as unknown as T)
    : ({ ...(target as Record<string, unknown>) } as T);

  for (const key of Object.keys(source) as (keyof T)[]) {
    const sourceValue = source[key];

    if (sourceValue === undefined) {
      continue;
    }

    const targetValue = (result as Record<string, unknown>)[key as unknown as string];

    if (isPlainObject(sourceValue)) {
      const baseValue = isPlainObject(targetValue) ? targetValue : {};
      (result as Record<string, unknown>)[key as unknown as string] = deepMerge(
        baseValue,
        sourceValue as Record<string, unknown>
      ) as unknown as T[keyof T];
      continue;
    }

    (result as Record<string, unknown>)[key as unknown as string] = sourceValue as unknown as T[keyof T];
  }

  return result;
}

function applyScreenOverrides<T extends ScreenDefinition>(
  screen: T,
  overrides: ScreenVariantDefinition<T>["overrides"]
): T {
  const cloned = cloneScreen(screen);
  // DeepPartial совместим с deepMerge, так как deepMerge обрабатывает вложенные объекты
  return deepMerge(cloned, overrides as unknown as Partial<T>);
}

export function resolveScreenVariant<T extends ScreenDefinition>(
  screen: T,
  answers: FunnelAnswers,
  allScreens?: ScreenDefinition[],
  unleashChecker?: UnleashChecker
): T {
  const variants = (screen as T & { variants?: ScreenVariantDefinition<T>[] }).variants;

  if (!variants || variants.length === 0) {
    return screen;
  }

  for (const variant of variants) {
    // Передаем allScreens и unleashChecker для правильной проверки условий
    if (matchesNavigationConditions(variant.conditions, answers, allScreens, unleashChecker)) {
      return applyScreenOverrides(screen, variant.overrides);
    }
  }

  return screen;
}
