import { readFileSync } from "fs";
import { join } from "path";
import { hello } from "@sil/tools";
import { RGB, COLOR, hexToRgb, toHex, toRGB, toHSL, mix } from "@sil/color";
import { jsOutput, tsOutput, sassOutput,jsonOutput } from "./output.js";
import { toDarkMode, toLightMode, getColorMode } from "./color.js";

import { state } from "./state.js";
import { getKey, getTextColor, getEnv, writeFile } from "./helpers.js";
import { ColorSet, ColorMode } from "./types.js";
import {
  blockFooter,
  blockHeader,
  blockLine,
  blockMid,
  blockRowLine,
  bold,
  yellow,
  dim,
  blockSettings,
  blockLineSuccess,
} from "cli-block";

const buildColors = (
  ogColors: ColorSet,
  mixColor: COLOR,
  mixColorAlt: COLOR
) => {
  const newColors: { [key: string]: COLOR | number } = {};

  const mixRgb = hexToRgb(toHex(mixColor));
  const altMixRgb = hexToRgb(toHex(mixColorAlt));

  Object.keys(ogColors).forEach((key) => {
    const isBase = key == "background" || key == "foreground";

    state.settings.colorSteps.forEach((step) => {
      const color: COLOR = getKey(ogColors, key);
      const mixer = color == mixColor ? altMixRgb : mixRgb;
      const mixStep = isBase ? Math.abs(100 - step) : step;
      const mixed = mix(mixer, toRGB(color), mixStep) as RGB;
      const hsl = toHSL(color);

      newColors[`${key}${step}`] = toHex(mixed);
      newColors[`${key}${step}-r`] = mixed.r;
      newColors[`${key}${step}-g`] = mixed.g;
      newColors[`${key}${step}-b`] = mixed.b;
      newColors[`${key}${step}-h`] = hsl.h;
      newColors[`${key}${step}-s`] = hsl.s;
      newColors[`${key}${step}-l`] = hsl.l;
      newColors[`${key}${step}-text`] = getTextColor(state, mixed);
    });
  });

  // Create RGB for input colors
  Object.keys(ogColors).forEach((key) => {
    const color: COLOR = getKey(ogColors, key);
    const rgb = toRGB(color);
    const hsl = toHSL(color);
    const textColor = getTextColor(state, color);

    newColors[`${key}-r`] = rgb.r;
    newColors[`${key}-g`] = rgb.g;
    newColors[`${key}-b`] = rgb.b;
    newColors[`${key}-h`] = hsl.h;
    newColors[`${key}-s`] = hsl.s;
    newColors[`${key}-l`] = hsl.l;
    newColors[`${key}-text`] = textColor;
  });

  // Rgb's
  return newColors;
};

// Get files
const getFiles = () => {
  const env = getEnv();
  try {
    const data = readFileSync(join(env.local, "themer.json"), {
      encoding: "utf8",
    });
    state.local = JSON.parse(data);
  } catch (err) {
    console.error("woops");
  }
};

/*
 * Steps
 */

// Generate files

const setOutputs = async() => {
  state.output = { ...state.themer.outputs, ...state.local.outputs };

  await blockMid('Outputs')
  await blockSettings(state.output)
};

const mergeData = async () => {
  blockMid("Colors");

  const mergedColors = {
    ...(state?.themer?.colors ? state.themer.colors : {}),
    ...state.local.colors,
  };

  Object.keys(mergedColors).forEach((key: string) => {
    let localColor = "";
    if (state.local.colors && getKey(state.local.colors, key)) {
      localColor = getKey(state.local.colors, key);
    }
    const themerColor = getKey(state.themer.colors, key);

    if (themerColor == localColor) {
      blockRowLine([bold(key), getKey(mergedColors, key)]);
    } else {
      blockRowLine([
        bold(key),
        `${dim(themerColor)} ${yellow("→")} ${localColor}`,
      ]);
    }
  });

  state.colors.og = mergedColors as ColorSet;

  blockMid("Settings");
  const mergedSettings = { ...state.themer.settings, ...state.local.settings };

  Object.keys(mergedSettings).forEach((key) => {
    blockRowLine([key, getKey(mergedSettings, key)]);
  });

  state.settings = mergedSettings;

  blockMid("Base");
  const mergedBase = { ...state.themer.base, ...state.local.base };

  Object.keys(mergedBase).forEach((key) => {
    blockRowLine([key, getKey(mergedBase, key)]);
  });

  state.base = mergedBase;

  blockMid("Typography");
  const mergedTypography = {
    ...state.themer.typography,
    ...state.local.typography,
  };

  Object.keys(mergedTypography).forEach((key) => {
    blockRowLine([key, getKey(mergedTypography, key)]);
  });

  state.typography = mergedTypography;
};

const fixColors = () => {
  if (state.colors.og.background && state.colors.og.foreground) {
    state.colors.mode = getColorMode(state.colors.og);
  }

  state.colors.og.dark =
    state.colors.og.dark || state.colors.mode == ColorMode.DARK
      ? state.colors.og.background
      : state.colors.og.foreground;
  state.colors.og.light =
    state.colors.og.light || state.colors.mode == ColorMode.LIGHT
      ? state.colors.og.background
      : state.colors.og.foreground;
};

const createColors = () => {
  const ogColors = Object.assign({}, state.colors.og);

  const darkColor = state.colors.og.dark || "#000000";
  const lightColor = state.colors.og.light || "#ffffff";

  const darkmodeColorSet = toDarkMode(state.colors.og);
  const lightmodeColorSet = toLightMode(state.colors.og);

  const lightModeColors = buildColors(lightmodeColorSet, lightColor, darkColor);

  const darkModeColors = buildColors(darkmodeColorSet, darkColor, lightColor);

  state.colors.light = { ...ogColors, ...lightModeColors };
  state.colors.dark = { ...ogColors, ...darkModeColors };

  // Fix background / Foreground

  if (state.colors.mode == "light") {
    state.colors.dark.background = state.colors.og.foreground;
    state.colors.dark.foreground = state.colors.og.background;
  } else {
    state.colors.light.background = state.colors.og.foreground;
    state.colors.light.foreground = state.colors.og.background;
  }

  // Set default colors according to current colormode
  state.colors.default =
    state.colors.mode == "light" ? state.colors.light : state.colors.dark;
};

const writeConfig = async () => {
  const env = getEnv();

  if (state.output.scss) {
    const sassData = sassOutput(state);
    const sassFile = join(env.local, state.output.scss);
    await writeFile(sassFile, sassData);
  }
  if (state.output.js) {
    const jsData = jsOutput(state);
    const jsFile = join(env.local, state.output.js || "src/data/theme.js");
    await writeFile(jsFile, jsData);
  }
  if (state.output.ts) {
    const tsData = tsOutput(state);
    const tsFile = join(env.local, state.output.ts || "src/data/theme.ts");
    await writeFile(tsFile, tsData);
  }
  if (state.output.json) {
    const jsonData = jsonOutput(state);
    const jsonFile = join(env.local, state.output.json || "src/data/theme.json");
    await writeFile(jsonFile, jsonData);
  }
};

// Runner

hello()
  .then(() => {
    blockHeader("Themer Config");
    blockLine();
    blockLine([
      "Themer is creating your config files. If you didnt define any",
      "custom options, Themer will just use the defaults",
    ]);
  })
  .then(() => getFiles())
  .then(() => mergeData())
  .then(() => setOutputs())
  .then(() => fixColors())
  .then(() => createColors())
  .then(() => writeConfig())
  .then(() => {
    blockFooter();
  });
