import { Color } from './color';

export type Appearance = 'dark' | 'light' | 'system';

interface ThemeComponent {
  background?: Color;
  backgroundHighlight?: Color;
  backgroundHighlightSecondary?: Color;
  backgroundActive?: Color;
  foreground?: Color;
  foregroundSubtle?: Color;
  foregroundSubtler?: Color;
  shadow?: Color;
  colors?: Partial<RootColors>;
}

export interface YaakTheme extends ThemeComponent {
  id: string;
  name: string;
  components?: {
    dialog?: ThemeComponent;
    menu?: ThemeComponent;
    toast?: ThemeComponent;
    sidebar?: ThemeComponent;
    responsePane?: ThemeComponent;
    appHeader?: ThemeComponent;
    button?: ThemeComponent;
    banner?: ThemeComponent;
    placeholder?: ThemeComponent;
    urlBar?: ThemeComponent;
    editor?: ThemeComponent;
    input?: ThemeComponent;
  };
}

interface RootColors {
  primary: Color;
  secondary: Color;
  info: Color;
  success: Color;
  notice: Color;
  warning: Color;
  danger: Color;
}

type ColorName = keyof RootColors;
type ComponentName = keyof NonNullable<YaakTheme['components']>;

type CSSVariables = Record<string, string | undefined>;

function themeVariables(theme?: ThemeComponent, base?: CSSVariables): CSSVariables | null {
  const vars: CSSVariables = {
    '--background': theme?.background?.css(),
    '--background-highlight':
      theme?.backgroundHighlight?.css() ?? theme?.background?.lift(0.11).css(),
    '--background-highlight-secondary':
      theme?.backgroundHighlightSecondary?.css() ?? theme?.background?.lift(0.06).css(),
    '--background-active':
      theme?.backgroundActive?.css() ?? theme?.colors?.primary?.lower(0.2).translucify(0.8).css(),
    '--background-backdrop': theme?.background?.lower(0.2).translucify(0.2).css(),
    '--background-selection': theme?.colors?.primary?.lower(0.1).translucify(0.7).css(),
    '--fg': theme?.foreground?.css(),
    '--fg-subtle': theme?.foregroundSubtle?.css() ?? theme?.foreground?.lower(0.2).css(),
    '--fg-subtler': theme?.foregroundSubtler?.css() ?? theme?.foreground?.lower(0.3).css(),
    '--border-focus': theme?.colors?.info?.css(),
    '--shadow':
      theme?.shadow?.css() ??
      Color.black()
        .translucify(isThemeDark(theme ?? {}) ? 0.7 : 0.93)
        .css(),
  };

  for (const [color, value] of Object.entries(theme?.colors ?? {})) {
    vars[`--fg-${color}`] = (value as Color).css();
  }

  // Extend with base
  for (const [k, v] of Object.entries(vars)) {
    if (!v && base?.[k]) {
      vars[k] = base[k];
    }
  }

  return vars;
}

function placeholderColorVariables(color: Color): CSSVariables {
  return {
    '--fg': color.lift(0.6).css(),
    '--fg-subtle': color.lift(0.4).css(),
    '--fg-subtler': color.css(),
    '--background': color.lower(0.2).translucify(0.8).css(),
    '--background-highlight': color.lower(0.2).translucify(0.2).css(),
    '--background-highlight-secondary': color.lower(0.1).translucify(0.7).css(),
  };
}

function bannerColorVariables(color: Color): CSSVariables {
  return {
    '--fg': color.lift(0.8).css(),
    '--fg-subtle': color.translucify(0.3).css(),
    '--fg-subtler': color.css(),
    '--background': color.css(),
    '--background-highlight': color.lift(0.3).translucify(0.4).css(),
    '--background-highlight-secondary': color.translucify(0.9).css(),
  };
}

function buttonSolidColorVariables(color: Color): CSSVariables {
  return {
    '--fg': new Color('white', 'dark').css(),
    '--background': color.lower(0.15).css(),
    '--background-highlight': color.css(),
    '--background-highlight-secondary': color.lower(0.3).css(),
  };
}

function buttonBorderColorVariables(color: Color): CSSVariables {
  return {
    '--fg': color.lift(0.6).css(),
    '--fg-subtle': color.lift(0.4).css(),
    '--fg-subtler': color.lift(0.4).translucify(0.6).css(),
    '--background': Color.transparent().css(),
    '--background-highlight': color.translucify(0.8).css(),
  };
}

function variablesToCSS(selector: string | null, vars: CSSVariables | null): string | null {
  if (vars == null) {
    return null;
  }

  const css = Object.entries(vars ?? {})
    .filter(([, value]) => value)
    .map(([name, value]) => `${name}: ${value};`)
    .join('\n');

  return selector == null ? css : `${selector} {\n${indent(css)}\n}`;
}

function componentCSS(
  component: ComponentName,
  components?: YaakTheme['components'],
): string | null {
  if (components == null) {
    return null;
  }

  const themeVars = themeVariables(components[component]);
  return variablesToCSS(`.x-theme-${component}`, themeVars);
}

function buttonCSS(color: ColorName, colors?: Partial<RootColors>): string | null {
  const cssColor = colors?.[color];
  if (cssColor == null) {
    return null;
  }

  return [
    variablesToCSS(`.x-theme-button--solid--${color}`, buttonSolidColorVariables(cssColor)),
    variablesToCSS(`.x-theme-button--border--${color}`, buttonBorderColorVariables(cssColor)),
  ].join('\n\n');
}

function bannerCSS(color: ColorName, colors?: Partial<RootColors>): string | null {
  const cssColor = colors?.[color];
  if (cssColor == null) {
    return null;
  }

  return [variablesToCSS(`.x-theme-banner--${color}`, bannerColorVariables(cssColor))].join('\n\n');
}

function placeholderCSS(color: ColorName, colors?: Partial<RootColors>): string | null {
  const cssColor = colors?.[color];
  if (cssColor == null) {
    return null;
  }

  return [
    variablesToCSS(`.x-theme-placeholder--${color}`, placeholderColorVariables(cssColor)),
  ].join('\n\n');
}

export function isThemeDark(theme: ThemeComponent): boolean {
  if (theme.background && theme.foreground) {
    return theme.foreground.lighterThan(theme.background);
  }

  return false;
}

export function getThemeCSS(theme: YaakTheme): string {
  theme.components = theme.components ?? {};
  // Toast defaults to menu styles
  theme.components.toast = theme.components.toast ?? theme.components.menu;

  let themeCSS = '';
  try {
    const baseCss = variablesToCSS(null, themeVariables(theme));
    const { components, colors } = theme;
    themeCSS = [
      baseCss,
      ...Object.keys(components ?? {}).map((key) =>
        componentCSS(key as ComponentName, theme.components),
      ),
      ...Object.keys(colors ?? {}).map((key) =>
        buttonCSS(key as ColorName, theme.components?.button?.colors ?? colors),
      ),
      ...Object.keys(colors ?? {}).map((key) =>
        bannerCSS(key as ColorName, theme.components?.banner?.colors ?? colors),
      ),
      ...Object.keys(colors ?? {}).map((key) =>
        placeholderCSS(key as ColorName, theme.components?.placeholder?.colors ?? colors),
      ),
    ].join('\n\n');
  } catch (err) {
    console.error(err);
  }

  return [`/* ${theme.name} */`, `[data-theme="${theme.id}"] {`, indent(themeCSS), '}'].join('\n');
}

export function addThemeStylesToDocument(theme: YaakTheme) {
  let styleEl = document.head.querySelector(`style[data-theme]`);
  if (!styleEl) {
    styleEl = document.createElement('style');
    document.head.appendChild(styleEl);
  }

  styleEl.setAttribute('data-theme', theme.id);
  styleEl.textContent = getThemeCSS(theme);
}

export function setThemeOnDocument(theme: YaakTheme) {
  document.documentElement.setAttribute('data-theme', theme.id);
}

export function indent(text: string, space = '    '): string {
  return text
    .split('\n')
    .map((line) => space + line)
    .join('\n');
}
