// @flow
import { render, THROW_ON_ERROR } from '../common/render';
import { getThemes } from './index';
import type { Theme } from './index';

type ThemeBlock = {
  background?: {
    default: string,
    success?: string,
    notice?: string,
    warning?: string,
    danger?: string,
    surprise?: string,
    info?: string,
  },
  foreground?: {
    default: string,
    success?: string,
    notice?: string,
    warning?: string,
    danger?: string,
    surprise?: string,
    info?: string,
  },
  highlight?: {
    default: string,
    xxs?: string,
    xs?: string,
    sm?: string,
    md?: string,
    lg?: string,
    xl?: string,
  },
};

type ThemeInner = {
  ...ThemeBlock,
  rawCss?: string,
  styles: ?{
    overlay?: ThemeBlock,
    dropdown?: ThemeBlock,
    tooltip?: ThemeBlock,
    sidebar?: ThemeBlock,
    sidebarHeader?: ThemeBlock,
    sidebarList?: ThemeBlock,
    sidebarActions?: ThemeBlock,
    pane?: ThemeBlock,
    paneHeader?: ThemeBlock,
    dialog?: ThemeBlock,
    dialogHeader?: ThemeBlock,
    dialogFooter?: ThemeBlock,
    transparentOverlay?: ThemeBlock,
    link?: ThemeBlock,
  },
};

export type PluginTheme = {
  name: string,
  displayName: string,
  theme: ThemeInner,
};

export async function generateThemeCSS(theme: PluginTheme): Promise<string> {
  const renderedTheme: ThemeInner = await render(
    theme.theme,
    theme.theme,
    null,
    THROW_ON_ERROR,
    theme.name,
  );
  const n = theme.name;

  let css = '';
  css += wrapStyles(n, '', getThemeBlockCSS(renderedTheme));

  if (renderedTheme.styles) {
    const styles = renderedTheme.styles;

    // Dropdown Menus
    css += wrapStyles(n, '.theme--dropdown__menu', getThemeBlockCSS(styles.dialog));
    css += wrapStyles(n, '.theme--dropdown__menu', getThemeBlockCSS(styles.dropdown));

    // Tooltips
    css += wrapStyles(n, '.theme--tooltip', getThemeBlockCSS(styles.dialog));
    css += wrapStyles(n, '.theme--tooltip', getThemeBlockCSS(styles.tooltip));

    // Overlay
    css += wrapStyles(
      n,
      '.theme--transparent-overlay',
      getThemeBlockCSS(styles.transparentOverlay),
    );

    // Dialogs
    css += wrapStyles(n, '.theme--dialog', getThemeBlockCSS(styles.dialog));
    css += wrapStyles(n, '.theme--dialog__header', getThemeBlockCSS(styles.dialogHeader));
    css += wrapStyles(n, '.theme--dialog__footer', getThemeBlockCSS(styles.dialogFooter));

    // Panes
    css += wrapStyles(n, '.theme--pane', getThemeBlockCSS(styles.pane));
    css += wrapStyles(n, '.theme--pane__header', getThemeBlockCSS(styles.paneHeader));

    // Sidebar Styles
    css += wrapStyles(n, '.theme--sidebar', getThemeBlockCSS(styles.sidebar));
    css += wrapStyles(n, '.theme--sidebar__list', getThemeBlockCSS(styles.sidebarList));
    css += wrapStyles(n, '.theme--sidebar__header', getThemeBlockCSS(styles.sidebarHeader));

    // Link
    css += wrapStyles(n, '.theme--link', getThemeBlockCSS(styles.link));

    // HACK: Dialog styles for CodeMirror dialogs too
    css += wrapStyles(n, '.CodeMirror-info', getThemeBlockCSS(styles.dialog));
  }

  return css;
}

function getThemeBlockCSS(block?: ThemeBlock): string {
  if (!block) {
    return '';
  }

  const indent = '\t';

  let css = '';

  const addVar = (variable?: string, value?: string) => {
    if (variable && value) {
      css += `${indent}--${variable}: ${value};\n`;
    }
  };

  const addComment = comment => {
    css += `${indent}/* ${comment} */\n`;
  };

  const addNewLine = () => {
    css += `\n`;
  };

  if (block.background) {
    const { background } = block;
    addComment('Background');
    addVar('color-bg', background.default);
    addVar('color-success', background.success);
    addVar('color-notice', background.notice);
    addVar('color-warning', background.warning);
    addVar('color-danger', background.danger);
    addVar('color-surprise', background.surprise);
    addVar('color-info', background.info);
    addNewLine();
  }

  if (block.foreground) {
    const { foreground } = block;
    addComment('Foreground');
    addVar('color-font', foreground.default);
    addVar('color-font-success', foreground.success);
    addVar('color-font-notice', foreground.notice);
    addVar('color-font-warning', foreground.warning);
    addVar('color-font-danger', foreground.danger);
    addVar('color-font-surprise', foreground.surprise);
    addVar('color-font-info', foreground.info);
    addNewLine();
  }

  if (block.highlight) {
    const { highlight } = block;
    addComment('Highlight');
    addVar('hl', highlight.default);
    addVar('hl-xxs', highlight.xxs);
    addVar('hl-xs', highlight.xs);
    addVar('hl-sm', highlight.sm);
    addVar('hl-md', highlight.md);
    addVar('hl-lg', highlight.lg);
    addVar('hl-xl', highlight.xl);
    addNewLine();
  }

  return css.replace(/\s+$/, '');
}

function wrapStyles(theme: string, selector: string, styles: string) {
  if (!styles) {
    return '';
  }

  return [
    `[theme="${theme}"] ${selector}, `,
    `[subtheme="${theme}"] ${selector ? selector + '--sub' : ''} {`,
    styles,
    '}',
    '',
    '',
  ].join('\n');
}

export async function setTheme(themeName: string) {
  if (!document) {
    return;
  }

  const head = document.head;
  const body = document.body;

  if (!head || !body) {
    return;
  }

  body.setAttribute('theme', themeName);
  const themes: Array<Theme> = await getThemes();

  for (const theme of themes) {
    let themeCSS = (await generateThemeCSS(theme.theme)) + '\n';
    const { name } = theme.theme;
    const { rawCss } = theme.theme.theme;

    let s = document.querySelector(`style[data-theme-name="${name}"]`);

    if (!s) {
      s = document.createElement('style');
      s.setAttribute('data-theme-name', name);
      head.appendChild(s);
    }

    if (typeof rawCss === 'string' && name === themeName) {
      themeCSS += '\n\n' + rawCss;
    }

    s.innerHTML = themeCSS;
  }
}

export async function setFont(settings: Object) {
  if (!document) {
    return;
  }

  const html = document.querySelector('html');

  if (!html) {
    return;
  }

  html.style.setProperty('--font-default', settings.fontInterface);
  html.style.setProperty('--font-monospace', settings.fontMonospace);
  html.style.setProperty('--font-ligatures', settings.fontVariantLigatures ? 'normal' : 'none');
  html.style.setProperty('font-size', `${settings.fontSize}px`);
}
