import { buildPluginConfigSchema } from "openclaw/plugin-sdk/core";
import { z } from "openclaw/plugin-sdk/zod";
import type { OpenClawPluginConfigSchema } from "../api.js";
import {
  DIFF_IMAGE_QUALITY_PRESETS,
  DIFF_INDICATORS,
  DIFF_LAYOUTS,
  DIFF_MODES,
  DIFF_OUTPUT_FORMATS,
  DIFF_THEMES,
  type DiffFileDefaults,
  type DiffImageQualityPreset,
  type DiffIndicators,
  type DiffLayout,
  type DiffMode,
  type DiffOutputFormat,
  type DiffPresentationDefaults,
  type DiffTheme,
  type DiffToolDefaults,
} from "./types.js";

type DiffsPluginConfig = {
  defaults?: {
    fontFamily?: string;
    fontSize?: number;
    lineSpacing?: number;
    layout?: DiffLayout;
    showLineNumbers?: boolean;
    diffIndicators?: DiffIndicators;
    wordWrap?: boolean;
    background?: boolean;
    theme?: DiffTheme;
    fileFormat?: DiffOutputFormat;
    fileQuality?: DiffImageQualityPreset;
    fileScale?: number;
    fileMaxWidth?: number;
    format?: DiffOutputFormat;
    // Backward-compatible aliases retained for existing configs.
    imageFormat?: DiffOutputFormat;
    imageQuality?: DiffImageQualityPreset;
    imageScale?: number;
    imageMaxWidth?: number;
    mode?: DiffMode;
  };
  security?: {
    allowRemoteViewer?: boolean;
  };
};

const DEFAULT_IMAGE_QUALITY_PROFILES = {
  standard: {
    scale: 2,
    maxWidth: 960,
    maxPixels: 8_000_000,
  },
  hq: {
    scale: 2.5,
    maxWidth: 1200,
    maxPixels: 14_000_000,
  },
  print: {
    scale: 3,
    maxWidth: 1400,
    maxPixels: 24_000_000,
  },
} as const satisfies Record<
  DiffImageQualityPreset,
  { scale: number; maxWidth: number; maxPixels: number }
>;

export const DEFAULT_DIFFS_TOOL_DEFAULTS: DiffToolDefaults = {
  fontFamily: "Fira Code",
  fontSize: 15,
  lineSpacing: 1.6,
  layout: "unified",
  showLineNumbers: true,
  diffIndicators: "bars",
  wordWrap: true,
  background: true,
  theme: "dark",
  fileFormat: "png",
  fileQuality: "standard",
  fileScale: DEFAULT_IMAGE_QUALITY_PROFILES.standard.scale,
  fileMaxWidth: DEFAULT_IMAGE_QUALITY_PROFILES.standard.maxWidth,
  mode: "both",
};

export type DiffsPluginSecurityConfig = {
  allowRemoteViewer: boolean;
};

export const DEFAULT_DIFFS_PLUGIN_SECURITY: DiffsPluginSecurityConfig = {
  allowRemoteViewer: false,
};

const DiffsPluginJsonSchemaSource = z.strictObject({
  defaults: z
    .strictObject({
      fontFamily: z.string().default(DEFAULT_DIFFS_TOOL_DEFAULTS.fontFamily).optional(),
      fontSize: z.number().min(10).max(24).default(DEFAULT_DIFFS_TOOL_DEFAULTS.fontSize).optional(),
      lineSpacing: z
        .number()
        .min(1)
        .max(3)
        .default(DEFAULT_DIFFS_TOOL_DEFAULTS.lineSpacing)
        .optional(),
      layout: z.enum(DIFF_LAYOUTS).default(DEFAULT_DIFFS_TOOL_DEFAULTS.layout).optional(),
      showLineNumbers: z.boolean().default(DEFAULT_DIFFS_TOOL_DEFAULTS.showLineNumbers).optional(),
      diffIndicators: z
        .enum(DIFF_INDICATORS)
        .default(DEFAULT_DIFFS_TOOL_DEFAULTS.diffIndicators)
        .optional(),
      wordWrap: z.boolean().default(DEFAULT_DIFFS_TOOL_DEFAULTS.wordWrap).optional(),
      background: z.boolean().default(DEFAULT_DIFFS_TOOL_DEFAULTS.background).optional(),
      theme: z.enum(DIFF_THEMES).default(DEFAULT_DIFFS_TOOL_DEFAULTS.theme).optional(),
      fileFormat: z
        .enum(DIFF_OUTPUT_FORMATS)
        .default(DEFAULT_DIFFS_TOOL_DEFAULTS.fileFormat)
        .optional(),
      format: z.enum(DIFF_OUTPUT_FORMATS).optional(),
      fileQuality: z
        .enum(DIFF_IMAGE_QUALITY_PRESETS)
        .default(DEFAULT_DIFFS_TOOL_DEFAULTS.fileQuality)
        .optional(),
      fileScale: z.number().min(1).max(4).default(DEFAULT_DIFFS_TOOL_DEFAULTS.fileScale).optional(),
      fileMaxWidth: z
        .number()
        .min(640)
        .max(2400)
        .default(DEFAULT_DIFFS_TOOL_DEFAULTS.fileMaxWidth)
        .optional(),
      imageFormat: z.enum(DIFF_OUTPUT_FORMATS).optional(),
      imageQuality: z.enum(DIFF_IMAGE_QUALITY_PRESETS).optional(),
      imageScale: z.number().min(1).max(4).optional(),
      imageMaxWidth: z.number().min(640).max(2400).optional(),
      mode: z.enum(DIFF_MODES).default(DEFAULT_DIFFS_TOOL_DEFAULTS.mode).optional(),
    })
    .optional(),
  security: z
    .strictObject({
      allowRemoteViewer: z
        .boolean()
        .default(DEFAULT_DIFFS_PLUGIN_SECURITY.allowRemoteViewer)
        .optional(),
    })
    .optional(),
});

export const diffsPluginConfigSchema: OpenClawPluginConfigSchema = buildPluginConfigSchema(
  DiffsPluginJsonSchemaSource,
  {
    safeParse(value: unknown) {
      if (value === undefined) {
        return { success: true, data: undefined };
      }
      try {
        return { success: true, data: resolveDiffsPluginDefaults(value) };
      } catch (error) {
        return {
          success: false,
          error: {
            issues: [{ path: [], message: error instanceof Error ? error.message : String(error) }],
          },
        };
      }
    },
  },
);

export function resolveDiffsPluginDefaults(config: unknown): DiffToolDefaults {
  if (!config || typeof config !== "object" || Array.isArray(config)) {
    return { ...DEFAULT_DIFFS_TOOL_DEFAULTS };
  }

  const defaults = (config as DiffsPluginConfig).defaults;
  if (!defaults || typeof defaults !== "object" || Array.isArray(defaults)) {
    return { ...DEFAULT_DIFFS_TOOL_DEFAULTS };
  }

  const fileQuality = normalizeFileQuality(defaults.fileQuality ?? defaults.imageQuality);
  const profile = DEFAULT_IMAGE_QUALITY_PROFILES[fileQuality];

  return {
    fontFamily: normalizeFontFamily(defaults.fontFamily),
    fontSize: normalizeFontSize(defaults.fontSize),
    lineSpacing: normalizeLineSpacing(defaults.lineSpacing),
    layout: normalizeLayout(defaults.layout),
    showLineNumbers: defaults.showLineNumbers !== false,
    diffIndicators: normalizeDiffIndicators(defaults.diffIndicators),
    wordWrap: defaults.wordWrap !== false,
    background: defaults.background !== false,
    theme: normalizeTheme(defaults.theme),
    fileFormat: normalizeFileFormat(defaults.fileFormat ?? defaults.imageFormat ?? defaults.format),
    fileQuality,
    fileScale: normalizeFileScale(defaults.fileScale ?? defaults.imageScale, profile.scale),
    fileMaxWidth: normalizeFileMaxWidth(
      defaults.fileMaxWidth ?? defaults.imageMaxWidth,
      profile.maxWidth,
    ),
    mode: normalizeMode(defaults.mode),
  };
}

export function resolveDiffsPluginSecurity(config: unknown): DiffsPluginSecurityConfig {
  if (!config || typeof config !== "object" || Array.isArray(config)) {
    return { ...DEFAULT_DIFFS_PLUGIN_SECURITY };
  }

  const security = (config as DiffsPluginConfig).security;
  if (!security || typeof security !== "object" || Array.isArray(security)) {
    return { ...DEFAULT_DIFFS_PLUGIN_SECURITY };
  }

  return {
    allowRemoteViewer: security.allowRemoteViewer === true,
  };
}

export function toPresentationDefaults(defaults: DiffToolDefaults): DiffPresentationDefaults {
  const {
    fontFamily,
    fontSize,
    lineSpacing,
    layout,
    showLineNumbers,
    diffIndicators,
    wordWrap,
    background,
    theme,
  } = defaults;
  return {
    fontFamily,
    fontSize,
    lineSpacing,
    layout,
    showLineNumbers,
    diffIndicators,
    wordWrap,
    background,
    theme,
  };
}

function normalizeFontFamily(fontFamily?: string): string {
  const normalized = fontFamily?.trim();
  return normalized || DEFAULT_DIFFS_TOOL_DEFAULTS.fontFamily;
}

function normalizeFontSize(fontSize?: number): number {
  if (fontSize === undefined || !Number.isFinite(fontSize)) {
    return DEFAULT_DIFFS_TOOL_DEFAULTS.fontSize;
  }
  const rounded = Math.floor(fontSize);
  return Math.min(Math.max(rounded, 10), 24);
}

function normalizeLineSpacing(lineSpacing?: number): number {
  if (lineSpacing === undefined || !Number.isFinite(lineSpacing)) {
    return DEFAULT_DIFFS_TOOL_DEFAULTS.lineSpacing;
  }
  return Math.min(Math.max(lineSpacing, 1), 3);
}

function normalizeLayout(layout?: DiffLayout): DiffLayout {
  return layout && DIFF_LAYOUTS.includes(layout) ? layout : DEFAULT_DIFFS_TOOL_DEFAULTS.layout;
}

function normalizeDiffIndicators(diffIndicators?: DiffIndicators): DiffIndicators {
  return diffIndicators && DIFF_INDICATORS.includes(diffIndicators)
    ? diffIndicators
    : DEFAULT_DIFFS_TOOL_DEFAULTS.diffIndicators;
}

function normalizeTheme(theme?: DiffTheme): DiffTheme {
  return theme && DIFF_THEMES.includes(theme) ? theme : DEFAULT_DIFFS_TOOL_DEFAULTS.theme;
}

function normalizeFileFormat(fileFormat?: DiffOutputFormat): DiffOutputFormat {
  return fileFormat && DIFF_OUTPUT_FORMATS.includes(fileFormat)
    ? fileFormat
    : DEFAULT_DIFFS_TOOL_DEFAULTS.fileFormat;
}

function normalizeFileQuality(fileQuality?: DiffImageQualityPreset): DiffImageQualityPreset {
  return fileQuality && DIFF_IMAGE_QUALITY_PRESETS.includes(fileQuality)
    ? fileQuality
    : DEFAULT_DIFFS_TOOL_DEFAULTS.fileQuality;
}

function normalizeFileScale(fileScale: number | undefined, fallback: number): number {
  if (fileScale === undefined || !Number.isFinite(fileScale)) {
    return fallback;
  }
  const rounded = Math.round(fileScale * 100) / 100;
  return Math.min(Math.max(rounded, 1), 4);
}

function normalizeFileMaxWidth(fileMaxWidth: number | undefined, fallback: number): number {
  if (fileMaxWidth === undefined || !Number.isFinite(fileMaxWidth)) {
    return fallback;
  }
  const rounded = Math.round(fileMaxWidth);
  return Math.min(Math.max(rounded, 640), 2400);
}

function normalizeMode(mode?: DiffMode): DiffMode {
  return mode && DIFF_MODES.includes(mode) ? mode : DEFAULT_DIFFS_TOOL_DEFAULTS.mode;
}

export function resolveDiffImageRenderOptions(params: {
  defaults: DiffFileDefaults;
  fileFormat?: DiffOutputFormat;
  format?: DiffOutputFormat;
  fileQuality?: DiffImageQualityPreset;
  fileScale?: number;
  fileMaxWidth?: number;
  imageFormat?: DiffOutputFormat;
  imageQuality?: DiffImageQualityPreset;
  imageScale?: number;
  imageMaxWidth?: number;
}): {
  format: DiffOutputFormat;
  qualityPreset: DiffImageQualityPreset;
  scale: number;
  maxWidth: number;
  maxPixels: number;
} {
  const format = normalizeFileFormat(
    params.fileFormat ?? params.imageFormat ?? params.format ?? params.defaults.fileFormat,
  );
  const qualityOverrideProvided =
    params.fileQuality !== undefined || params.imageQuality !== undefined;
  const qualityPreset = normalizeFileQuality(
    params.fileQuality ?? params.imageQuality ?? params.defaults.fileQuality,
  );
  const profile = DEFAULT_IMAGE_QUALITY_PROFILES[qualityPreset];

  const scale = normalizeFileScale(
    params.fileScale ?? params.imageScale,
    qualityOverrideProvided ? profile.scale : params.defaults.fileScale,
  );
  const maxWidth = normalizeFileMaxWidth(
    params.fileMaxWidth ?? params.imageMaxWidth,
    qualityOverrideProvided ? profile.maxWidth : params.defaults.fileMaxWidth,
  );

  return {
    format,
    qualityPreset,
    scale,
    maxWidth,
    maxPixels: profile.maxPixels,
  };
}
