import { createCanvas, loadImage, GlobalFonts } from "@napi-rs/canvas";
import {
  GenerateBattlerOptions,
  GenerateBattleImageOptions,
  PlayerActionOptions,
  Opponents,
  CustomActionOptions,
  CommunityOpponents
} from "./types";
import {
  generateBattler, drawText, applyText,
  validOpponents, validPlayerActions
} from "./utils";

import { createServer } from "node:http";

import GenerateParty from "./party"

GlobalFonts
  .registerFromPath(import.meta.dirname + "/LilitaOne.ttf", "Lilita One");

const opponents = {
  [Opponents.BattleBeginner]: {
    text: "Battle Beginner",
    colour: ["#007500", "#FFFFFF"]
  },
  [Opponents.BattleBeginner2024]: {
    text: "Battle Beginner",
    colour: ["#007500", "#FFFFFF"]
  },
  [Opponents.BattleCasual]: {
    text: "Battle Casual",
    colour: ["#757500", "#FFFFFF"]
  },
  [Opponents.BattleMaster]: {
    text: "Battle Master",
    colour: ["#750000", "#FFFFFF"]
  },
  [Opponents.BattlePro]: {
    text: "Battle Pro",
    colour: ["#753000", "#FFFFFF"]
  },
  [Opponents.Battler]: {
    text: "Battler",
    colour: ["#FFFFFF", "#000000"]
  },
  [Opponents.BattlerElite]: {
    text: "Battler Elite",
    colour: ["#FFFFFF", "#FF0000"]
  },
  [Opponents.None]: {
    text: "",
    colour: ["#FFFFFF", "#FFFFFF"]
  },
  [Opponents.RapStar]: {
    text: "Rap Star",
    colour: ["#FF0000", "#FFFFFF"]
  },
  [Opponents.ArtMaster]: {
    text: "Art Master",
    colour: ["#000000", "#FFFFFF"]
  },
  [Opponents.TheInterrogator]: {
    text: "The Interrogator",
    colour: ["#FFFFFF", "#000000"]
  },
  [Opponents.CommonOverseer]: {
    text: "Common Overseer",
    colour: ["#000000", "#32CD32"]
  },
  [Opponents.Moonlite]: {
    text: "Moonlite",
    colour: ["#000000", "#FFFFFF"]
  },
  [Opponents.Dicey]: {
    text: "Dicey",
    colour: ["#000000", "#FFFFFF"]
  },
  [Opponents.Kacey]: {
    text: "Kacey",
    colour: ["#000000", "#FFFFFF"]
  },
  [Opponents.Lexa]: {
    text: "Lexa",
    colour: ["#FFFFFF", "#FF00FF"]
  },
  [Opponents.Delta]: {
    text: "Delta",
    colour: ["#000000", "#FFFFFF"]
  },
  [Opponents.DoodlePro]: {
    text: "Doodle Pro",
    colour: ["#000000", "#FFFFFF"]
  },
  [Opponents.GuitarHero]: {
    text: "Guitar Hero",
    colour: ["#FFFFFF", "#FF8000"]
  },
  [Opponents.SmallKeyMaster]: {
    text: "Small KeyMaster",
    colour: ["#FFFFFF", "#32CD32"]
  },
  [Opponents.RareOverseer]: {
    text: "Rare Overseer",
    colour: ["#000000", "#00FFFF"]
  },
  [Opponents.DEMUL]: {
    text: "DEMUL",
    colour: ["#FFFFFF", "#000000"]
  },
  [Opponents.AgentZ]: {
    text: "Agent Z",
    colour: ["#FFFFFF", "#000000"]
  },
  [Opponents.MediumKeyMaster]: {
    text: "Medium KeyMaster",
    colour: ["#000000", "#00FFFF"]
  },
  [Opponents.IncomingSword]: {
    text: "Incoming Sword",
    colour: ["#FF0000", "#FFFFFF"]
  },
  [Opponents.Eda]: {
    text: "Eda",
    colour: ["#9000FF", "#FFFFFF"]
  },
  [Opponents.TheFirewall]: {
    text: "The Firewall",
    colour: ["#FFFFFF", "#FFAA00"]
  },
  [Opponents.HouseannorSupport]: {
    text: "Houseannor Support",
    colour: ["#FF00FF", "#FFFFFF"]
  },
  [Opponents.Dicey2024]: {
    text: "Dicey",
    colour: ["#000000", "#FFFFFF"]
  },
  [Opponents.CakeDay]: {
    text: "Cake Day",
    colour: ["#FA61FF", "#FFFFFF"]
  },
  [Opponents.MasterOG]: {
    text: "Master OG",
    colour: ["#FF0000", "#FFFFFF"]
  },
}

const communityOpponents = {
  [Opponents.None]: {
    text: "",
    colour: ["#FFFFFF", "#FFFFFF"]
  },
  [CommunityOpponents.Stik]: {
    text: "Stik",
    colour: ["#FF0000", "#000000"]
  },
  [CommunityOpponents.ThisMessage]: {
    text: "This Message",
    colour: ["#000000", "#FFFFFF"]
  },
  [CommunityOpponents.ProjectIX]: {
    text: "Project IX",
    colour: ["#FFFFFF", "#000000"]
  },
  [CommunityOpponents.ProjectX]: {
    text: "Project X",
    colour: ["#000000", "#FFFFFF"]
  },
  [CommunityOpponents.WastelanderGrimmeras]: {
    text: "Wastelander (Grimmeras)",
    colour: ["#FFFF00", "#000000"]
  },
  [CommunityOpponents.RabidWastelanderGrimmeras]: {
    text: "Rabid Wastelander (Grimmeras)",
    colour: ["#FF0000", "#000000"]
  }
}

const server = createServer(async (req, res) => {
  if (req.method === "OPTIONS") {
    res.setHeader("access-control-allow-origin", "*")
    return res.end();
  }

  const url = new URL(req.url!, "https://loc.al");

  if (req.method == "GET" && url.pathname == "/status") {
    return res.end("OK")
  }

  if (req.method == "GET" && url.pathname == "/battler.png") {
    console.log(`Generating battler`)
    const opts = Object.fromEntries(
      Array.from(url.searchParams.entries())
        .map(([key, value]) => [key, value.replaceAll('+', ' ')]),
    ) as GenerateBattlerOptions;

    const battler = await generateBattler(opts);

    res.setHeader('content-type', 'image/png');

    return res.end(battler.toBuffer("image/png"), 'binary')
  }

  if (req.method == "GET" && url.pathname == "/profilebattler.png") {
    const opts = Object.fromEntries(
      Array.from(url.searchParams.entries())
        .map(([key, value]) => [key, value.replaceAll('+', ' ')]),
    ) as GenerateBattlerOptions & { username: string };

    console.log(`Generating profile battler for ${opts.username}`)

    const canvas = createCanvas(1280, 1280);
    const ctx = canvas.getContext("2d");

    const battler = await generateBattler(opts)

    ctx.drawImage(battler, 0, 0);

    ctx.textAlign = "center"

    ctx.font = applyText(canvas, opts.username, canvas.width - 20)

    drawText([640, 1100], opts.username, opts.username == "homeannor" ? "#AA00FF" : "#000000", { colour: "#FFFFFF", width: 20 }, ctx)

    res.setHeader('content-type', 'image/png');

    return res.end(canvas.toBuffer("image/png"), 'binary')
  }
  if (
    req.method == "GET" &&
    (url.pathname == "/battlesquadfight.png" ||
      url.pathname == "/battlestart.png")
  ) {
    const opts = Object.fromEntries(
      Array.from(url.searchParams.entries())
        .map(([key, value]) => [key, value.replaceAll('+', ' ')]),
    ) as GenerateBattlerOptions & GenerateBattleImageOptions;

    if (!opts.opponent) {
      res.writeHead(400, { 'content-type': "application/json" })
      return res.end(
        JSON.stringify({
          error: '"opponent" query parameter not specified',
        })
      );
    }

    console.log(`Generating battle start for ${opts.username} and ${opts.opponent}`)

    // Image dimensions
    const canvas = createCanvas(1920, 1080);
    const context = canvas.getContext("2d");

    if (!validOpponents.includes(opts.opponent)) opts.opponent = Opponents.None

    const SquadBackground = await loadImage(
      `./assets/battlebgs/${opts.opponent}.png`,
    );

    context.drawImage(SquadBackground, 0, 0, canvas.width, canvas.height);

    const battler = await generateBattler(opts);

    context.drawImage(battler, -140, -100, battler.width, battler.height);

    context.textAlign = "center"

    context.font = applyText(canvas, opponents[opts.opponent].text, 875)

    drawText([1450, 1000], opponents[opts.opponent].text, opponents[opts.opponent].colour[1], { colour: opponents[opts.opponent].colour[0], width: 20 }, context);

    context.font = applyText(canvas, opts.username, 675)

    drawText([470, 1000], opts.username, opts.username == "homeannor" ? "#AA00FF" : "#000000", { colour: "#FFFFFF", width: 20 }, context)

    res.setHeader('content-type', 'image/png');

    return res.end(canvas.toBuffer("image/png"), 'binary')
  }

  if (
    req.method == "GET" &&
    url.pathname == "/communityvs.png"
  ) {
    const opts = Object.fromEntries(
      Array.from(url.searchParams.entries())
        .map(([key, value]) => [key, value.replaceAll('+', ' ')]),
    ) as GenerateBattlerOptions & GenerateBattleImageOptions;

    if (!opts.opponent) {
      res.writeHead(400, { 'content-type': "application/json" })
      return res.end(
        JSON.stringify({
          error: '"opponent" query parameter not specified',
        })
      );
    }

    console.log(`Generating battle start for ${opts.username} and ${opts.opponent}`)

    // Image dimensions
    const canvas = createCanvas(1920, 1080);
    const context = canvas.getContext("2d");

    if (!validOpponents.includes(opts.opponent)) opts.opponent = Opponents.None

    const SquadBackground = await loadImage(
      `./assets/communityvs/${opts.opponent}.png`,
    );

    context.drawImage(SquadBackground, 0, 0, canvas.width, canvas.height);

    const battler = await generateBattler(opts);

    context.drawImage(battler, -140, -100, battler.width, battler.height);

    context.textAlign = "center"

    // @ts-ignore shut up
    context.font = applyText(canvas, communityOpponents[opts.opponent].text, 875)
    
    // @ts-ignore shut up
    drawText([1450, 1000], communityOpponents[opts.opponent].text, communityOpponents[opts.opponent].colour[1], { colour: communityOpponents[opts.opponent].colour[0], width: 20 }, context);

    context.font = applyText(canvas, opts.username, 675)

    drawText([470, 1000], opts.username, opts.username == "homeannor" ? "#AA00FF" : "#000000", { colour: "#FFFFFF", width: 20 }, context)

    res.setHeader('content-type', 'image/png');

    return res.end(canvas.toBuffer("image/png"), 'binary')
  }

  if (
    req.method == "GET" &&
    url.pathname == "/playeraction.png"
  ) {
    const opts = Object.fromEntries(
      Array.from(url.searchParams.entries())
        .map(([key, value]) => [key, value.replaceAll('+', ' ')]),
    ) as PlayerActionOptions;

    if (!opts.action) {
      res.writeHead(400, { 'content-type': "application/json" })
      return res.end(
        JSON.stringify({
          error: '"action" query parameter not specified',
        })
      );      
    }

    if (!validPlayerActions.includes(opts.action)) {
      res.writeHead(400, { 'content-type': "application/json" })
      return res.end(
        JSON.stringify({
          error:
            '"action" query parameter not formatted correctly. Got "' +
            opts.action +
            '".',
        })
      );
    }

    console.log(`Generating ${opts.action}`)

    // Image dimensions
    const canvas = createCanvas(1920, 1080);
    const context = canvas.getContext("2d");

    const actionBackground = await loadImage(
      `./assets/actionbgs/${opts.action}.png`,
    );

    context.drawImage(actionBackground, 0, 0, canvas.width, canvas.height);

    const pUrl = new URL(encodeURI(opts.player))

    const player = Object.fromEntries(
      Array.from(pUrl.searchParams.entries())
        .map(([key, value]) => [key, value.replaceAll('+', ' ')]),
    ) as GenerateBattlerOptions

    if (opts.action.endsWith("Left")) {
      const battler = await generateBattler(player);

      context.drawImage(battler, -50, -100, battler.width, battler.height);
    } else if (opts.action.endsWith("Right")) {
      const battler = await generateBattler({ ...player, direction: "left" });

      context.drawImage(battler, (canvas.width / 2) - 300, -100, battler.width, battler.height);
    }

    res.setHeader('content-type', 'image/png');

    return res.end(canvas.toBuffer("image/png"), 'binary')
  }

  if (
    req.method == "GET" &&
    url.pathname == "/customaction.png"
  ) {
    const opts = Object.fromEntries(
      Array.from(url.searchParams.entries())
        .map(([key, value]) => [key, value.replaceAll('+', ' ')]),
    ) as CustomActionOptions;

    if (!opts.background) {
      res.writeHead(400, { 'content-type': "application/json" })
      return res.end(
        JSON.stringify({
          error:
            '"background" query parameter not found',
        })
      );
    }

    console.log(`Generating custom action (${opts.background})`)

    // Image dimensions
    const canvas = createCanvas(1920, 1080);
    const context = canvas.getContext("2d");

    const actionBackground = await loadImage(
      opts.background,
    );

    context.drawImage(actionBackground, 0, 0, canvas.width, canvas.height);

    const pUrl = new URL(encodeURI(opts.player))

    const player = Object.fromEntries(
      Array.from(pUrl.searchParams.entries())
        .map(([key, value]) => [key, value.replaceAll('+', ' ')]),
    ) as GenerateBattlerOptions

    const battler = await generateBattler(player);

    if (opts.position == "middle") {
      context.drawImage(battler, (1920 / 2) - 640, -100, battler.width, battler.height);
    } else {
      context.drawImage(battler, -50, -100, battler.width, battler.height);
    }

    res.setHeader('content-type', 'image/png');

    return res.end(canvas.toBuffer("image/png"), 'binary')
  }

  if (
    req.method == "GET" &&
    url.pathname == "/playermap.png"
  ) {
    const opts = Object.fromEntries(
      Array.from(url.searchParams.entries())
        .map(([key, value]) => [key, value.replaceAll('+', ' ')]),
    ) as { player1: string, player2: string, map: string };

    if (!opts.player1 || !opts.player2 || !opts.map) {
      res.writeHead(400, { 'content-type': "application/json" })
      return res.end(
        JSON.stringify({
          error:
            'Did not pass all parameters',
        })
      );
    }

    console.log(`Generating player map (${opts.map})`)

    const p1URL = new URL(encodeURI(opts.player1))
    const p2URL = new URL(encodeURI(opts.player2))

    const p1Opts = Object.fromEntries(
      Array.from(p1URL.searchParams.entries())
        .map(([key, value]) => [key, value.replaceAll('+', ' ')]),
    ) as GenerateBattlerOptions
    const p2Opts = Object.fromEntries(
      Array.from(p2URL.searchParams.entries())
        .map(([key, value]) => [key, value.replaceAll('+', ' ')]),
    ) as GenerateBattlerOptions

    const p1 = await generateBattler(p1Opts);
    const p2 = await generateBattler({ ...p2Opts, direction: "left" })

    // Image dimensions
    const canvas = createCanvas(1920, 1080);
    const context = canvas.getContext("2d");

    const Background = await loadImage(
      `./assets/playerlocations/${opts.map}.png`,
    );

    context.drawImage(Background, 0, 0, canvas.width, canvas.height);

    context.drawImage(p1, 140, 313, p1.width / 2, p1.height / 2);
    context.drawImage(p2, (canvas.width / 2) + 177, 313, p2.width / 2, p2.height / 2);

    res.setHeader('content-type', 'image/png');

    return res.end(canvas.toBuffer("image/png"), 'binary')
  }

  if (
    req.method == "GET" &&
    url.pathname == "/map.png"
  ) {
    const opts = Object.fromEntries(
      Array.from(url.searchParams.entries())
        .map(([key, value]) => [key, value.replaceAll('+', ' ')]),
    ) as { player: string, map: string };

    if (!opts.player || !opts.map) {
      res.writeHead(400, { 'content-type': "application/json" })
      return res.end(
        JSON.stringify({
          error:
            'Did not pass all parameters',
        })
      );
    }

    console.log(`Generating map (${opts.map})`)

    const p1URL = new URL(encodeURI(opts.player))

    const p1Opts = Object.fromEntries(
      Array.from(p1URL.searchParams.entries())
        .map(([key, value]) => [key, value.replaceAll('+', ' ')]),
    ) as GenerateBattlerOptions

    const p1 = await generateBattler(p1Opts);

    // Image dimensions
    const canvas = createCanvas(1920, 1080);
    const context = canvas.getContext("2d");

    const Background = await loadImage(
      `./assets/locations/${opts.map}.png`,
    );

    context.drawImage(Background, 0, 0, canvas.width, canvas.height);

    context.drawImage(p1, 140, 313, p1.width / 2, p1.height / 2);

    res.setHeader('content-type', 'image/png');

    return res.end(canvas.toBuffer("image/png"), 'binary')
  }

  if (
    req.method == "GET" &&
    url.pathname == "/communitymap.png"
  ) {
    const opts = Object.fromEntries(
      Array.from(url.searchParams.entries())
        .map(([key, value]) => [key, value.replaceAll('+', ' ')]),
    ) as { player: string, map: string };

    if (!opts.player || !opts.map) {
      res.writeHead(400, { 'content-type': "application/json" })
      return res.end(
        JSON.stringify({
          error:
            'Did not pass all parameters',
        })
      );
    }

    console.log(`Generating map (${opts.map})`)

    const p1URL = new URL(encodeURI(opts.player))

    const p1Opts = Object.fromEntries(
      Array.from(p1URL.searchParams.entries())
        .map(([key, value]) => [key, value.replaceAll('+', ' ')]),
    ) as GenerateBattlerOptions

    const p1 = await generateBattler(p1Opts);

    // Image dimensions
    const canvas = createCanvas(1920, 1080);
    const context = canvas.getContext("2d");

    const Background = await loadImage(
      `./assets/communitymaps/${opts.map}.png`,
    );

    context.drawImage(Background, 0, 0, canvas.width, canvas.height);

    context.drawImage(p1, 140, 313, p1.width / 2, p1.height / 2);

    res.setHeader('content-type', 'image/png');

    return res.end(canvas.toBuffer("image/png"), 'binary')
  }

  if (
    req.method == "GET" &&
    url.pathname == "/pvp.png"
  ) {
    const opts = Object.fromEntries(
      Array.from(url.searchParams.entries())
        .map(([key, value]) => [key, value.replaceAll('+', ' ')]),
    ) as { player1: string, player2: string, username1: string, username2: string };

    if (!opts.player1 || !opts.player2 || !opts.username1 || !opts.username2) {
      res.writeHead(400, { 'content-type': "application/json" })
      return res.end(
        JSON.stringify({
          error:
            'Did not pass all parameters',
        })
      );
    }

    console.log(`Generating PvP image for ${opts.username1} and ${opts.username2}`)

    const p1URL = new URL(encodeURI(opts.player1))
    const p2URL = new URL(encodeURI(opts.player2))

    const p1Opts = Object.fromEntries(
      Array.from(p1URL.searchParams.entries())
        .map(([key, value]) => [key, value.replaceAll('+', ' ')]),
    ) as GenerateBattlerOptions
    const p2Opts = Object.fromEntries(
      Array.from(p2URL.searchParams.entries())
        .map(([key, value]) => [key, value.replaceAll('+', ' ')]),
    ) as GenerateBattlerOptions

    const p1 = await generateBattler(p1Opts);
    const p2 = await generateBattler({ ...p2Opts, direction: "left" })

    // Image dimensions
    const canvas = createCanvas(1920, 1080);
    const context = canvas.getContext("2d");

    const Background = await loadImage(
      `./assets/battlebgs/PvP.png`,
    );

    context.drawImage(Background, 0, 0, canvas.width, canvas.height);

    context.drawImage(p1, -140, -100, p1.width, p1.height);
    context.drawImage(p2, (canvas.width / 2) - 180, -100, p2.width, p2.height);

    context.textAlign = "center"

    context.font = applyText(canvas, opts.username2, 675)

    drawText([1450, 1000], opts.username2, opts.username2 == "homeannor" ? "#AA00FF" : "#000000", { colour: "#FFFFFF", width: 20 }, context)

    context.font = applyText(canvas, opts.username1, 675)

    drawText([470, 1000], opts.username1, opts.username1 == "homeannor" ? "#AA00FF" : "#000000", { colour: "#FFFFFF", width: 20 }, context)

    res.setHeader('content-type', 'image/png');

    return res.end(canvas.toBuffer("image/png"), 'binary')
  }

  console.log(`Failed request (${req.method}, ${url.pathname})`)
  res.writeHead(404)
  return res.end("404 Not Found")
})

server.listen(60125, () => {
  console.log(`Listening on localhost:60125`)
})