import { itGetCached } from "@/fetchHelpers";
import { useCockpitStore } from "@/stores/cockpit";
import type { CourseSession, DueDate } from "@/types";
import eventBus from "@/utils/eventBus";
import { useRouteLookups } from "@/utils/route";
import { useLocalStorage } from "@vueuse/core";
import dayjs from "dayjs";
import uniqBy from "lodash/uniqBy";
import log from "loglevel";
import { defineStore } from "pinia";
import { computed, ref } from "vue";
import { useUserStore } from "./user";

const SELECTED_COURSE_SESSIONS_KEY = "selectedCourseSessionMap";

export const useCourseSessionsStore = defineStore("courseSessions", () => {
  const loaded = ref(false);
  const allCourseSessions = ref<CourseSession[]>([]);
  const { inCompetenceProfile, inLearningPath } = useRouteLookups();

  async function loadCourseSessionsData(reload = false) {
    log.debug("loadCourseSessionsData called");
    allCourseSessions.value = await itGetCached(`/api/course/sessions/`, {
      reload: reload,
    });

    const userStore = useUserStore();
    if (userStore.loggedIn) {
      // TODO: refactor after implementing of Klassenkonzept
      await Promise.all(
        allCourseSessions.value.map(async (cs) => {
          sortDueDates(cs.due_dates);
        })
      );

      if (!allCourseSessions.value) {
        throw `No courseSessionData found for user`;
      }
    }

    loaded.value = true;
  }

  const selectedCourseSessionMap = useLocalStorage(
    SELECTED_COURSE_SESSIONS_KEY,
    new Map<string, string>()
  );

  const _currentCourseSlug = ref("");

  const uniqueCourseSessionsByCourse = computed(() =>
    // Im Dashboard wird aktuell ein Widget pro Kurs dargestellt
    // mit dem Fortschritt jeweils einer Durchführung.
    // Dieser Getter wird nur dort benutzt.
    // TODO: Das Dashboard verschwindet evtl. in Zukunft, dann kann dieser Getter weg.
    // @ts-ignore
    uniqBy(allCourseSessions.value, "course.id")
  );

  function selectedCourseSessionForCourse(courseSlug: string) {
    // Wir wollen pro Kurs wissen, welche Durchführung der User zuletzt ausgewählt hat.
    // Die letzte Durchführung wird im localStorage via `selectedCoruseSessionMap`
    // gespeichert und hier geladen.
    // Wenn noch keine Durchführung ausgewählt wurde, wird die erste Durchführung
    // in `courseSessionForCourse` zurückgegeben.
    try {
      const courseSessionId = selectedCourseSessionMap.value.get(courseSlug);
      if (courseSessionId) {
        return allCourseSessions.value.find((cs) => {
          return cs.id === courseSessionId;
        });
      }
    } catch (e) {
      log.error("Error while parsing courseSessions from localStorage", e);
    }

    // Keine Durchführung ausgewählt im localStorage
    return undefined;
  }

  function _switchCourseSession(courseSession: CourseSession) {
    log.debug("switchCourseSession", courseSession);
    selectedCourseSessionMap.value.set(courseSession.course.slug, courseSession.id);
    // Emit event so that the App can re-render with the new courseSession
    eventBus.emit("switchedCourseSession", courseSession.id);
  }

  function getCourseSessionById(courseSessionId: string) {
    return allCourseSessions.value.find((cs) => {
      return courseSessionId === cs.id;
    });
  }

  function switchCourseSessionById(courseSessionId: string) {
    const courseSession = allCourseSessions.value.find((cs) => {
      return courseSessionId === cs.id;
    });
    if (courseSession) {
      _switchCourseSession(courseSession);
      return true;
    } else {
      return false;
    }
  }

  function courseSessionForCourse(courseSlug: string) {
    if (courseSlug) {
      const courseSession = selectedCourseSessionForCourse(courseSlug);
      if (courseSession) {
        return courseSession;
      } else {
        // return first if there is no selected courseSession
        return allCourseSessions.value.find((cs) => {
          return cs.course.slug === courseSlug;
        });
      }
    }

    return undefined;
  }

  function allCourseSessionsForCourse(courseSlug: string) {
    return allCourseSessions.value.filter((cs) => {
      return cs.course.slug === courseSlug;
    });
  }

  const currentCourseSession = computed(() => {
    return courseSessionForCourse(_currentCourseSlug.value);
  });

  const allCurrentCourseSessions = computed(() => {
    return allCourseSessionsForCourse(_currentCourseSlug.value);
  });

  const currentCourseSessionHasCockpit = computed(() => {
    if (currentCourseSession.value) {
      return hasCockpit(currentCourseSession.value);
    }

    return false;
  });

  const hasCourseSessionPreview = computed(() => {
    const isCourseExpert =
      currentCourseSession.value && currentCourseSessionHasCockpit.value;
    return Boolean(isCourseExpert && (inLearningPath() || inCompetenceProfile()));
  });

  function hasCockpit(courseSession: CourseSession) {
    const userStore = useUserStore();
    return (
      useCockpitStore().hasMentorCockpitType ||
      useCockpitStore().hasExpertCockpitType ||
      // for legacy reasons: don't forget course session supervisors!
      userStore.course_session_experts.includes(courseSession.id) ||
      userStore.is_superuser
    );
  }

  function allDueDates() {
    const allDueDatesReturn: DueDate[] = [];

    allCourseSessions.value?.forEach((cs) => {
      allDueDatesReturn.push(...cs.due_dates);
    });

    sortDueDates(allDueDatesReturn);
    return allDueDatesReturn;
  }

  function sortDueDates(dueDates: DueDate[]) {
    dueDates.sort((a, b) => {
      const dateA = dayjs(a.start);
      const dateB = dayjs(b.start);

      if (!dateA.isValid() && !dateB.isValid()) return 0; // If both are invalid, they are equal
      if (!dateA.isValid()) return 1; // If dateA is invalid, it goes after dateB
      if (!dateB.isValid()) return -1; // If dateB is invalid, it goes after dateA

      return dateA.diff(dateB); // sort by `start`
    });
  }

  return {
    uniqueCourseSessionsByCourse,
    allCurrentCourseSessions,
    courseSessionForCourse,
    getCourseSessionById,
    switchCourseSessionById,
    hasCockpit,
    hasCourseSessionPreview,
    currentCourseSessionHasCockpit,
    allDueDates,

    // use `useCurrentCourseSession` whenever possible
    currentCourseSession,

    loadCourseSessionsData,
    loaded,
    // only used so that `router.afterEach` can switch it
    _currentCourseSlug,

    // only used for unit testing
    allCourseSessions,
  };
});
