import {
  DEFAULT_CURRENCY,
  DEFAULT_FONT_SIZE,
  DEFAULT_VERTICAL_ALIGN,
  DEFAULT_WRAPPING_MODE,
  FONT_SIZES,
  ROTATION_EPSILON,
} from "../constants";
import { parseLiteral } from "../helpers/cells/cell_evaluation";
import {
  createAccountingFormat,
  createCurrencyFormat,
  formatValue,
  roundFormat,
} from "../helpers/format/format";
import { getDateTimeFormat } from "../helpers/locale";
import { _t } from "../translation";
import { CellValue } from "../types/cells";
import { Format } from "../types/format";
import { DEFAULT_LOCALE } from "../types/locale";
import { Align, VerticalAlign, Wrapping } from "../types/misc";
import { SpreadsheetChildEnv } from "../types/spreadsheet_env";
import { ActionSpec } from "./action";
import * as ACTIONS from "./menu_items_actions";
import { setFormatter, setStyle } from "./menu_items_actions";

export interface NumberFormatActionSpec extends ActionSpec {
  format?: Format | ((env: SpreadsheetChildEnv) => Format);
}

/**
 * Create a format action specification for a given format.
 * The format can be dynamically computed from the environment.
 */
export function createFormatActionSpec({
  name,
  format,
  descriptionValue,
}: {
  name: string;
  descriptionValue: CellValue;
  format: Format | ((env: SpreadsheetChildEnv) => Format);
}): NumberFormatActionSpec {
  const formatCallback = typeof format === "function" ? format : () => format;
  return {
    name,
    description: (env) =>
      formatValue(descriptionValue, {
        format: formatCallback(env),
        locale: env.model.getters.getLocale(),
      }),
    execute: (env) => setFormatter(env.model, formatCallback(env)),
    isActive: (env) => isFormatSelected(env, formatCallback(env)),
    format,
  };
}

export const formatNumberAutomatic: NumberFormatActionSpec = {
  name: _t("Automatic"),
  execute: (env) => setFormatter(env.model, ""),
  isActive: (env) => isAutomaticFormatSelected(env),
};

export const formatNumberPlainText: NumberFormatActionSpec = {
  name: _t("Plain text"),
  execute: (env) => setFormatter(env.model, "@"),
  isActive: (env) => isFormatSelected(env, "@"),
};

export const formatNumberNumber = createFormatActionSpec({
  name: _t("Number"),
  descriptionValue: 1000.12,
  format: "#,##0.00",
});

export const formatPercent: ActionSpec = {
  name: _t("Format as percent"),
  execute: ACTIONS.FORMAT_PERCENT_ACTION,
  icon: "o-spreadsheet-Icon.PERCENT",
};

export const formatNumberPercent = createFormatActionSpec({
  name: _t("Percent"),
  descriptionValue: 0.1012,
  format: "0.00%",
});

export const formatNumberScientific = createFormatActionSpec({
  name: _t("Scientific"),
  descriptionValue: 0.1012,
  format: "0.00e",
});

export const formatNumberCurrency = createFormatActionSpec({
  name: _t("Currency"),
  descriptionValue: 1000.12,
  format: (env) => createCurrencyFormat(env.model.config.defaultCurrency || DEFAULT_CURRENCY),
});

export const formatNumberCurrencyRounded: NumberFormatActionSpec = {
  ...createFormatActionSpec({
    name: _t("Currency rounded"),
    descriptionValue: 1000,
    format: (env) =>
      roundFormat(createCurrencyFormat(env.model.config.defaultCurrency || DEFAULT_CURRENCY)),
  }),
  isVisible: (env) => {
    const currencyFormat = createCurrencyFormat(
      env.model.config.defaultCurrency || DEFAULT_CURRENCY
    );
    const roundedFormat = roundFormat(currencyFormat);
    return currencyFormat !== roundedFormat;
  },
};

export const formatNumberAccounting = createFormatActionSpec({
  name: _t("Accounting"),
  descriptionValue: -1000.12,
  format: (env) => createAccountingFormat(env.model.config.defaultCurrency || DEFAULT_CURRENCY),
});

export const EXAMPLE_DATE = parseLiteral("2023/09/26 10:43:00 PM", DEFAULT_LOCALE);

export const formatCustomCurrency: ActionSpec = {
  name: _t("Custom currency"),
  isVisible: (env) => env.loadCurrencies !== undefined && !env.isSmall,
  execute: (env) => env.openSidePanel("MoreFormats", { category: "currency" }),
};

export const formatNumberDate = createFormatActionSpec({
  name: _t("Date"),
  descriptionValue: EXAMPLE_DATE,
  format: (env) => env.model.getters.getLocale().dateFormat,
});

export const formatNumberTime = createFormatActionSpec({
  name: _t("Time"),
  descriptionValue: EXAMPLE_DATE,
  format: (env) => env.model.getters.getLocale().timeFormat,
});

export const formatNumberDateTime = createFormatActionSpec({
  name: _t("Date time"),
  descriptionValue: EXAMPLE_DATE,
  format: (env) => {
    const locale = env.model.getters.getLocale();
    return getDateTimeFormat(locale);
  },
});

export const formatNumberDuration = createFormatActionSpec({
  name: _t("Duration"),
  descriptionValue: "27:51:38",
  format: "hhhh:mm:ss",
});

export const customDateFormat: ActionSpec = {
  name: _t("Custom date and time"),
  isVisible: (env) => !env.isSmall,
  execute: (env) => env.openSidePanel("MoreFormats", { category: "date" }),
};

export const customNumberFormat: ActionSpec = {
  name: _t("Custom number format"),
  isVisible: (env) => !env.isSmall,
  execute: (env) => env.openSidePanel("MoreFormats", { category: "number" }),
};

export const formatNumberFullDateTime = createFormatActionSpec({
  name: _t("Full date time"),
  format: "dddd d mmmm yyyy hh:mm:ss a",
  descriptionValue: EXAMPLE_DATE,
});

export const increaseDecimalPlaces: ActionSpec = {
  name: _t("Increase decimal places"),
  icon: "o-spreadsheet-Icon.INCREASE_DECIMAL",
  execute: (env) =>
    env.model.dispatch("SET_DECIMAL", {
      sheetId: env.model.getters.getActiveSheetId(),
      target: env.model.getters.getSelectedZones(),
      step: 1,
    }),
};

export const decreaseDecimalPlaces: ActionSpec = {
  name: _t("Decrease decimal places"),
  icon: "o-spreadsheet-Icon.DECRASE_DECIMAL",
  execute: (env) =>
    env.model.dispatch("SET_DECIMAL", {
      sheetId: env.model.getters.getActiveSheetId(),
      target: env.model.getters.getSelectedZones(),
      step: -1,
    }),
};

export const formatBold: ActionSpec = {
  name: _t("Bold"),
  shortcut: "Ctrl+B",
  execute: (env) => setStyle(env.model, { bold: !env.model.getters.getCurrentStyle().bold }),
  icon: "o-spreadsheet-Icon.BOLD",
  isActive: (env) => !!env.model.getters.getCurrentStyle().bold,
};

export const formatItalic: ActionSpec = {
  name: _t("Italic"),
  shortcut: "Ctrl+I",
  execute: (env) => setStyle(env.model, { italic: !env.model.getters.getCurrentStyle().italic }),
  icon: "o-spreadsheet-Icon.ITALIC",
  isActive: (env) => !!env.model.getters.getCurrentStyle().italic,
};

export const formatUnderline: ActionSpec = {
  name: _t("Underline"),
  shortcut: "Ctrl+U",
  execute: (env) =>
    setStyle(env.model, { underline: !env.model.getters.getCurrentStyle().underline }),
  icon: "o-spreadsheet-Icon.UNDERLINE",
  isActive: (env) => !!env.model.getters.getCurrentStyle().underline,
};

export const formatRotation: ActionSpec = {
  name: _t("Rotation"),
  icon: (env) => getRotationIcon(env),
};

function setRotation(env: SpreadsheetChildEnv, rotation: number) {
  rotation = Math.trunc(rotation / ROTATION_EPSILON) * ROTATION_EPSILON;
  setStyle(env.model, { rotation });
}

function currentRotationEqual(env: SpreadsheetChildEnv, rotation: number): boolean {
  const current = env.model.getters.getCurrentStyle().rotation;
  if (current === undefined) {
    return rotation === 0;
  }
  return Math.abs(current - rotation) < ROTATION_EPSILON;
}

export const formatNoRotation: ActionSpec = {
  name: _t("No rotation"),
  execute: (env) => setStyle(env.model, { rotation: 0 }),
  icon: "o-spreadsheet-Icon.ROTATION-0",
  isActive: (env) => currentRotationEqual(env, 0),
};

export const formatRotation45: ActionSpec = {
  name: _t("45° rotation"),
  execute: (env) => setRotation(env, Math.PI / 4),
  icon: "o-spreadsheet-Icon.ROTATION-45",
  isActive: (env) => currentRotationEqual(env, Math.PI / 4),
};

export const formatRotation90: ActionSpec = {
  name: _t("90° rotation"),
  execute: (env) => setRotation(env, Math.PI / 2),
  icon: "o-spreadsheet-Icon.ROTATION-90",
  isActive: (env) => currentRotationEqual(env, Math.PI / 2),
};

export const formatRotation270: ActionSpec = {
  name: _t("-90° rotation"),
  execute: (env) => setRotation(env, -Math.PI / 2),
  icon: "o-spreadsheet-Icon.ROTATION-270",
  isActive: (env) => currentRotationEqual(env, -Math.PI / 2),
};

export const formatRotation315: ActionSpec = {
  name: _t("-45° rotation"),
  execute: (env) => setRotation(env, -Math.PI / 4),
  icon: "o-spreadsheet-Icon.ROTATION-315",
  isActive: (env) => currentRotationEqual(env, -Math.PI / 4),
};

export const formatStrikethrough: ActionSpec = {
  name: _t("Strikethrough"),
  execute: (env) =>
    setStyle(env.model, { strikethrough: !env.model.getters.getCurrentStyle().strikethrough }),
  icon: "o-spreadsheet-Icon.STRIKE",
  isActive: (env) => !!env.model.getters.getCurrentStyle().strikethrough,
};

export const formatFontSize: ActionSpec = {
  name: _t("Font size"),
  children: fontSizeMenuBuilder(),
  icon: "o-spreadsheet-Icon.FONT_SIZE",
};

export const formatAlignment: ActionSpec = {
  name: _t("Alignment"),
  icon: "o-spreadsheet-Icon.ALIGN_LEFT",
};

export const formatAlignmentHorizontal: ActionSpec = {
  name: _t("Horizontal align"),
  icon: (env) => getHorizontalAlignmentIcon(env),
};

export const formatAlignmentLeft: ActionSpec = {
  name: _t("Left"),
  shortcut: "Ctrl+Shift+L",
  execute: (env) => ACTIONS.setStyle(env.model, { align: "left" }),
  isActive: (env) => getHorizontalAlign(env) === "left",
  icon: "o-spreadsheet-Icon.ALIGN_LEFT",
};

export const formatAlignmentCenter: ActionSpec = {
  name: _t("Center"),
  shortcut: "Ctrl+Shift+E",
  execute: (env) => ACTIONS.setStyle(env.model, { align: "center" }),
  isActive: (env) => getHorizontalAlign(env) === "center",
  icon: "o-spreadsheet-Icon.ALIGN_CENTER",
};

export const formatAlignmentRight: ActionSpec = {
  name: _t("Right"),
  shortcut: "Ctrl+Shift+R",
  execute: (env) => ACTIONS.setStyle(env.model, { align: "right" }),
  isActive: (env) => getHorizontalAlign(env) === "right",
  icon: "o-spreadsheet-Icon.ALIGN_RIGHT",
};

export const formatAlignmentVertical: ActionSpec = {
  name: _t("Vertical align"),
  icon: (env) => getVerticalAlignmentIcon(env),
};

export const formatAlignmentTop: ActionSpec = {
  name: _t("Top"),
  execute: (env) => ACTIONS.setStyle(env.model, { verticalAlign: "top" }),
  isActive: (env) => getVerticalAlign(env) === "top",
  icon: "o-spreadsheet-Icon.ALIGN_TOP",
};

export const formatAlignmentMiddle: ActionSpec = {
  name: _t("Middle"),
  execute: (env) => ACTIONS.setStyle(env.model, { verticalAlign: "middle" }),
  isActive: (env) => getVerticalAlign(env) === "middle",
  icon: "o-spreadsheet-Icon.ALIGN_MIDDLE",
};

export const formatAlignmentBottom: ActionSpec = {
  name: _t("Bottom"),
  execute: (env) => ACTIONS.setStyle(env.model, { verticalAlign: "bottom" }),
  isActive: (env) => getVerticalAlign(env) === "bottom",
  icon: "o-spreadsheet-Icon.ALIGN_BOTTOM",
};

export const formatWrappingIcon: ActionSpec = {
  name: _t("Wrapping"),
  icon: "o-spreadsheet-Icon.WRAPPING_OVERFLOW",
};

export const formatWrapping: ActionSpec = {
  name: _t("Wrapping"),
  icon: (env) => getWrapModeIcon(env),
};

export const formatWrappingOverflow: ActionSpec = {
  name: _t("Overflow"),
  execute: (env) => ACTIONS.setStyle(env.model, { wrapping: "overflow" }),
  isActive: (env) => getWrappingMode(env) === "overflow",
  icon: "o-spreadsheet-Icon.WRAPPING_OVERFLOW",
};

export const formatWrappingWrap: ActionSpec = {
  name: _t("Wrap"),
  execute: (env) => ACTIONS.setStyle(env.model, { wrapping: "wrap" }),
  isActive: (env) => getWrappingMode(env) === "wrap",
  icon: "o-spreadsheet-Icon.WRAPPING_WRAP",
};

export const formatWrappingClip: ActionSpec = {
  name: _t("Clip"),
  execute: (env) => ACTIONS.setStyle(env.model, { wrapping: "clip" }),
  isActive: (env) => getWrappingMode(env) === "clip",
  icon: "o-spreadsheet-Icon.WRAPPING_CLIP",
};

export const textColor: ActionSpec = {
  name: _t("Text Color"),
  icon: "o-spreadsheet-Icon.TEXT_COLOR",
};

export const fillColor: ActionSpec = {
  name: _t("Fill Color"),
  icon: "o-spreadsheet-Icon.FILL_COLOR",
};

export const formatCF: ActionSpec = {
  name: _t("Conditional formatting"),
  execute: ACTIONS.OPEN_CF_SIDEPANEL_ACTION,
  isEnabled: (env) => !env.isSmall,
  isEnabledOnLockedSheet: true,
  icon: "o-spreadsheet-Icon.CONDITIONAL_FORMAT",
};

export const clearFormat: ActionSpec = {
  name: _t("Clear formatting"),
  shortcut: "Ctrl+<",
  execute: (env) =>
    env.model.dispatch("CLEAR_FORMATTING", {
      sheetId: env.model.getters.getActiveSheetId(),
      target: env.model.getters.getSelectedZones(),
    }),

  icon: "o-spreadsheet-Icon.CLEAR_FORMAT",
};

function fontSizeMenuBuilder(): ActionSpec[] {
  return FONT_SIZES.map((fs) => {
    return {
      name: fs.toString(),
      sequence: fs,
      id: `font_size_${fs}`,
      execute: (env) => ACTIONS.setStyle(env.model, { fontSize: fs }),
      isActive: (env) => isFontSizeSelected(env, fs),
    };
  });
}

function isAutomaticFormatSelected(env: SpreadsheetChildEnv): boolean {
  const activePosition = env.model.getters.getActivePosition();
  const pivotCell = env.model.getters.getPivotCellFromPosition(activePosition);
  if (pivotCell.type === "VALUE") {
    return !env.model.getters.getEvaluatedCell(activePosition).format;
  }
  return !env.model.getters.getCell(activePosition)?.format;
}

function isFormatSelected(env: SpreadsheetChildEnv, format: string): boolean {
  const activePosition = env.model.getters.getActivePosition();
  const pivotCell = env.model.getters.getPivotCellFromPosition(activePosition);
  if (pivotCell.type === "VALUE") {
    return env.model.getters.getEvaluatedCell(activePosition).format === format;
  }
  return env.model.getters.getCell(activePosition)?.format === format;
}

function isFontSizeSelected(env: SpreadsheetChildEnv, fontSize: number): boolean {
  const currentFontSize = env.model.getters.getCurrentStyle().fontSize || DEFAULT_FONT_SIZE;
  return currentFontSize === fontSize;
}

function getHorizontalAlign(env: SpreadsheetChildEnv): Align {
  const style = env.model.getters.getCurrentStyle();
  if (style.align && style.align !== "default") {
    return style.align;
  }
  const cell = env.model.getters.getActiveCell();
  return cell.defaultAlign;
}

function getVerticalAlign(env: SpreadsheetChildEnv): VerticalAlign {
  const style = env.model.getters.getCurrentStyle();
  if (style.verticalAlign) {
    return style.verticalAlign;
  }
  return DEFAULT_VERTICAL_ALIGN;
}

function getWrappingMode(env: SpreadsheetChildEnv): Wrapping {
  const style = env.model.getters.getCurrentStyle();
  if (style.wrapping) {
    return style.wrapping;
  }
  return DEFAULT_WRAPPING_MODE;
}

function getHorizontalAlignmentIcon(env: SpreadsheetChildEnv) {
  const horizontalAlign = getHorizontalAlign(env);

  switch (horizontalAlign) {
    case "right":
      return "o-spreadsheet-Icon.ALIGN_RIGHT";
    case "center":
      return "o-spreadsheet-Icon.ALIGN_CENTER";
    default:
      return "o-spreadsheet-Icon.ALIGN_LEFT";
  }
}

function getVerticalAlignmentIcon(env: SpreadsheetChildEnv) {
  const verticalAlign = getVerticalAlign(env);

  switch (verticalAlign) {
    case "top":
      return "o-spreadsheet-Icon.ALIGN_TOP";
    case "middle":
      return "o-spreadsheet-Icon.ALIGN_MIDDLE";
    default:
      return "o-spreadsheet-Icon.ALIGN_BOTTOM";
  }
}

function getWrapModeIcon(env: SpreadsheetChildEnv) {
  const wrapMode = getWrappingMode(env);

  switch (wrapMode) {
    case "wrap":
      return "o-spreadsheet-Icon.WRAPPING_WRAP";
    case "clip":
      return "o-spreadsheet-Icon.WRAPPING_CLIP";
    default:
      return "o-spreadsheet-Icon.WRAPPING_OVERFLOW";
  }
}

function getRotationIcon(env: SpreadsheetChildEnv) {
  if (currentRotationEqual(env, Math.PI / 2)) {
    return "o-spreadsheet-Icon.ROTATION-90";
  } else if (currentRotationEqual(env, -Math.PI / 2)) {
    return "o-spreadsheet-Icon.ROTATION-270";
  } else if (currentRotationEqual(env, Math.PI / 4)) {
    return "o-spreadsheet-Icon.ROTATION-45";
  } else if (currentRotationEqual(env, -Math.PI / 4)) {
    return "o-spreadsheet-Icon.ROTATION-315";
  }
  return "o-spreadsheet-Icon.ROTATION-0";
}
