export type NumericSettingKey = 'temperature' | 'topK' | 'topP' | 'repeatPenalty';
export type BooleanSettingKey = 'enableThinking' | 'collapseThinkingByDefault';
export type StringSettingKey = 'baseURL' | 'apiKey';
export type SettingKey = NumericSettingKey | BooleanSettingKey | StringSettingKey;

export interface NumericSetting {
  value: number;
  useDefault: boolean;
}

export interface BooleanSetting {
  value: boolean;
  useDefault: boolean;
}

export interface StringSetting {
  value: string;
  useDefault: boolean;
}

export interface LayoutSettings {
  isChatVisible: boolean;
  isModelManagerVisible: boolean;
  leftPanelView: 'models' | 'marketplace' | 'backends' | 'settings';
  isLogsVisible: boolean;
  modelManagerWidth: number;
  chatWidth: number;
  logsHeight: number;
}

export interface TTSSettings {
  model: StringSetting;
  userVoice: StringSetting;
  assistantVoice: StringSetting;
  enableTTS: BooleanSetting;
  enableUserTTS: BooleanSetting;
}

export interface AppSettings {
  temperature: NumericSetting;
  topK: NumericSetting;
  topP: NumericSetting;
  repeatPenalty: NumericSetting;
  enableThinking: BooleanSetting;
  collapseThinkingByDefault: BooleanSetting;
  baseURL: StringSetting;
  apiKey: StringSetting;
  layout: LayoutSettings;
  tts: TTSSettings;
}

type BaseSettingValues = Record<NumericSettingKey, number> & {
  enableThinking: boolean;
  collapseThinkingByDefault: boolean;
  baseURL: string;
  apiKey: string;
};

export const BASE_SETTING_VALUES: BaseSettingValues = {
  temperature: 0.7,
  topK: 40,
  topP: 0.9,
  repeatPenalty: 1.1,
  enableThinking: true,
  collapseThinkingByDefault: false,
  baseURL: '',
  apiKey: '',
};

export const NUMERIC_SETTING_LIMITS: Record<NumericSettingKey, { min: number; max: number; step: number }> = {
  temperature: { min: 0, max: 2, step: 0.1 },
  topK: { min: 1, max: 100, step: 1 },
  topP: { min: 0, max: 1, step: 0.01 },
  repeatPenalty: { min: 1, max: 2, step: 0.1 },
};

const numericSettingKeys: NumericSettingKey[] = ['temperature', 'topK', 'topP', 'repeatPenalty'];

export const DEFAULT_LAYOUT_SETTINGS: LayoutSettings = {
  isChatVisible: true,
  isModelManagerVisible: true,
  leftPanelView: 'models',
  isLogsVisible: false,
  modelManagerWidth: 280,
  chatWidth: 350,
  logsHeight: 200,
};

export const DEFAULT_TTS_SETTINGS: TTSSettings = {
  model: { value: 'kokoro-v1', useDefault: true },
  userVoice: { value: 'fable', useDefault: true },
  assistantVoice: { value: 'alloy', useDefault: true },
  enableTTS: { value: false, useDefault: true },
  enableUserTTS: { value: false, useDefault: true }
};

export const createDefaultSettings = (): AppSettings => ({
  temperature: { value: BASE_SETTING_VALUES.temperature, useDefault: true },
  topK: { value: BASE_SETTING_VALUES.topK, useDefault: true },
  topP: { value: BASE_SETTING_VALUES.topP, useDefault: true },
  repeatPenalty: { value: BASE_SETTING_VALUES.repeatPenalty, useDefault: true },
  enableThinking: { value: BASE_SETTING_VALUES.enableThinking, useDefault: true },
  collapseThinkingByDefault: { value: BASE_SETTING_VALUES.collapseThinkingByDefault, useDefault: true },
  baseURL: { value: BASE_SETTING_VALUES.baseURL, useDefault: true },
  apiKey: { value: BASE_SETTING_VALUES.apiKey, useDefault: true },
  layout: { ...DEFAULT_LAYOUT_SETTINGS },
  tts: {...DEFAULT_TTS_SETTINGS}
});

export const cloneSettings = (settings: AppSettings): AppSettings => ({
  temperature: { ...settings.temperature },
  topK: { ...settings.topK },
  topP: { ...settings.topP },
  repeatPenalty: { ...settings.repeatPenalty },
  enableThinking: { ...settings.enableThinking },
  collapseThinkingByDefault: { ...settings.collapseThinkingByDefault },
  baseURL: { ...settings.baseURL },
  apiKey: { ...settings.apiKey },
  layout: { ...settings.layout },
  tts: { ...settings.tts },
});

export const clampNumericSettingValue = (key: NumericSettingKey, value: number): number => {
  const { min, max } = NUMERIC_SETTING_LIMITS[key];

  if (!Number.isFinite(value)) {
    return BASE_SETTING_VALUES[key];
  }

  return Math.min(Math.max(value, min), max);
};

export const mergeWithDefaultSettings = (incoming?: Partial<AppSettings>): AppSettings => {
  const defaults = createDefaultSettings();

  if (!incoming) {
    return defaults;
  }

  numericSettingKeys.forEach((key) => {
    const rawSetting = incoming[key];
    if (!rawSetting || typeof rawSetting !== 'object') {
      return;
    }

    const useDefault =
      typeof rawSetting.useDefault === 'boolean'
        ? rawSetting.useDefault
        : defaults[key].useDefault;
    const numericValue = useDefault
      ? defaults[key].value
      : typeof rawSetting.value === 'number'
        ? clampNumericSettingValue(key, rawSetting.value)
        : defaults[key].value;

    defaults[key] = {
      value: numericValue,
      useDefault,
    };
  });

  const rawEnableThinking = incoming.enableThinking;
  if (rawEnableThinking && typeof rawEnableThinking === 'object') {
    const useDefault =
      typeof rawEnableThinking.useDefault === 'boolean'
        ? rawEnableThinking.useDefault
        : defaults.enableThinking.useDefault;
    const value = useDefault
      ? defaults.enableThinking.value
      : typeof rawEnableThinking.value === 'boolean'
        ? rawEnableThinking.value
        : defaults.enableThinking.value;

    defaults.enableThinking = {
      value,
      useDefault,
    };
  }

  const rawCollapseThinkingByDefault = incoming.collapseThinkingByDefault;
  if (rawCollapseThinkingByDefault && typeof rawCollapseThinkingByDefault === 'object') {
    const useDefault =
      typeof rawCollapseThinkingByDefault.useDefault === 'boolean'
        ? rawCollapseThinkingByDefault.useDefault
        : defaults.collapseThinkingByDefault.useDefault;
    const value = useDefault
      ? defaults.collapseThinkingByDefault.value
      : typeof rawCollapseThinkingByDefault.value === 'boolean'
        ? rawCollapseThinkingByDefault.value
        : defaults.collapseThinkingByDefault.value;

    defaults.collapseThinkingByDefault = {
      value,
      useDefault,
    };
  }

  const rawBaseURL = incoming.baseURL;
  if (rawBaseURL && typeof rawBaseURL === 'object') {
    const useDefault =
      typeof rawBaseURL.useDefault === 'boolean'
        ? rawBaseURL.useDefault
        : defaults.baseURL.useDefault;
    const value = useDefault
      ? defaults.baseURL.value
      : typeof rawBaseURL.value === 'string'
        ? rawBaseURL.value
        : defaults.baseURL.value;

    defaults.baseURL = {
      value,
      useDefault,
    };
  }

  const rawApiKey = incoming.apiKey;
  if (rawApiKey && typeof rawApiKey === 'object') {
    const useDefault =
      typeof rawApiKey.useDefault === 'boolean'
        ? rawApiKey.useDefault
        : defaults.apiKey.useDefault;
    const value = useDefault
      ? defaults.apiKey.value
      : typeof rawApiKey.value === 'string'
        ? rawApiKey.value
        : defaults.apiKey.value;

    defaults.apiKey = {
      value,
      useDefault,
    };
  }

  // Merge layout settings
  const rawLayout = incoming.layout;
  if (rawLayout && typeof rawLayout === 'object') {
    // Merge boolean visibility settings
    if (typeof rawLayout.isChatVisible === 'boolean') {
      defaults.layout.isChatVisible = rawLayout.isChatVisible;
    }
    if (typeof rawLayout.isModelManagerVisible === 'boolean') {
      defaults.layout.isModelManagerVisible = rawLayout.isModelManagerVisible;
    }
    if (rawLayout.leftPanelView === 'models' || rawLayout.leftPanelView === 'marketplace' || rawLayout.leftPanelView === 'backends' || rawLayout.leftPanelView === 'settings') {
      defaults.layout.leftPanelView = rawLayout.leftPanelView;
    }
    if (typeof rawLayout.isLogsVisible === 'boolean') {
      defaults.layout.isLogsVisible = rawLayout.isLogsVisible;
    }
    // Merge numeric size settings
    if (typeof rawLayout.modelManagerWidth === 'number') {
      defaults.layout.modelManagerWidth = rawLayout.modelManagerWidth;
    }
    if (typeof rawLayout.chatWidth === 'number') {
      defaults.layout.chatWidth = rawLayout.chatWidth;
    }
    if (typeof rawLayout.logsHeight === 'number') {
      defaults.layout.logsHeight = rawLayout.logsHeight;
    }
  }

  // Merge TTS settings
  const rawTTS = incoming.tts;
  if (rawTTS && typeof rawTTS === 'object') {
    const ttsKeys = Object.keys(rawTTS) as (keyof TTSSettings)[];

    ttsKeys.forEach((key) => {
      if (rawTTS[key] && typeof rawTTS[key] === 'object') {
        const useDefault = (typeof rawTTS[key].useDefault === 'boolean') ? rawTTS[key].useDefault : defaults.tts[key].useDefault;
        const value = useDefault ? defaults.tts[key].value : (typeof rawTTS[key].value === 'string' || typeof rawTTS[key].value === 'boolean') ? rawTTS[key].value : defaults.tts[key].value;
        (defaults.tts[key] as (StringSetting | BooleanSetting)) = { value, useDefault };
      }
    });
  }

  return defaults;
};

export const buildChatRequestOverrides = (settings?: AppSettings | null): Record<string, number | boolean> => {
  if (!settings) {
    return {};
  }

  const overrides: Record<string, number | boolean> = {};

  if (!settings.temperature.useDefault) {
    overrides.temperature = Number(settings.temperature.value.toFixed(4));
  }

  if (!settings.topK.useDefault) {
    overrides.top_k = Math.round(settings.topK.value);
  }

  if (!settings.topP.useDefault) {
    overrides.top_p = Number(settings.topP.value.toFixed(4));
  }

  if (!settings.repeatPenalty.useDefault) {
    overrides.repeat_penalty = Number(settings.repeatPenalty.value.toFixed(4));
  }

  if (!settings.enableThinking.useDefault) {
    overrides.enable_thinking = settings.enableThinking.value;
  }

  return overrides;
};
