import * as ejs from "ejs";
import { existsSync } from "fs";
import { createServer } from "http";

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

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 => c.types[0] != "Land" && colorid(c.colorIdentity) == "W",
  U: c => c.types[0] != "Land" && colorid(c.colorIdentity) == "U",
  B: c => c.types[0] != "Land" && colorid(c.colorIdentity) == "B",
  R: c => c.types[0] != "Land" && colorid(c.colorIdentity) == "R",
  G: c => c.types[0] != "Land" && colorid(c.colorIdentity) == "G",
  MC: c => c.types[0] != "Land" && c.colorIdentity.length > 1,
  CL: c => c.types[0] != "Land" && colorid(c.colorIdentity) == "CL",
  L: c => c.types[0] == "Land"
};

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;
}

async function run() {
  if (process.argv.length < 3) {
    console.error("Usage: yarn fetch <uid>");
    process.exit(1);
    return;
  }
  const uid = process.argv[2];
  const uidCards = `${uid}-cards.json`;
  if (!existsSync("mcmCards.json")) {
    console.error("Card db is missing! Run 'yarn convert-db' first.");
    process.exit(1);
  }
  if (!existsSync(uidCards)) {
    console.error(`Could not find ${uidCards}! Run 'yarn fetch ${uid}' first.`);
    process.exit(1);
  }
  let db = await asyncLoadJSON<MCMDB>("mcmCards.json");
  let articles = await asyncLoadJSON<Article[]>(`${uid}-cards.json`);

  let cards: CardItem[] = articles
    .filter(art => art.idProduct in db)
    .map(art => {
      const card = db[art.idProduct];
      return {
        ...leanArticle(art),
        ...leanCard(card)
      };
    });

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

  // Filter by colors and cmc (nested)
  let colorcards = filterDict(valid, columns);
  let cmccards = dictMap(colorcards, (cards, col) =>
    dictMap(
      spanBy(cards, c =>
        col == "MC" || col == "L" ? colorid(c.colorIdentity) : c.types[0]
      ),
      typed => spanBy(typed, c => c.convertedManaCost)
    )
  );
  const server = createServer(async (req, res) => {
    const template = await ejs.renderFile("templates/cube.ejs", {
      user: uid,
      cards: valid,
      cmccards,
      columns,
      utils: { wubrg, prettyColor, colorid, deepSum, typeSort, abcSort }
    });
    res.end(template);
  });

  server.on("clientError", (err, socket) => {
    socket.end("HTTP/1.1 400 Bad Request\r\n\r\n");
  });
  server.listen(8000);
}

run();
