import {
  TemplateSampleSetting,
  TemplateSettingValueBulkItem,
  TemplateSettingValueScope,
} from "@/user-management/services/TemplateConfiguration/types/templateTypes";
import {
  isLetterLabelsSetting,
  normalizeLetterLabelsValue,
} from "./letterLabelsConstants";
import {
  isPageMarginSetting,
  normalizePageMarginValue,
} from "../components/PageMarginEditor";
import {
  isLayoutConfigSetting,
  normalizeLayoutConfigValue,
} from "../components/LayoutConfigEditor";
import { ListBulletResource } from "./templateResourceConstants";
import {
  formatSettingValueForApi,
  resolveSettingResourceId,
} from "./templateSamplePayloadUtils";

export type BuiltSettingPayloadItem = {
  id: string;
  code: string;
  value: Record<string, unknown>;
  resourceId: string | null;
};

export type SettingBaseline = {
  value: Record<string, unknown>;
  resourceId: string | null;
  usesGlobalDefault: boolean;
};

function stableSerialize(value: unknown): string {
  return JSON.stringify(value ?? null);
}

export function buildSettingBaselines(
  payloadItems: BuiltSettingPayloadItem[],
  settings: TemplateSampleSetting[],
): Record<string, SettingBaseline> {
  const overrideById = new Map(settings.map((setting) => [setting.id, setting]));
  const baselines: Record<string, SettingBaseline> = {};

  for (const item of payloadItems) {
    const setting = overrideById.get(item.id);
    baselines[item.id] = {
      value: item.value,
      resourceId: item.resourceId,
      usesGlobalDefault: !setting?.isUnitOverride,
    };
  }

  return baselines;
}

export function buildSettingsPayloadItems(
  settings: TemplateSampleSetting[],
  resourceIds: Record<string, string | null>,
  bulletResources: ListBulletResource[],
  cssValueToPreviewObject: (cssString: string) => Record<string, string>,
): BuiltSettingPayloadItem[] {
  return settings.map((setting) => {
    if (isLetterLabelsSetting(setting.code)) {
      return {
        id: setting.id,
        code: setting.code,
        value: normalizeLetterLabelsValue(setting.value),
        resourceId: null,
      };
    }

    if (isPageMarginSetting(setting.code)) {
      return {
        id: setting.id,
        code: setting.code,
        value: normalizePageMarginValue(setting.value) as unknown as Record<
          string,
          unknown
        >,
        resourceId: null,
      };
    }

    if (isLayoutConfigSetting(setting.code)) {
      return {
        id: setting.id,
        code: setting.code,
        value: normalizeLayoutConfigValue(setting.value) as unknown as Record<
          string,
          unknown
        >,
        resourceId: null,
      };
    }

    const cssString = typeof setting.value === "string" ? setting.value : "";
    const valueObj = cssValueToPreviewObject(cssString);
    const resourceId = resolveSettingResourceId(
      setting,
      valueObj,
      resourceIds,
      bulletResources,
    );
    const formattedValue = formatSettingValueForApi(
      valueObj,
      resourceId,
      setting.code,
    );

    return {
      id: setting.id,
      code: setting.code,
      value: formattedValue,
      resourceId,
    };
  });
}

export function filterChangedSettingOverrides(
  items: BuiltSettingPayloadItem[],
  baselines: Record<string, SettingBaseline>,
  useDefaultSettings: Record<string, boolean>,
): BuiltSettingPayloadItem[] {
  return items.filter((item) => {
    const baseline = baselines[item.id];
    if (!baseline) return true;

    const usesGlobalDefault = useDefaultSettings[item.id] ?? true;
    const valueChanged =
      stableSerialize(item.value) !== stableSerialize(baseline.value);
    const resourceChanged = (item.resourceId ?? null) !== baseline.resourceId;
    const defaultStateChanged = usesGlobalDefault !== baseline.usesGlobalDefault;

    return valueChanged || resourceChanged || defaultStateChanged;
  });
}

/** Preview overrides must not carry unit resource ids when reverting to template default. */
export function finalizePreviewOverrides(
  items: BuiltSettingPayloadItem[],
  useDefaultSettings: Record<string, boolean>,
  baselines: Record<string, SettingBaseline>,
): BuiltSettingPayloadItem[] {
  return items.map((item) => {
    const usesGlobalDefault = useDefaultSettings[item.id] ?? true;
    const baseline = baselines[item.id];
    const revertedToDefault =
      usesGlobalDefault && baseline && !baseline.usesGlobalDefault;

    if (!revertedToDefault) {
      return item;
    }

    return {
      ...item,
      resourceId: null,
    };
  });
}

export function buildBulkSavePayload(options: {
  settings: TemplateSampleSetting[];
  payloadItems: BuiltSettingPayloadItem[];
  useDefaultSettings: Record<string, boolean>;
  scope: TemplateSettingValueScope;
  baselines?: Record<string, SettingBaseline>;
}): TemplateSettingValueBulkItem[] {
  const { settings, payloadItems, useDefaultSettings, scope, baselines } =
    options;
  const payloadById = new Map(payloadItems.map((item) => [item.id, item]));
  const bulk: TemplateSettingValueBulkItem[] = [];

  for (const setting of settings) {
    const usesGlobalDefault = useDefaultSettings[setting.id] ?? true;
    const baseline = baselines?.[setting.id];

    if (usesGlobalDefault) {
      if (scope === "unit" && setting.isUnitOverride) {
        bulk.push({
          templateSettingId: setting.id,
          revertToDefault: true,
        });
      }
      continue;
    }

    const item = payloadById.get(setting.id);
    if (!item) continue;

    if (baseline) {
      const valueChanged =
        stableSerialize(item.value) !== stableSerialize(baseline.value);
      const resourceChanged = (item.resourceId ?? null) !== baseline.resourceId;
      const defaultStateChanged =
        usesGlobalDefault !== baseline.usesGlobalDefault;
      if (!valueChanged && !resourceChanged && !defaultStateChanged) {
        continue;
      }
    }

    bulk.push({
      templateSettingId: setting.id,
      resourceId: item.resourceId,
      value: item.value,
      scope,
    });
  }

  return bulk;
}
