import * as ejs from "ejs";

import { Article, CardItem, dictMap, filterDict, leanArticle, leanCard, MCMDB, onlyUnique, spanBy } from ".";

const colorNames = {
  CL: "Colorless",
  MC: "Multicolor",
  L: "Land",
  W: "White",
  U: "Blue",
  B: "Black",
  R: "Red",
  G: "Green",
  WU: "Azorius",
  UB: "Dimir",
  BR: "Rakdos",
  RG: "Gruul",
  WG: "Selesnya",
  WB: "Orzhov",
  UR: "Izzet",
  BG: "Golgari",
  WR: "Boros",
  UG: "Simic",
  WUG: "Bant",
  WUB: "Esper",
  UBR: "Grixis",
  BRG: "Jund",
  WRG: "Naya",
  WBG: "Abzan",
  WUR: "Jeskai",
  UBG: "Sultai",
  WBR: "Mardu",
  URG: "Temur"
};

const columns: Record<string, (c: CardItem) => boolean> = {
  W: c => colorid(c.colorIdentity) == "W",
  U: c => colorid(c.colorIdentity) == "U",
  B: c => colorid(c.colorIdentity) == "B",
  R: c => colorid(c.colorIdentity) == "R",
  G: c => colorid(c.colorIdentity) == "G",
  MC: c => c.colorIdentity.length > 1,
  CL: c => colorid(c.colorIdentity) == "CL"
};

function wubrg(a: string, b: string) {
  const order = ["W", "U", "B", "R", "G"];
  const indexA = order.indexOf(a);
  const indexB = order.indexOf(b);
  return indexA - indexB;
}

function colorid(colors: string[]): string {
  if (colors.length < 1) {
    return "CL";
  }
  return colors.sort(wubrg).join("");
}

function prettyColor(color: string) {
  if (color in colorNames) {
    return colorNames[color];
  }
  return color;
}

const allTypes = [
  "Creature",
  "Planeswalker",
  "Instant",
  "Sorcery",
  "Artifact",
  "Enchantment"
];
function typeSort(a: string, b: string): number {
  const indexA = allTypes.indexOf(a);
  const indexB = allTypes.indexOf(b);
  return indexA - indexB;
}

function abcSort(a: string, b: string): number {
  if (a > b) {
    return 1;
  }
  if (b > a) {
    return -1;
  }
  return 0;
}

// This would be properly typed but as of currently TS does not allow circular references for types
function deepSum(dict: any): number {
  if (Array.isArray(dict)) {
    return dict.length;
  }
  let total = 0;
  for (let key in dict) {
    if (Array.isArray(dict[key])) {
      total += dict[key].length;
    } else {
      total += deepSum(dict[key]);
    }
  }
  return total;
}

export function cubeCards(c: CubeCards): CardItem[] {
  let cards: CardItem[] = [];
  dictMap(c, bytyp =>
    dictMap(bytyp, bycmc =>
      dictMap(bycmc, cardlist => (cards = cards.concat(cardlist)))
    )
  );
  return cards;
}

function byRank(a: CardItem, b: CardItem): number {
  return a.edhrecRank - b.edhrecRank;
}

export type ByCMC<T> = Record<number, T>;
export type ByType<T> = Record<string, T>;
export type ByColor<T> = Record<string, T>;
export type CubeCards = ByColor<ByType<ByCMC<CardItem[]>>>;

export async function genCube(
  db: MCMDB,
  articles: Article[]
): Promise<CubeCards> {
  const cards: CardItem[] = articles
    .filter(art => art.idProduct in db)
    .map(art => {
      const card = db[art.idProduct];
      return {
        ...leanArticle(art),
        ...leanCard(card)
      };
    });

  const valid = cards
    .filter(
      c =>
        (c.language == "Italian" || c.language == "English") &&
        c.price < 0.2 &&
        (c.rarity == "rare" || c.rarity == "mythic")
    )
    .filter(onlyUnique);

  const categorized = filterDict(valid, columns);

  // Cut cards
  const filtered = dictMap(categorized, (pool, col) => {
    switch (col) {
      case "MC":
        const colors = spanBy(pool, c => colorid(c.colorIdentity));
        let cards = [];
        for (const color in colors) {
          cards = cards.concat(colors[color].sort(byRank).slice(0, 3));
        }
        return cards;
      case "CL":
        return pool.sort(byRank).slice(0, 30);
      default:
        return pool.sort(byRank).slice(0, 58);
    }
  });

  // Filter by colors and cmc (nested)
  return dictMap(filtered, (cards, col) =>
    dictMap(
      spanBy(cards, c =>
        col == "MC" || col == "L"
          ? colorid(c.colorIdentity)
          : c.types.slice(-1)[0]
      ),
      typed => spanBy(typed, c => c.convertedManaCost)
    )
  );
}

export async function cubeHTML(user, cubecards: CubeCards) {
  return await ejs.renderFile("templates/cube.ejs", {
    user,
    cards: cubeCards(cubecards),
    cubecards,
    columns,
    utils: { wubrg, prettyColor, colorid, deepSum, typeSort, abcSort }
  });
}
