<script lang="ts">
  import { page } from "$app/stores";
  import {
    getRoom
  } from "../../../requests/room.request";
  import { getContext, onDestroy, onMount, setContext } from "svelte";
  import { writable, type Writable } from "svelte/store";
  import { messages, connect, events, handlers } from "../../../store/ws";
  import type { Member, ParcoursRead, Room } from "../../../types/room.type";
  import { afterNavigate, beforeNavigate, goto } from "$app/navigation";
  import AskPseudo from "../../../components/rooms/AskPseudo.svelte";
  import Members from "../../../components/rooms/Members.svelte";
  import ExerciceSelector from "../../../components/exos/ExerciceSelector.svelte";
  import RoomHead from "../../../components/rooms/RoomHead.svelte";
  import ParcoursList from "../../../components/rooms/ParcoursList.svelte";
  import ParcoursCreate from "../../../components/rooms/ParcoursCreate.svelte";
  import ParcoursDetails from "../../../components/rooms/ParcoursDetails.svelte";
  import Challenge from "../../../components/rooms/Challenge.svelte";
  import { refreshRequest } from "../../../requests/auth.request";
  import Waiting from "../../../components/rooms/Waiting.svelte";
  import { env } from "$env/dynamic/public";

  const p = "p";

  const { isAuth } = getContext<{ isAuth: Writable<boolean> }>("auth");

  const room = writable<Room | null>(null);
  const member = writable<Member | null>(null);
  const parcours = writable<ParcoursRead | null>(null);
  const ws = connect(`${env.PUBLIC_WS_BASE}room/${$page.params.slug}`);

  setContext("room", room);
  setContext("member", member);
  setContext("parcours", parcours);
  setContext("ws", { send: (type: string, data: Object) => ws.send({ type, data }), ws: ws.ws });
  const { error, info } = getContext("notif");
  const onMessage = (payload: { type: string; data: any }) => {
    if (payload == undefined) return;
    const { type, data } = payload;
    switch (type) {
      case "loggedIn":
        close();
        member.set(data.member);
        if (data.member.reconnect_code != "") {
          sessionStorage.setItem("reconnect", data.member.reconnect_code);
        }
        getRoom($page.params.slug, !$isAuth ? data.member.clientId : null).then((r) => {
          room.set(r);
        });

        const parcoursId =
          $page.url.searchParams.get("p") ||
          $page.url.searchParams.get("corr") ||
          $page.url.searchParams.get("c");
        if (parcoursId != null) {
          ws.send({ type: "sub_parcours", data: { parcours_id: parcoursId } });
        }

        return;


      case "accepted":
        close();
        if ($page.url.searchParams.get("a") == "waiting") {
          goto(`?`);
        }
        member.set(data.member);

        getRoom($page.params.slug, !$isAuth ? data.member.clientId : null).then((r) => {
          room.set(r);
        });
        if (!$isAuth) {
          sessionStorage.setItem("reconnect", data.member.reconnect_code);
        }
        const parcours_id =
          $page.url.searchParams.get("p") ||
          $page.url.searchParams.get("corr") ||
          $page.url.searchParams.get("c");
        if (parcours_id != null) {
          ws.send({ type: "sub_parcours", data: { parcours_id: parcours_id } });
        }
        return;


      case "waiting":
        close();
        $member = { ...data.waiter, room: data.room };
        goto(`?${new URLSearchParams({ a: "waiting" })}`);
        return;

      case "refused":
        error("Refusé", "L'administrateur a refusé votre demande");
        close();
        ws?.close(1000);
        goto("/room/join");
        return;

      case "leaved":
        if (data.member.id_code == $member!.id_code) {
          ws?.close(1000);
          goto("/room/join");
          sessionStorage.removeItem("reconnect");
          return;
        }
        if ($room != null) {
          info("Départ", `*${data.member.username}* n'est plus dans la salle`);
          $room.members = [
            ...$room.members.filter((r) => "waiter_id" in r || r.id_code != data.member.id_code)
          ];
        }
        return;
      case "banned":
        error("Ban", "Vous avez été banni de la salle par l'administrateur");
        ws?.close(1000);
        goto("/room/join");
        sessionStorage.removeItem("reconnect");
        return;

      case "deleted":
        info("Suppression", "La salle a été supprimée par l'administrateur");
        ws.close(1000);
        goto("/room/join");
        return;

      case "error":
        const { code, msg } = data;
        if (code == 401) {
          if ($isAuth) {
            ws.send({ type: "join", data: { token: localStorage.getItem("token") } });
          } else {
            show(
              //Ask for pseudo and try join
              AskPseudo,
              {
                validate: (p: string) => {

                  if (p.startsWith("#")) {
                    ws.send({ type: "login", data: { reconnect_code: p.replace("#", "") } });
                  } else {
                    ws.send({ type: "join", data: { username: p } });
                  }

                  //close();
                }
              },
              () => {
              }
            );
          }
        }
        if (code == 422 && msg.toLowerCase().indexOf("signature expired") > -1) {
          const refresh = localStorage.getItem("refresh");
          if (refresh == null) return;
          refreshRequest(refresh).then((r) => {
            localStorage.setItem("access", r.access_token);
            ws.send({ type: "join", data: { token: r.access_token } });
          });
        } else {
          error("Erreur", "Message : " + msg);
        }
        if (code == 404) {
          ws.close(1000);
          goto("/room/join");
        }
        return;
      case "waiter":

        if ($room != null) {
          $room.members = [...$room?.members, data.waiter];
        }
        return;

      case "disconnect_waiter":
        if ($room != null) {
          $room.members = [
            ...$room?.members.filter((m) => "id_code" in m || m.waiter_id != data.waiter.waiter_id)
          ];
        }
        return;

      case "successfullyRefused":
        if ($room != null) {
          $room.members = [
            ...$room?.members.filter((m) => "id_code" in m || m.waiter_id != data.waiter_id)
          ];
        }
        return;
      case "update_parcours":
        if ($room != null) {
          $room.parcours = [
            ...$room.parcours.map((c) => {
              if (c.id_code == data.parcours.id_code) {
                return data.parcours;
              }
              return c;
            })
          ];
        }
        return;

      case "parcours_stats":
        if ($room != null) {
          $room.parcours = [
            ...$room.parcours.map((c) => {
              if (c.id_code == data.parcours.id_code) {
                return data.parcours;
              }
              return c;
            })
          ];
        }
        return;

      case "joined":
        if ($room != null) {
          info("Arrivée", `*${data.member.username}* a rejoint la salle`)
          $room.members = [
            ...$room?.members.filter(
              (m) =>
                ("id_code" in m && m.id_code != data.member.id_code) ||
                ("waiter_id" in m && m.waiter_id != data.member.id_code)
            ),
            data.member
          ];
        }
        return;

      case "connect":
        if ($room != null) {
          $room.members = [
            ...$room?.members.map((r) => {
              if ("id_code" in r && r.id_code == data.member.id_code) {
                return data.member;
              }
              return r;
            })
          ];
        }
        return;

      case "disconnect":
        if ($room != null) {
          $room.members = [
            ...$room?.members.map((r) => {
              if ("id_code" in r && r.id_code == data.member.id_code) {
                return data.member;
              }
              return r;
            })
          ];
        }
        return;
      case "add_parcours":
        if ($room != null) {
          $room.parcours = [...$room?.parcours, data.parcours];
        }
        return;
      case "del_parcours":
        if ($room != null) {
          $room.parcours = [...$room?.parcours.filter((p) => p.id_code != data.parcours_id)];
        }
        return;
      case "new_visibility":
        if ($room != null) {
          $room.public = data.public;
        }
        return;
      case "new_name":
        if ($room != null) {
          $room.name = data.name;
        }
        return;
    }
  };
  const { show, close } = getContext<{ show: Function; close: Function }>("modal");
  const onEvent = (event: { type: string; e: Event | CloseEvent }) => {
    if (event == undefined) return;
    const { type, e } = event;
    if (type == "open") {
      //On the open of ws
      if ($isAuth) {
        ws.send({ type: "login", data: { token: localStorage.getItem("token") } }); // try to login, if denied the message handler tries to join
      } else {
        const reconnect_code = sessionStorage.getItem("reconnect");
        if (reconnect_code !== null) {
          // Try to log in if a reconnect code is found
          ws.send({ type: "login", data: { reconnect_code } });
          return;
        }
        show(
          //Ask for pseudo and try join
          AskPseudo,
          {
            validate: (p: string) => {
              if (p.startsWith("#")) {
                ws.send({ type: "login", data: { reconnect_code: p.replace("#", "") } });
              } else {
                ws.send({ type: "join", data: { username: p } });
              }
              //close();
            }
          },
          () => {
          }
        );
      }
    }
  };

  const parcoursMessage = (payload: { type: string; data: any }) => {
    if (payload == undefined || ($parcours == null && $member == null)) return;

    const { type, data } = payload;
    switch (type) {
      case "challenge":
        if ($parcours != null && $member != null) {
          if (!!$parcours.challenges[data.challenger.id_code]) {
            $parcours.challenges[data.challenger.id_code].challenges = [
              ...$parcours.challenges[data.challenger.id_code].challenges,
              { ...data.challenges[0] }
            ];
          } else {
            $parcours.challenges[data.challenger.id_code] = { ...data };
          }
          if (data.challenges[0].validated == true && data.challenger.id_code == $member.id_code) {
            $parcours.validated = true;
          }
        }
        return;

      case "newRanks":
        if ($parcours != null) {
          $parcours.rank = data.rank;
          $parcours.memberRank = data.avgRank;
        }
        return;
      case "newTops":
        if ($parcours != null) {
          $parcours.tops = data.tops;
          $parcours.ranking = data.avgTops;
        }
        return;
      case "edit_parcours":
        if ($parcours != null && data.parcours.id_code == $parcours.id_code) {
          parcours.update((p) => {
            if (p == null) return p;
            return {
              ...p,
              name: data.parcours.name,
              time: data.parcours.time,
              max_mistakes: data.parcours.max_mistakes
            };
          });
          if (data.parcours.update_challenges) {
            $parcours.exercices = data.parcours.exercices;
            $parcours.challenges = {};
            $parcours.tops = [];
            $parcours.ranking = [];
            $parcours.validated = false;
          } else {
            $parcours.validated = $parcours.pb.mistakes <= $parcours.max_mistakes;
          }
        }
        return
      case "challenge_change":
        if ($parcours != null && $member != null && !!$parcours.challenges[data.member]) {
          $parcours.challenges[data.member].challenges = $parcours.challenges[
            data.member
            ].challenges.map((c) => {
            if (c.id_code == data.challenge.id_code) {
              return data.challenge;
            }
            return c;
          });
          $parcours.challenges[data.member].challenger.validated = data.validated;
          if (data.member == $member.id_code) {
            $parcours.validated = data.validated;
          }
        }
        return;

      case "update_challenges":
        if ($parcours != null && $member != null) {
          const { challenger, challenges } = data;
          if (challenges.length != 0) {
            $parcours.challenges[challenger.id_code] = data;
          }
          if (challenger.id_code == $member.id_code) {
            $parcours.validated = data.challenger.validated;
          }
        }
        return;
    }
  };

  handlers.update((h) => {
    return { ...h, [$page.params.slug]: onMessage };
  });
  handlers.update((h) => {
    return { ...h, parcours: parcoursMessage };
  });
  onDestroy(() => {
    handlers.update((h) => {
      return { ...h, [$page.params.slug]: null };
    });
    handlers.update((h) => {
      return { ...h, parcours: null };
    });
  });
  $: onEvent($events[0]);

  let fade = false;

  beforeNavigate((n) => {
    if(!n.from || !n.to) return;
    console.log('NAVIGATION', n.to.url)
    if (fade == false && !!n.to?.url) {
      fade = true;
      n.cancel();

      setTimeout(() => {
        if (!!n.to?.url) {
          goto(n.to?.url);
        }
      }, 100);
    } else {
      fade = false;
    }
  });
  afterNavigate((n) => {
    if(!n.from || !n.to) return;

    let oldParcoursId =
      n.from?.url.searchParams.get("p") ||
      n.from?.url.searchParams.get("corr") ||
      n.from?.url.searchParams.get("c");
    let newParcoursId =
      n.to?.url.searchParams.get("p") ||
      n.to?.url.searchParams.get("corr") ||
      n.to?.url.searchParams.get("c");

    oldParcoursId = oldParcoursId == "new" ? null : oldParcoursId;
    newParcoursId = newParcoursId == "new" ? null : newParcoursId;

    if (oldParcoursId != null && newParcoursId == null) {
      ws.send({ type: "unsub_parcours", data: { parcours_id: oldParcoursId } });
      $parcours = null;
    }
    /* if (oldParcoursId == newParcoursId) return;
    if (oldParcoursId == null && newParcoursId != null) {

        //ws.send({ type: 'sub_parcours', data: { parcours_id: newParcoursId } });
    } else  */
  });


  onDestroy(() => {

    ws.close(1000);
  });

  $: console.log($page.url.searchParams.toString(),$page.url.searchParams.get('p'))

</script>

<div class="container">
  <div class="full" class:fade>
    {#if $page.url.searchParams.get('a') == "waiting"}
      <Waiting />
    {/if}
    {#if $room != null}
      {#if $page.url.searchParams.get("p") == null && $page.url.searchParams.get('c') == null && $page.url.searchParams.get('corr') == null}
        <div class="room">
          <div class="head">
            <RoomHead />
          </div>
          <div class="parcours">
            <ParcoursList />
          </div>
          <div class="members">
            <Members />
          </div>
        </div>
      {/if}


      {#if $page.url.searchParams.get('p') == 'new'}
        <ParcoursCreate />

      {:else if $page.url.searchParams.get('p') != 'new' && $page.url.searchParams.get('p') != null}
        {#if $page.url.searchParams.get('edit') != '1'}
          <ParcoursDetails id_code={$page.url.searchParams.get('p')} />
        {:else}
          <ParcoursCreate id_code={$page.url.searchParams.get('p')} />
        {/if}
      {/if}
      {#if $page.url.searchParams.get('c') != null}
        <Challenge id_code={$page.url.searchParams.get('c')} />
      {/if}
      {#if $page.url.searchParams.get('corr') != null}
        <Challenge id_code={$page.url.searchParams.get('corr')} corrige />
      {/if}
    {/if}



  </div>
</div>

<style lang="scss">
  @import "../../../mixins.scss";

  .full {
    width: 100%;
    height: 100%;
    transition: 0.2s;
    opacity: 1;

  }

  .fade {
    opacity: 0;
    transform: translateX(-50px);
    transform: scale(0.95);
  }

  .co {
    color: red;
  }

  .head {
    grid-row: 1/2;
    grid-column: 1/3;
  }

  .members {
    grid-row: 2/3;
    grid-column: 1/2;
  }

  .parcours {
    grid-row: 2/2;
    grid-column: 2/2;
  }

  .room {
    display: grid;
    grid-template-columns: 1fr 3fr;
    grid-template-rows: 60px 1fr;
    column-gap: 50px;
    row-gap: 10px;
    height: 100%;
    padding: 7px 15px;
    @include down(800) {
      display: flex;
      flex-direction: column;
      padding: 13px;
      gap: 10px;
    }
  }
</style>
