import { render } from "solid-js/web";
import { Tooltip } from "@kobalte/core/tooltip";
import {
  For,
  Show,
  createEffect,
  createResource,
  createSignal,
  onMount,
  onCleanup,
} from "solid-js";
import { customElement } from "solid-element";
import { SolidMarkdown } from "solid-markdown";
import {
  SunIcon,
  MoonIcon,
  HomeIcon,
  TrophyIcon,
  SwordsIcon,
  UsersIcon,
  UserIcon,
  LogInIcon,
  LogOutIcon,
} from "lucide-solid";
import toast, { Toaster } from "solid-toast";

export { toast } from "solid-toast";

import { FluentBundle, FluentResource } from "@fluent/bundle";
import { negotiateLanguages } from "@fluent/langneg";

import {
  CommandDialog,
  CommandEmpty,
  CommandGroup,
  CommandInput,
  CommandItem,
  CommandList,
  CommandSeparator,
} from "./command";

function navigate(url: string) {
  // @ts-ignore
  htmx.ajax("GET", url, {
    select: "#screen",
    target: "#screen",
    swap: "outerHTML",
    history: "push",
    headers: {
      "HX-Boosted": true,
    },
  });
  history.pushState({ htmx: true }, "", url);
}

const CommandMenu = () => {
  const [open, setOpen] = createSignal(false);

  const [data] = createResource(
    async () =>
      (await (await fetch("/command-palette")).json()) as CommandPaletteData,
  );

  onMount(() => {
    const down = (e: KeyboardEvent) => {
      if (e.key === "k" && (e.metaKey || e.ctrlKey)) {
        e.preventDefault();
        setOpen((open) => !open);
      }
    };

    // @ts-ignore
    window.openCommandPalette = () => setOpen(true);

    document.addEventListener("keydown", down);
    onCleanup(() => document.removeEventListener("keydown", down));
  });

  return (
    <CommandDialog open={open()} onOpenChange={setOpen}>
      <CommandInput placeholder={translate("type-to-search")} />
      <CommandList>
        <CommandEmpty>{translate("no-results")}</CommandEmpty>
        <CommandGroup heading={translate("pages")}>
          <CommandItem
            onSelect={() => {
              navigate("/");
              setOpen(false);
            }}
          >
            <HomeIcon class="mr-2 h-4 w-4" />
            <span>{translate("home")}</span>
          </CommandItem>
          <Show when={data().challenges}>
            <CommandItem
              onSelect={() => {
                navigate("/challenges");
                setOpen(false);
              }}
            >
              <SwordsIcon class="mr-2 h-4 w-4" />
              <span>{translate("challenges")}</span>
            </CommandItem>
            <CommandItem
              onSelect={() => {
                navigate("/team");
                setOpen(false);
              }}
            >
              <UsersIcon class="mr-2 h-4 w-4" />
              <span>{translate("team")}</span>
            </CommandItem>
            <CommandItem
              onSelect={() => {
                navigate("/account");
                setOpen(false);
              }}
            >
              <UserIcon class="mr-2 h-4 w-4" />
              <span>{translate("account")}</span>
            </CommandItem>
          </Show>
          <Show when={!data().challenges}>
            <CommandItem
              onSelect={() => {
                navigate("/signin");
                setOpen(false);
              }}
            >
              <LogInIcon class="mr-2 h-4 w-4" />
              <span>{translate("sign-in")}</span>
            </CommandItem>
          </Show>
        </CommandGroup>
        <CommandSeparator />
        <Show when={data().challenges}>
          <For each={Object.entries(data().challenges)}>
            {(category) => {
              const category_name = category[0];
              const challenges = category[1];
              return (
                <CommandGroup heading={category_name}>
                  <For each={challenges}>
                    {(challenge) => {
                      return (
                        <CommandItem
                          onSelect={() => {
                            navigate(`/challenges#${challenge}`);
                            setOpen(false);
                          }}
                        >
                          <SwordsIcon class="mr-2 h-4 w-4" />
                          <span>{challenge}</span>
                        </CommandItem>
                      );
                    }}
                  </For>
                </CommandGroup>
              );
            }}
          </For>
          <CommandSeparator />
        </Show>
        <Show when={data().divisions}>
          <CommandGroup heading={translate("scoreboard-title")}>
            <For each={Object.entries(data().divisions)}>
              {(division) => {
                const id = division[0];
                const name = division[1];
                return (
                  <CommandItem
                    onSelect={() => {
                      navigate(`/scoreboard/${id}`);
                      setOpen(false);
                    }}
                  >
                    <TrophyIcon class="mr-2 h-4 w-4" />
                    <span>{translate("scoreboard", { scoreboard: name })}</span>
                  </CommandItem>
                );
              }}
            </For>
          </CommandGroup>
          <CommandSeparator />
        </Show>
        <CommandGroup heading={translate("account")}>
          <Show when={!data().challenges}>
            <CommandItem
              onSelect={() => {
                // @ts-ignore
                window.location = "/signin/discord";
              }}
            >
              <LogInIcon class="mr-2 h-4 w-4" />
              <span>{translate("sign-in-with-discord")}</span>
            </CommandItem>
          </Show>
          <Show when={data().challenges}>
            <CommandItem
              onSelect={() => {
                navigate("/signout");
                setOpen(false);
              }}
            >
              <LogOutIcon class="mr-2 h-4 w-4" />
              <span>{translate("sign-out")}</span>
            </CommandItem>
          </Show>
        </CommandGroup>
        <CommandSeparator />
        <CommandGroup heading={translate("theme")}>
          <CommandItem
            onSelect={() => {
              // @ts-ignore
              setTheme(false);
              setOpen(false);
            }}
          >
            <SunIcon class="mr-2 h-4 w-4" />
            <span>{translate("light-theme")}</span>
          </CommandItem>
          <CommandItem
            onSelect={() => {
              // @ts-ignore
              setTheme(true);
              setOpen(false);
            }}
          >
            <MoonIcon class="mr-2 h-4 w-4" />
            <span>{translate("dark-theme")}</span>
          </CommandItem>
        </CommandGroup>
      </CommandList>
    </CommandDialog>
  );
};

const localizations: Record<string, string> = {
  en: `
type-to-search = Type a command or search...
no-results = No results found.
pages = Pages
home = Home
challenges = Challenges
team = Team
account = Account
sign-in = Sign In
scoreboard-title = Division Scoreboards
scoreboard = {$scoreboard} Scoreboard
sign-in-with-discord = Sign In with Discord
sign-out = Sign Out
theme = Theme
light-theme = Light Theme
dark-theme = Dark Theme

healthy = Challenge is <span class="text-green-500">up</span>. Last checked {$last_checked ->
  [one] {$last_checked} minute
  *[other] {$last_checked} minutes
} ago
unhealthy = Challenge is <span class="text-red-500">down</span>. Last checked {$last_checked ->
  [one] {$last_checked} minute
  *[other] {$last_checked} minutes
} ago
solved-by = Solved by {$name}
authored-by = Authored by {$name}
solves = {$solves ->
  [one] {$solves} solve 
  *[other] {$solves} solves
}
points = {$points ->
  [one] {$points} point
  *[other] {$points} points
}
solves-points = {solves} / {points}
  `.trim(),
  de: `
type-to-search = Befehl oder Suche eingeben...
no-results = Keine Ergebnisse gefunden.
pages = Seiten
home = Startseite
challenges = Challenges
team = Team
account = Konto
sign-in = Anmelden
scoreboard-title = Teilung Anzeigetafeln
scoreboard = {$scoreboard} Anzeigetafel
sign-in-with-discord = Mit Discord anmelden
sign-out = Abmelden
theme = Farbdesign
light-theme = Helles Farbdesign
dark-theme = Dunkles Farbdesign

healthy = Challenge ist <span class="text-green-500">online</span> und wurde vor {$last_checked ->
  [one] {$last_checked} Minute
  *[other] {$last_checked} Minuten
} überprüft
unhealthy = Challenge ist <span class="text-red-500">offline</span> und wurde vor {$last_checked ->
  [one] {$last_checked} Minute
  *[other] {$last_checked} Minuten
} überprüft
solved-by = Gelöst von {$name}
authored-by = Geschrieben von {$name}
solves = {$solves ->
  [one] {$solves} Lösung
  *[other] {$solves} Lösungen
}
points = {$points ->
  [one] {$points} Punkt
  *[other] {$points} Punkte
}
solves-points = {solves} / {points}
`.trim(),
};

const negotiatedLocales = negotiateLanguages(
  navigator.languages,
  Object.keys(localizations),
  {
    defaultLocale: Object.keys(localizations)[0],
  },
);

const bundles = negotiatedLocales.map((locale) => {
  const bundle = new FluentBundle(locale);
  bundle.addResource(new FluentResource(localizations[locale]));
  return bundle;
});

export function translate(key: string, args?: Record<string, string | number>) {
  for (const bundle of bundles) {
    const message = bundle.getMessage(key);
    if (message) {
      return bundle.formatPattern(message.value, args);
    }
  }
  return key;
}

customElement("rhombus-tooltip", (_props, { element }) => {
  const anchor = document.querySelector("dialog");

  // @ts-ignore
  let children: HTMLCollection = element.renderRoot.host.children;
  let content: Element = undefined;
  let trigger: Element = undefined;
  for (let i = 0; i < children.length; i++) {
    const slot = children[i].slot;
    if (slot === "content") {
      content = children[i];
    } else {
      trigger = children[i];
    }
  }

  return (
    <Tooltip placement="top">
      <Tooltip.Portal mount={anchor}>
        <Tooltip.Content class="tooltip">
          {content}
          <Tooltip.Arrow class="text-secondary" />
        </Tooltip.Content>
      </Tooltip.Portal>
      <Tooltip.Trigger as="div">{trigger}</Tooltip.Trigger>
    </Tooltip>
  );
});

const ChallengesComponent = ({
  challenge_json,
}: {
  challenge_json: ChallengesData;
}) => {
  const [shouldFetch, setShouldFetch] = createSignal<boolean>();
  const [data, { refetch }] = createResource(
    shouldFetch,
    async () =>
      (await (
        await fetch("/challenges.json", {
          headers: { accept: "application/json" },
        })
      ).json()) as ChallengesData,
    { initialValue: challenge_json },
  );

  (window as any).challengeRefetchHandler = () => {
    setShouldFetch(true);
    refetch();
  };

  createEffect(() => {
    data();
    // @ts-ignore
    htmx.process(document.body);
  });

  return (
    <Show when={data()}>
      <For each={data().categories}>
        {(category) => {
          const challenges = data()
            .challenges.filter(
              (challenge) => challenge.category_id === category.id,
            )
            .sort(
              (a, b) =>
                a.division_points[0].points - b.division_points[0].points,
            );

          return (
            <div>
              <div
                class="flex justify-between rounded-md p-4 font-bold"
                style={`background-color: ${category.color}`}
              >
                <span>{category.name}</span>
                <span>
                  {
                    challenges.filter(
                      (challenge) =>
                        data().team.solves[challenge.id] !== undefined,
                    ).length
                  }{" "}
                  / {challenges.length}
                </span>
              </div>
              <ul class="flex flex-col gap-4 px-2 pt-4">
                <For each={challenges}>
                  {(challenge) => {
                    const author = data().authors[challenge.author_id];
                    const solve = data().team.solves[challenge.id];

                    return (
                      <li
                        classList={{
                          ring:
                            decodeURIComponent(location.hash.substring(1)) ===
                            challenge.name,
                        }}
                        class="border-l-4 bg-card p-4 ring-offset-4 ring-offset-background"
                        style={`border-color: ${category.color}; --tw-ring-color: ${category.color}`}
                      >
                        <div class="mb-2 flex justify-between h-8">
                          <div class="font-bold flex items-center gap-2">
                            <button
                              hx-trigger="click"
                              hx-get={`/challenges/${challenge.id}`}
                              hx-target="body"
                              hx-swap="beforeend"
                            >
                              <span style={`color: ${category.color}`}>
                                {category.name} /
                              </span>
                              <span> {challenge.name}</span>
                            </button>
                            <Show when={challenge.health}>
                              <Tooltip placement="top">
                                <Tooltip.Portal>
                                  <Tooltip.Content class="tooltip">
                                    {challenge.health.healthy ? (
                                      <div
                                        innerHTML={translate("healthy", {
                                          last_checked: Math.ceil(
                                            (new Date().getTime() -
                                              new Date(
                                                challenge.health.last_checked,
                                              ).getTime()) /
                                              1000 /
                                              60,
                                          ),
                                        })}
                                      ></div>
                                    ) : (
                                      <div
                                        innerHTML={translate("unhealthy", {
                                          last_checked: Math.ceil(
                                            (new Date().getTime() -
                                              new Date(
                                                challenge.health.last_checked,
                                              ).getTime()) /
                                              1000 /
                                              60,
                                          ),
                                        })}
                                      ></div>
                                    )}
                                    <Tooltip.Arrow class="text-secondary" />
                                  </Tooltip.Content>
                                </Tooltip.Portal>
                                <Tooltip.Trigger
                                  as="div"
                                  class={`size-3 rounded-full cursor-pointer ${challenge.health.healthy ? "bg-green-500" : "bg-red-500"}`}
                                />
                              </Tooltip>
                            </Show>
                          </div>
                          <div class="flex items-center gap-4">
                            <Show when={solve}>
                              <Tooltip placement="top">
                                <Tooltip.Portal>
                                  <Tooltip.Content class="tooltip">
                                    <span>
                                      {translate("solved-by", {
                                        name: data().team.users[solve.user_id]
                                          .name,
                                      })}
                                    </span>
                                    <Tooltip.Arrow class="text-secondary" />
                                  </Tooltip.Content>
                                </Tooltip.Portal>
                                <Tooltip.Trigger
                                  as="a"
                                  hx-boost="true"
                                  hx-select="#screen"
                                  hx-target="#screen"
                                  hx-swap="outerHTML"
                                  href={`/user/${solve.user_id}`}
                                >
                                  <img
                                    class="aspect-square rounded-full h-8"
                                    alt={translate("solved-by", {
                                      name: data().team.users[solve.user_id]
                                        .name,
                                    })}
                                    src={
                                      data().team.users[solve.user_id]
                                        .avatar_url
                                    }
                                  />
                                </Tooltip.Trigger>
                              </Tooltip>
                            </Show>
                            <Tooltip placement="top">
                              <Tooltip.Portal>
                                <Tooltip.Content class="tooltip">
                                  <table>
                                    {challenge.division_points.map(
                                      (division_points) => (
                                        <tr>
                                          <td class="pr-2 text-right">
                                            {
                                              data().divisions[
                                                division_points.division_id
                                              ].name
                                            }
                                          </td>
                                          <td class="pr-2">
                                            {translate("solves", {
                                              solves: division_points.solves,
                                            })}
                                          </td>
                                          <td>
                                            {translate("points", {
                                              points: division_points.points,
                                            })}
                                          </td>
                                        </tr>
                                      ),
                                    )}
                                  </table>
                                  <Tooltip.Arrow class="text-secondary" />
                                </Tooltip.Content>
                              </Tooltip.Portal>
                              <Tooltip.Trigger as="span" class="cursor-pointer">
                                {translate("solves-points", {
                                  solves: challenge.division_points[0].solves,
                                  points: challenge.division_points[0].points,
                                })}
                              </Tooltip.Trigger>
                            </Tooltip>
                            <Show when={author}>
                              <Tooltip placement="top">
                                <Tooltip.Portal>
                                  <Tooltip.Content class="tooltip">
                                    <span>
                                      {translate("authored-by", {
                                        name: author.name,
                                      })}
                                    </span>
                                    <Tooltip.Arrow class="text-secondary" />
                                  </Tooltip.Content>
                                </Tooltip.Portal>
                                <Tooltip.Trigger
                                  as="img"
                                  class="aspect-square rounded-full h-8"
                                  alt={translate("authored-by", {
                                    name: author.name,
                                  })}
                                  src={author.avatar_url}
                                />
                              </Tooltip>
                            </Show>
                            <Show when={data().ticket_enabled}>
                              <button
                                hx-trigger="click"
                                hx-get={`/challenges/${challenge.id}/ticket`}
                                hx-target="body"
                                hx-swap="beforeend"
                              >
                                <svg
                                  xmlns="http://www.w3.org/2000/svg"
                                  width="24"
                                  height="24"
                                  viewBox="0 0 24 24"
                                  fill="none"
                                  stroke="currentColor"
                                  stroke-width="2"
                                  stroke-linecap="round"
                                  stroke-linejoin="round"
                                  class="lucide lucide-ticket -rotate-45"
                                >
                                  <path d="M2 9a3 3 0 0 1 0 6v2a2 2 0 0 0 2 2h16a2 2 0 0 0 2-2v-2a3 3 0 0 1 0-6V7a2 2 0 0 0-2-2H4a2 2 0 0 0-2 2Z" />
                                  <path d="M13 5v2" />
                                  <path d="M13 17v2" />
                                  <path d="M13 11v2" />
                                </svg>
                              </button>
                            </Show>
                            <button
                              hx-trigger="click"
                              hx-get={`/challenges/${challenge.id}`}
                              hx-target="body"
                              hx-swap="beforeend"
                            >
                              <svg
                                xmlns="http://www.w3.org/2000/svg"
                                width="24"
                                height="24"
                                viewBox="0 0 24 24"
                                fill="none"
                                stroke="currentColor"
                                stroke-width="2"
                                stroke-linecap="round"
                                stroke-linejoin="round"
                                class="lucide lucide-flag"
                              >
                                <path d="M4 15s1-1 4-1 5 2 8 2 4-1 4-1V3s-1 1-4 1-5-2-8-2-4 1-4 1z" />
                                <line x1="4" x2="4" y1="22" y2="15" />
                              </svg>
                            </button>
                          </div>
                        </div>
                        <div
                          class="prose dark:prose-invert"
                          innerHTML={challenge.description}
                        ></div>
                        <Show when={challenge.attachments.length > 0}>
                          <div class="flex gap-2 mt-4">
                            <For each={challenge.attachments}>
                              {(attachment) => (
                                <a
                                  class="pr-2 py-1 pl-1 max-w-fit rounded-lg flex"
                                  style={`background-color: ${category.color}77`}
                                  href={attachment.url}
                                >
                                  <div class="scale-75">
                                    <svg
                                      xmlns="http://www.w3.org/2000/svg"
                                      width="24"
                                      height="24"
                                      viewBox="0 0 24 24"
                                      fill="none"
                                      stroke="currentColor"
                                      stroke-width="2"
                                      stroke-linecap="round"
                                      stroke-linejoin="round"
                                      class="lucide lucide-paperclip"
                                    >
                                      <path d="m21.44 11.05-9.19 9.19a6 6 0 0 1-8.49-8.49l8.57-8.57A4 4 0 1 1 18 8.84l-8.59 8.57a2 2 0 0 1-2.83-2.83l8.49-8.48" />
                                    </svg>
                                  </div>
                                  {attachment.name}
                                </a>
                              )}
                            </For>
                          </div>
                        </Show>
                      </li>
                    );
                  }}
                </For>
              </ul>
            </div>
          );
        }}
      </For>
    </Show>
  );
};

type CommandPaletteData = {
  challenges?: Record<string, string[]>;
  divisions: Record<string, string>;
};

type ChallengesData = {
  ticket_enabled: boolean;
  challenges: {
    id: number;
    name: string;
    description: string;
    health: {
      healthy: boolean;
      last_checked: string;
    } | null;
    category_id: number;
    author_id: number;
    division_points: {
      division_id: number;
      points: number;
      solves: number;
    }[];
    attachments: {
      name: string;
      url: string;
    }[];
  }[];
  categories: {
    id: number;
    name: string;
    color: string;
  }[];
  authors: Record<
    number,
    {
      name: string;
      avatar_url: string;
    }
  >;
  divisions: Record<
    number,
    {
      name: string;
    }
  >;
  team: {
    users: Record<
      number,
      {
        name: string;
        avatar_url: string;
      }
    >;
    solves: Record<
      number,
      {
        solved_at: Date;
        user_id: number;
      }
    >;
  };
};

export function renderChallenges(
  element: HTMLElement,
  challenge_json: ChallengesData,
) {
  element.innerHTML = "";
  render(
    () => <ChallengesComponent challenge_json={challenge_json} />,
    element,
  );
}

document.addEventListener("DOMContentLoaded", () => {
  render(
    () => <Toaster position="top-center" gutter={8} />,
    document.querySelector("#toaster"),
  );

  render(() => <CommandMenu />, document.body);

  document.body.addEventListener("pageRefresh", () => {
    location.reload();
  });

  document.body.addEventListener(
    "toast",
    (
      evt: Event & { detail: { kind: "success" | "error"; message: string } },
    ) => {
      if (evt.detail.kind === "success") {
        toast.success(evt.detail.message);
      } else if (evt.detail.kind === "error") {
        toast.error(evt.detail.message);
      } else {
        console.log("Unknown toast kind", evt.detail.kind);
      }
    },
  );
});
