import type {
  ClassificationMethod,
  ColorType,
  StyleConfig,
  DefaultLayerStyleId,
} from "@components/olmap/core/Controls/styleEditorTypes";

export type ApplyLayerStyleActionPayload = {
  layerId: DefaultLayerStyleId;
  resetToDefault: boolean;
  styleConfig?: Partial<StyleConfig>;
};

export const APPLY_LAYER_STYLE_TOOL = "apply_layer_style";

const LAYER_LABELS: Record<DefaultLayerStyleId, string> = {
  junctions: "节点",
  pipes: "管道",
};

const asString = (value: unknown): string | undefined =>
  typeof value === "string" && value.trim() ? value.trim() : undefined;

const asNumber = (value: unknown): number | undefined =>
  typeof value === "number" && Number.isFinite(value)
    ? value
    : typeof value === "string" && value.trim() && Number.isFinite(Number(value))
      ? Number(value)
      : undefined;

const asBoolean = (value: unknown): boolean | undefined =>
  typeof value === "boolean"
    ? value
    : typeof value === "string"
      ? value === "true"
        ? true
        : value === "false"
          ? false
          : undefined
      : undefined;

const asNumberArray = (value: unknown): number[] | undefined =>
  Array.isArray(value)
    ? value
        .map((item) => asNumber(item))
        .filter((item): item is number => item !== undefined)
    : undefined;

const asStringArray = (value: unknown): string[] | undefined =>
  Array.isArray(value)
    ? value
        .map((item) => asString(item))
        .filter((item): item is string => item !== undefined)
    : undefined;

const asClassificationMethod = (value: unknown): ClassificationMethod | undefined => {
  const normalized = asString(value);
  return normalized === "pretty_breaks" || normalized === "custom_breaks"
    ? normalized
    : undefined;
};

const asColorType = (value: unknown): ColorType | undefined => {
  const normalized = asString(value);
  return normalized === "single" ||
    normalized === "gradient" ||
    normalized === "rainbow" ||
    normalized === "custom"
    ? normalized
    : undefined;
};

export const normalizeStyleLayerId = (value: unknown): DefaultLayerStyleId | null => {
  const normalized = asString(value)?.toLowerCase();
  if (normalized === "junctions" || normalized === "pipes") {
    return normalized;
  }
  return null;
};

export const getStyleLayerLabel = (layerId: DefaultLayerStyleId): string =>
  LAYER_LABELS[layerId];

export const parseApplyLayerStylePayload = (
  params: Record<string, unknown>,
): ApplyLayerStyleActionPayload | null => {
  const layerId = normalizeStyleLayerId(params.layer_id ?? params.layerId);
  if (!layerId) {
    return null;
  }

  const resetToDefault = Boolean(
    asBoolean(params.reset_to_default ?? params.resetToDefault),
  );
  const rawStyleConfig =
    params.style_config && typeof params.style_config === "object"
      ? (params.style_config as Record<string, unknown>)
      : params.styleConfig && typeof params.styleConfig === "object"
        ? (params.styleConfig as Record<string, unknown>)
        : null;

  const classificationValue =
    rawStyleConfig?.classification_method ?? rawStyleConfig?.classificationMethod;
  const colorTypeValue = rawStyleConfig?.color_type ?? rawStyleConfig?.colorType;
  const segmentsValue = rawStyleConfig?.segments;
  const segments = asNumber(segmentsValue);
  if (
    (classificationValue !== undefined && !asClassificationMethod(classificationValue)) ||
    (colorTypeValue !== undefined && !asColorType(colorTypeValue)) ||
    (segmentsValue !== undefined &&
      (!Number.isInteger(segments) || (segments as number) < 2 || (segments as number) > 10))
  ) {
    return null;
  }

  const styleConfig: Partial<StyleConfig> | undefined = rawStyleConfig
    ? {
        property: asString(rawStyleConfig.property),
        classificationMethod: asClassificationMethod(classificationValue),
        segments,
        minSize: asNumber(rawStyleConfig.min_size ?? rawStyleConfig.minSize),
        maxSize: asNumber(rawStyleConfig.max_size ?? rawStyleConfig.maxSize),
        minStrokeWidth: asNumber(
          rawStyleConfig.min_stroke_width ?? rawStyleConfig.minStrokeWidth,
        ),
        maxStrokeWidth: asNumber(
          rawStyleConfig.max_stroke_width ?? rawStyleConfig.maxStrokeWidth,
        ),
        fixedStrokeWidth: asNumber(
          rawStyleConfig.fixed_stroke_width ?? rawStyleConfig.fixedStrokeWidth,
        ),
        colorType: asColorType(colorTypeValue),
        singlePaletteIndex: asNumber(
          rawStyleConfig.single_palette_index ?? rawStyleConfig.singlePaletteIndex,
        ),
        gradientPaletteIndex: asNumber(
          rawStyleConfig.gradient_palette_index ?? rawStyleConfig.gradientPaletteIndex,
        ),
        rainbowPaletteIndex: asNumber(
          rawStyleConfig.rainbow_palette_index ?? rawStyleConfig.rainbowPaletteIndex,
        ),
        showLabels: asBoolean(rawStyleConfig.show_labels ?? rawStyleConfig.showLabels),
        showId: asBoolean(rawStyleConfig.show_id ?? rawStyleConfig.showId),
        opacity: asNumber(rawStyleConfig.opacity),
        adjustWidthByProperty: asBoolean(
          rawStyleConfig.adjust_width_by_property ??
            rawStyleConfig.adjustWidthByProperty,
        ),
        customBreaks: asNumberArray(
          rawStyleConfig.custom_breaks ?? rawStyleConfig.customBreaks,
        ),
        customColors: asStringArray(
          rawStyleConfig.custom_colors ?? rawStyleConfig.customColors,
        ),
      }
    : undefined;

  if (styleConfig) {
    const numericValues = [
      styleConfig.minSize,
      styleConfig.maxSize,
      styleConfig.minStrokeWidth,
      styleConfig.maxStrokeWidth,
      styleConfig.fixedStrokeWidth,
    ].filter((value): value is number => value !== undefined);
    if (numericValues.some((value) => value <= 0)) return null;
    const paletteIndexes: Array<[number | undefined, number]> = [
      [styleConfig.singlePaletteIndex, 7],
      [styleConfig.gradientPaletteIndex, 3],
      [styleConfig.rainbowPaletteIndex, 2],
    ];
    if (
      paletteIndexes.some(
        ([index, length]) =>
          index !== undefined &&
          (!Number.isInteger(index) || index < 0 || index >= length),
      )
    ) return null;
    if (
      styleConfig.opacity !== undefined &&
      (styleConfig.opacity < 0 || styleConfig.opacity > 1)
    ) return null;
    if (
      styleConfig.customBreaks &&
      styleConfig.customBreaks.some(
        (value, index, values) => index > 0 && value <= values[index - 1],
      )
    ) return null;
    if (
      styleConfig.segments !== undefined &&
      styleConfig.customBreaks &&
      styleConfig.customBreaks.length !== styleConfig.segments + 1
    ) return null;
    if (
      styleConfig.segments !== undefined &&
      styleConfig.customColors &&
      styleConfig.customColors.length !== styleConfig.segments
    ) return null;
  }

  const hasStyleOverrides =
    styleConfig &&
    Object.values(styleConfig).some((value) =>
      Array.isArray(value) ? value.length > 0 : value !== undefined,
    );

  if (!resetToDefault && !hasStyleOverrides) {
    return null;
  }

  return {
    layerId,
    resetToDefault,
    styleConfig: hasStyleOverrides ? styleConfig : undefined,
  };
};

export const describeApplyLayerStyle = (
  payload: ApplyLayerStyleActionPayload,
): string => {
  const layerLabel = getStyleLayerLabel(payload.layerId);
  if (payload.resetToDefault) {
    return `${layerLabel} · 重置默认样式`;
  }
  const property = payload.styleConfig?.property;
  return property ? `${layerLabel} · ${property}` : `${layerLabel} · 应用样式`;
};
