/**
 * Copyright (c) Streamlit Inc. (2018-2022) Snowflake Inc. (2022-2026)
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

import {
  createTheme as createBaseTheme,
  lightThemePrimitives as lightBaseThemePrimitives,
  Primitives as ThemePrimitives,
} from "baseui"
import { Theme as BaseTheme } from "baseui/theme"
import { transparentize } from "color2k"

import { EmotionTheme } from "./types"

/**
 * Creates theme primitives for the BaseUI theme.
 *
 * See lightThemePrimitives for what's available. These are used to create a
 * large JSON-style structure with theme values for all widgets.
 * - See node_modules/baseui/themes/light-theme-primitives.js for an example
 * of primitives we can use here.
 *
 * - See node_modules/baseui/themes/creator.js for the mapping of values from
 * this file to output values.
 *
 * @param baseTheme: The base theme primitives.
 * @param theme: The emotion theme to use.
 * @returns the theme primitives
 */
const createBaseUiThemePrimitives = (
  baseTheme: ThemePrimitives,
  theme: EmotionTheme
): ThemePrimitives => {
  const { colors, genericFonts } = theme

  return {
    ...baseTheme,

    primaryFontFamily: genericFonts.bodyFont,

    primary100: colors.primary,
    primary200: colors.primary,
    primary300: colors.primary,
    primary400: colors.primary,
    primary500: colors.primary,
    primary600: colors.primary,
    primary700: colors.primary,

    // Override gray values based on what is actually used in BaseWeb, and the
    // way we want it to match our theme originating from Bootstrap.
    mono100: colors.bgColor, // Popup menu
    mono200: colors.secondaryBg, // Text input, text area, selectbox
    mono300: colors.gray30, // Disabled widget background
    mono400: colors.gray30, // Slider track
    mono500: colors.gray60, // Clicked checkbox and radio
    mono600: colors.fadedText40, // Disabled widget text
    mono700: colors.gray60, // Unselected checkbox and radio
    mono800: colors.bodyText, // Selectbox text
    mono900: colors.bodyText, // Not used, but just in case.
    mono1000: colors.black,
  }
}

/**
 * Creates theme overrides for the BaseUI theme.
 *
 * NOTE: A lot of the properties we can override here don't seem to actually
 * be used anywhere in BaseWeb's source.
 *
 * @param theme: The emotion theme to use.
 * @returns the theme overrides
 */
const createBaseUiThemeOverrides = (
  theme: EmotionTheme
  // eslint-disable-next-line @typescript-eslint/no-explicit-any -- TODO: Replace 'any' with a more specific type.
): Record<string, any> => {
  const { inSidebar, colors, genericFonts, fontSizes, lineHeights, radii } =
    theme

  const fontStyles = {
    fontFamily: genericFonts.bodyFont,
    fontSize: fontSizes.md,
    fontSizeSm: fontSizes.sm,
    fontWeight: "normal",
    lineHeight: lineHeights.base,
    lineHeightTight: lineHeights.tight,
  }

  const widgetBackgroundColor = colors.secondaryBg

  // We want menuFill to always use bgColor. But when in sidebar, bgColor and secondaryBg are
  // swapped! So here we unswap them.
  const mainPaneBgColor = inSidebar ? colors.secondaryBg : colors.bgColor
  const mainPaneSecondaryBgColor = inSidebar
    ? colors.bgColor
    : colors.secondaryBg

  return {
    borders: {
      // Override borders that are declared from literals in
      // https://github.com/uber/baseweb/blob/master/src/themes/shared/borders.ts

      radius100: radii.default,
      radius200: radii.default,
      radius300: radii.default,
      radius400: radii.default,
      radius500: radii.default,

      /** Datepicker (Range), Progress Bar, Slider, Tag */
      useRoundedCorners: true,
      /** Button, ButtonGroup */
      buttonBorderRadiusMini: radii.md, // Unused today.
      buttonBorderRadius: radii.default,
      /** Checkbox */
      checkboxBorderRadius: `min(${radii.md}, ${radii.maxCheckbox})`,
      /** Input, Select, Textarea */
      inputBorderRadiusMini: radii.md, // Unused today.
      inputBorderRadius: radii.default,
      /** Popover, Menu, Tooltip */
      popoverBorderRadius: radii.default,
      /** Card, Datepicker, Modal, Toast, Notification */
      surfaceBorderRadius: radii.default,
      /** Tag */
      tagBorderRadius: radii.md,
    },
    typography: {
      // Here we override some fonts that are used in widgets. We don't care
      // about the ones that are not used.
      font100: {},
      font150: { ...fontStyles }, // Popup menus
      font200: {},
      font250: {},
      font300: { ...fontStyles }, // Popup menus
      font350: { ...fontStyles }, // Checkbox
      font400: { ...fontStyles }, // Textinput, textarea, selectboxes
      font450: { ...fontStyles }, // Radio
      font460: { ...fontStyles }, // Calendar header buttons
      font470: { ...fontStyles }, // Button
      font500: { ...fontStyles }, // Selected items in selectbox
      font600: {},

      LabelXSmall: { ...fontStyles },
      LabelSmall: { ...fontStyles },
      LabelMedium: { ...fontStyles },
      LabelLarge: { ...fontStyles },
      ParagraphSmall: { ...fontStyles },
    },

    colors: {
      white: colors.white,
      black: colors.black,
      primary: colors.primary,
      primaryA: colors.primary,
      backgroundPrimary: colors.bgColor,
      backgroundSecondary: widgetBackgroundColor,
      backgroundTertiary: colors.bgColor,
      borderOpaque: colors.darkenedBgMix25,
      accent: transparentize(colors.primary, 0.5),
      tagPrimarySolidBackground: colors.primary,
      tagPrimaryFontDisabled: colors.fadedText40,
      tagPrimaryOutlinedDisabled: colors.transparent,
      borderSelected: colors.primary,
      contentPrimary: colors.bodyText,
      inputPlaceholder: colors.fadedText60,
      tickFillDisabled: colors.fadedText40,
      tickMarkFill: colors.gray20,
      tickFillSelected: colors.primary,
      datepickerBackground: mainPaneBgColor,
      calendarBackground: mainPaneBgColor,
      calendarForeground: colors.bodyText,
      calendarDayForegroundPseudoSelected: colors.bodyText,
      calendarHeaderBackground: mainPaneSecondaryBgColor,
      calendarHeaderBackgroundActive: mainPaneSecondaryBgColor,
      calendarHeaderForeground: colors.bodyText,
      calendarHeaderForegroundDisabled: colors.gray40,
      calendarDayBackgroundSelected: colors.primary,
      calendarDayBackgroundSelectedHighlighted: colors.primary,
      calendarDayForegroundSelected: colors.white,
      calendarDayForegroundSelectedHighlighted: colors.white,
      calendarDayForegroundPseudoSelectedHighlighted: colors.bodyText,
      menuFontHighlighted: colors.bodyText,
      menuFontSelected: colors.bodyText,

      modalCloseColor: colors.bodyText,

      notificationInfoBackground: colors.blueBackgroundColor,
      notificationInfoText: colors.blueTextColor,
      notificationPositiveBackground: colors.greenBackgroundColor,
      notificationPositiveText: colors.greenTextColor,
      notificationWarningBackground: colors.yellowBackgroundColor,
      notificationWarningText: colors.yellowTextColor,
      notificationNegativeBackground: colors.redBackgroundColor,
      notificationNegativeText: colors.redTextColor,
      progressbarTrackFill: widgetBackgroundColor,

      // mono100 overrides
      tickFill: colors.lightenedBg05, // Checkbox and Radio
      tickMarkFillDisabled: colors.lightenedBg05,
      // We want menuFill to always use bgColor. But when in sidebar, bgColor and secondaryBg are
      // swapped! So here we unswap them.
      menuFill: mainPaneBgColor,

      // mono200 overrides
      buttonDisabledFill: colors.lightenedBg05,
      tickFillHover: widgetBackgroundColor,
      inputFillDisabled: widgetBackgroundColor,
      inputFillActive: widgetBackgroundColor,

      // mono300 overrides
      toggleTrackFillDisabled: widgetBackgroundColor,
      tickFillActive: widgetBackgroundColor,
      sliderTrackFillDisabled: widgetBackgroundColor,
      inputBorder: colors.widgetBorderColor || widgetBackgroundColor,
      inputFill: widgetBackgroundColor,
      inputEnhanceFill: widgetBackgroundColor,
      inputEnhancerFillDisabled: widgetBackgroundColor,

      // mono400 overrides
      buttonDisabledSpinnerBackground: colors.gray40,
      toggleTrackFill: colors.gray40,
      sliderTrackFill: colors.gray40,
      sliderHandleInnerFill: colors.gray40,
      sliderHandleInnerFillDisabled: colors.gray40,
    },
  }
}

/**
 * Creates the BaseUI theme based on an emotion theme.
 *
 * @param theme: The emotion theme to use.
 * @param primitives: The primitives to use.
 * @returns the BaseUI theme
 */
export const createBaseUiTheme = (
  theme: EmotionTheme,
  primitives = lightBaseThemePrimitives
  // eslint-disable-next-line @typescript-eslint/no-explicit-any -- TODO: Replace 'any' with a more specific type.
): BaseTheme & Record<string, any> =>
  createBaseTheme(
    createBaseUiThemePrimitives(primitives, theme),
    createBaseUiThemeOverrides(theme)
  )
