"use client";

import "@/components/pages/participant/opportunities/custom-styles.css";
import { useRouter } from "next/navigation";
import { useMemo, useReducer, useState } from "react";
import LoadingBar from "react-top-loading-bar";
import { IoIosCloseCircle } from "react-icons/io";
import {
  Accordion,
  AccordionItem,
  Checkbox,
  CheckboxGroup,
  Chip,
  cn,
  Modal,
  ModalBody,
  ModalContent,
  ModalFooter,
  ModalHeader,
  Pagination,
  Switch,
  useDisclosure,
} from "@nextui-org/react";
import { FaFilter } from "react-icons/fa6";
import useSWR from "swr";

import { INTERNAL_PAGES } from "@/constants/pages-mapping/internal-pages-mapping";
import { OppsResponse } from "@/types/opportunities/opportunity-types";
import { Options } from "@/types/options/options-data-types";
import { parseOpportunity } from "@/utils/opportunities/parse-opportunity";
import { FiltersCategories } from "@/constants/opportunity-list/opportunity-list-constants";
import {
  filterReducer,
  initialFilterState,
} from "@/reducers/filter-opp-reducer";
import Button from "@/components/common/button";
import Headline from "@/components/pages/participant/opportunities/partials/headline";
import ListSort from "@/components/pages/participant/opportunities/partials/list-sort";
import NoResults from "@/components/pages/participant/opportunities/partials/no-results";
import OpportunityCard from "@/components/pages/participant/opportunities/partials/opportunity-card";
import CTAOpportunities from "@/components/pages/participant/opportunities/partials/cta-opportunities";
import {
  getOpportunitiesLoggedIn,
  getOpportunitiesNotLoggedIn,
} from "@/services/opportunities";
import Loading from "@/components/pages/participant/opportunities/partials/loading";
import useMediaBreakpoints, {
  MediaBreakpoints,
} from "@/hooks/useMediaBreakpoints";

type OpportunitiesListProps = {
  userId: number;
  options: {
    currencyOptions: Options;
    typeOfStudyOptions: Options;
    paymentMethods: Options;
    jobTopicOptions: Options;
    formatOfResearchOptions: Options;
  };
};

type FilterAccordionProps = {
  listOfCategories: string[];
  renderItem: (category: string) => React.ReactNode;
};

function FilterAccordion({
  listOfCategories,
  renderItem,
}: FilterAccordionProps) {
  const categoryLabel = (key: string) => {
    switch (key) {
      case "typeOfStudy":
        return "Type of study";
      case "formatOfResearch":
        return "Format of research";
      case "researchTopic":
        return "Research topic";
      default:
        return key;
    }
  };

  return (
    <Accordion
      isCompact
      defaultExpandedKeys={[
        FiltersCategories.TypeOfStudy,
        FiltersCategories.FormatOfResearch,
        FiltersCategories.ResearchTopic,
      ]}
      selectionMode="multiple"
      variant="light"
    >
      {listOfCategories.map((category) => (
        <AccordionItem
          key={category}
          aria-label={category}
          classNames={{
            base: "py-4",
            title: "font-inter font-bold text-black dark:text-white text-sm",
          }}
          title={categoryLabel(category)}
        >
          {renderItem(category)}
        </AccordionItem>
      ))}
    </Accordion>
  );
}

function OpportunitiesList({
  userId,
  options: {
    currencyOptions,
    typeOfStudyOptions,
    paymentMethods,
    jobTopicOptions,
    formatOfResearchOptions,
  },
}: OpportunitiesListProps) {
  const [currentPage, setCurrentPage] = useState(1);
  const screenSize = useMediaBreakpoints();
  const itemsPerPage =
    screenSize === MediaBreakpoints.MOBILE
      ? 4
      : screenSize === MediaBreakpoints.TABLET
        ? 6
        : screenSize === MediaBreakpoints.DESKTOP
          ? 9
          : screenSize === MediaBreakpoints.XXL
            ? 12
            : 20;
  const { isOpen, onOpen, onClose } = useDisclosure();
  const [progress, setProgress] = useState(0);
  const [sortOrder, setSortOrder] = useState("recentlyAdded");
  const router = useRouter();
  const [filterState, dispatch] = useReducer(filterReducer, initialFilterState);
  const [hasApplied, setHasApplied] = useState(true);

  const filterModel = {
    typeOfStudy: filterState.typeOfStudy,
    formatOfResearch: filterState.formatOfResearch,
    researchTopic: filterState.researchTopic,
  };

  const { data, isLoading } = useSWR<OppsResponse>(
    [
      `opportunities`,
      itemsPerPage,
      currentPage,
      sortOrder,
      filterModel,
      userId,
      hasApplied,
    ],
    () => {
      let sort: { colId: string; sort: "desc" | "asc" } = {
        colId: "publishDate",
        sort: "desc",
      };

      if (sortOrder === "recentlyAdded") {
        sort = { colId: "publishDate", sort: "desc" };
      } else if (sortOrder === "happeningFirst") {
        sort = { colId: "startDate", sort: "asc" };
      } else if (sortOrder === "happeningLast") {
        sort = { colId: "startDate", sort: "desc" };
      }

      const cleanedFilterModel = Object.fromEntries(
        Object.entries(filterModel).filter(([_, values]) => values.length > 0),
      );

      if (userId !== undefined && userId !== null) {
        return getOpportunitiesLoggedIn(
          itemsPerPage,
          currentPage,
          sort,
          cleanedFilterModel,
          userId,
          hasApplied,
        );
      } else {
        return getOpportunitiesNotLoggedIn(
          itemsPerPage,
          currentPage,
          sort,
          cleanedFilterModel,
        );
      }
    },
    { revalidateOnFocus: false },
  );

  const listOfOpps = useMemo(() => {
    return data?.data || [];
  }, [data]);
  const totalOpportunities = data?.totalRecords || 0;
  const totalPages = Math.ceil(totalOpportunities / itemsPerPage);

  const optionsMapping: Record<string, Options> = {
    typeOfStudy: typeOfStudyOptions,
    formatOfResearch: formatOfResearchOptions,
    researchTopic: jobTopicOptions,
  };

  const handleOnClickCard = (jobId: number) => {
    router.push(`${INTERNAL_PAGES.participant.opportunities.main}/${jobId}`);
  };

  const handleFilterChange = async (newValues: string[], category: string) => {
    setProgress(50);
    window.scrollTo({ top: 0, behavior: "smooth" });

    // Artificial delay. The idea here is just to show the loading animation instead of almost immediately ending
    await new Promise((resolve) => setTimeout(resolve, 300));

    const parsedValues = newValues.map(Number);

    switch (category) {
      case "typeOfStudy":
        dispatch({ type: "SET_TYPE_OF_STUDY", payload: parsedValues });
        break;
      case "formatOfResearch":
        dispatch({ type: "SET_FORMAT_OF_RESEARCH", payload: parsedValues });
        break;
      case "researchTopic":
        dispatch({ type: "SET_RESEARCH_TOPIC", payload: parsedValues });
        break;
      default:
        return;
    }

    setCurrentPage(1);
    setProgress(100);
  };

  const handleSortChange = async (order: string) => {
    setProgress(50);
    window.scrollTo({ top: 0, behavior: "smooth" });

    // Artificial delay. The idea here is just to show the loading animation instead of almost immediately ending
    await new Promise((resolve) => setTimeout(resolve, 300));
    setCurrentPage(1);
    setSortOrder(order);
    setProgress(100);
  };

  const handleClearFilters = () => {
    dispatch({ type: "CLEAR_FILTERS" });
  };

  const handlePageChange = (page: number) => {
    setCurrentPage(page);
    window.scrollTo({ top: 0, behavior: "smooth" });
  };

  const idToName = (id: string, key: string) => {
    const options = optionsMapping[key];

    if (!options) {
      return "";
    }

    const option = options.find((option) => option.id === Number(id));
    return option ? option.title : `Unknown ${key} (${id})`;
  };

  const handleHasAppliedChange = async (value: boolean) => {
    setProgress(50);
    window.scrollTo({ top: 0, behavior: "smooth" });
    // Artificial delay. The idea here is just to show the loading animation instead of almost immediately ending
    await new Promise((resolve) => setTimeout(resolve, 300));

    setCurrentPage(1);
    setHasApplied(value);
    setCurrentPage(1);
    setProgress(100);
  };

  const renderChips = (key: string, values: any[]) => {
    return values.map((value) => (
      <Chip
        key={`${key}-${value}`}
        classNames={{
          content: "font-semibold py-1",
          base: "bg-slate-200 dark:bg-slate-700 text-black dark:text-white font-inter font-extrabold text-xs",
        }}
        onClose={() => {
          dispatch({
            type: "REMOVE_FILTER",
            payload: { type: key, value },
          });
        }}
      >
        {idToName(value, key)}
      </Chip>
    ));
  };

  const categoryDataGenerator = (category: string) => {
    const selectedValues = filterState[category] || [];
    const options = optionsMapping[category];

    const countsMap = {
      typeOfStudy: data?.typeOfStudyCounts || {},
      formatOfResearch: data?.formatOfResearchCounts || {},
      researchTopic: data?.jobTopicCounts || {},
    };

    const totals = countsMap[category as keyof typeof countsMap];
    const availableOptions = options.filter(
      (option) => (totals[option.id] || 0) > 0,
    );

    return (
      <CheckboxGroup
        classNames={{
          label: "font-bold text-gray-900 dark:text-white mb-3",
          wrapper: "gap-2",
        }}
        value={selectedValues.map(String)}
        onChange={(newValues) => handleFilterChange(newValues, category)}
      >
        {availableOptions.map((option) => (
          <Checkbox
            key={option.id}
            classNames={{
              base: "inline-flex w-full max-w-none hover:bg-gray-50 dark:hover:bg-white/5 rounded-md p-2 -m-2 transition-colors",
              wrapper: "before:border-gray-300 dark:before:border-gray-600",
              label: "font-medium text-gray-700 dark:text-gray-300",
            }}
            color="default"
            value={String(option.id)}
          >
            <span className="flex justify-between w-full">
              <span>{option.title}</span>
              <span className="text-gray-500 dark:text-gray-400 font-normal ps-1">
                ({totals[option.id] || 0})
              </span>
            </span>
          </Checkbox>
        ))}
      </CheckboxGroup>
    );
  };

  const hasOpportunities = listOfOpps.length > 0;
  const showClearAllBtn =
    filterState.typeOfStudy.length !== 0 ||
    filterState.researchTopic.length !== 0 ||
    filterState.formatOfResearch.length !== 0;

  return (
    <div className="w-full min-h-screen py-8 bg-participant-light dark:bg-participant-dark">
      {/* Headline */}
      <Headline />
      <div className="flex flex-row items-center justify-center xl:items-start xl:justify-between py-6 xl:ms-8">
        {/* Filters */}
        <div className="hidden xl:block xl:w-3/12 xl:pe-12 xxl:pe-48 xl:space-y-5">
          <div className="flex justify-between">
            <h4 className="inline-flex font-inter items-center font-black text-2xl text-black dark:text-white tracking-tight">
              <FaFilter className="size-4 me-2" />
              Filters
            </h4>
            {showClearAllBtn && (
              <Button
                btnLabel="Clear all"
                customVariant="transparent"
                endContent={<IoIosCloseCircle className="size-4" />}
                size="sm"
                onClick={handleClearFilters}
              />
            )}
          </div>
          {userId && (
            <Switch
              classNames={{
                base: cn(
                  "inline-flex flex-row-reverse w-full max-w-md items-center",
                  "justify-between cursor-pointer gap-2 p-4 border border-gray-300 dark:border-white/10 bg-gray-400/10 dark:bg-white/5",
                ),
                wrapper: "p-0 h-4 overflow-visible",
                thumb: cn(
                  "w-6 h-6 border-2 shadow-lg",
                  "group-data-[hover=true]:border-primary",
                  "group-data-[selected=true]:ms-6",
                  "group-data-[pressed=true]:w-7",
                  "group-data-[selected]:group-data-[pressed]:ms-4",
                ),
              }}
              isSelected={hasApplied}
              onValueChange={handleHasAppliedChange}
            >
              <div className="flex flex-col gap-1">
                <p className="text-sm font-semibold">
                  Show applied opportunities
                </p>
                <p className="text-sm text-gray-700 dark:text-gray-400">
                  Show/hide opportunities you have applied to before.
                </p>
              </div>
            </Switch>
          )}
          <div className="px-4 border border-gray-300 dark:border-white/10 bg-gray-400/10 dark:bg-white/5">
            <FilterAccordion
              listOfCategories={[
                FiltersCategories.TypeOfStudy,
                FiltersCategories.FormatOfResearch,
                FiltersCategories.ResearchTopic,
              ]}
              renderItem={(category) => {
                return categoryDataGenerator(category);
              }}
            />
          </div>
        </div>

        <div className="flex flex-col xl:w-10/12">
          <div className="flex flex-row justify-center xl:justify-between space-x-4 mb-4">
            {/* Current active filters (desktop) */}
            <div className="hidden xl:flex xl:flex-wrap xl:gap-2">
              <div className="flex flex-wrap gap-2">
                {Object.entries(filterState as Record<string, any[]>).map(
                  ([key, values]) => renderChips(key, values),
                )}
              </div>
            </div>

            {/* Current active filters (mobile) */}
            <div className="flex justify-start xl:hidden">
              <Button
                btnLabel="Filters"
                className="text-black dark:text-white font-inter font-bold"
                endContent={<FaFilter className="size-3" />}
                size="sm"
                variant="ghost"
                onClick={onOpen}
              />
            </div>

            {/* Sort */}
            <div className="flex flex-col space-y-2 xl:pe-6">
              <ListSort
                selectedValue={sortOrder}
                onSortChange={handleSortChange}
              />
            </div>
          </div>
          <div className="flex xl:hidden flex-wrap gap-2 mx-5 my-2 items-center justify-center">
            {Object.entries(filterState as Record<string, any[]>).map(
              ([key, values]) => renderChips(key, values),
            )}
          </div>

          {!isLoading && (
            <span className="flex font-inter text-center items-center justify-center xl:text-end xl:items-end xl:justify-end my-6 xl:my-0 xl:pb-3 me-6 font-medium text-xs text-black/50 dark:text-white">
              {`Showing ${listOfOpps.length} opportunities from a total of ${totalOpportunities}.`}
            </span>
          )}

          {/* Loading state */}
          {isLoading && <Loading loadingMessage="Loading opportunities..." />}

          {/* Opportunities Grid - No results found */}
          {!hasOpportunities && !isLoading && <NoResults />}

          {/* Opportunities Grid */}
          {hasOpportunities && (
            <main
              className="grid grid-cols-1 justify-center items-center m-auto md:grid-cols-2 lg:grid-cols-3 xxxl:grid-cols-4 2xxl:grid-cols-5 xl:pe-6 xxl:pe-24 gap-4 place-items-center h-full mx-12 lg:mx-0"
              id="main-content"
            >
              {listOfOpps.map((item, index) => {
                const parsedOpportunity = parseOpportunity(
                  item,
                  currencyOptions,
                  typeOfStudyOptions,
                  paymentMethods,
                  jobTopicOptions,
                  formatOfResearchOptions,
                );

                return (
                  <OpportunityCard
                    key={`${item}-${index}`}
                    currencyOptions={currencyOptions}
                    opportunityDetail={parsedOpportunity}
                    onClickCard={(jobId) => handleOnClickCard(jobId)}
                  />
                );
              })}
            </main>
          )}

          {hasOpportunities && (
            <div className="flex flex-col items-center justify-center m-auto py-6">
              <Pagination
                loop
                showControls
                classNames={{
                  cursor:
                    "bg-participant-bittersweet-50 dark:bg-participant-dianne-900 text-black dark:text-white font-bold",
                  item: "bg-white dark:bg-white/5",
                  next: "bg-white dark:bg-white/5",
                  prev: "bg-white dark:bg-white/5",
                }}
                color="primary"
                initialPage={1}
                page={currentPage}
                total={totalPages}
                onChange={handlePageChange}
              />
            </div>
          )}
        </div>
      </div>

      {/* Loading bar */}
      <LoadingBar
        className="bg-participant-bittersweet dark:bg-participant-cerulean"
        height={4}
        progress={progress}
        onLoaderFinished={() => setProgress(0)}
      />

      {/* Filters modal (mobile only) */}
      <Modal
        backdrop="blur"
        isOpen={isOpen}
        scrollBehavior="inside"
        size="full"
        onClose={onClose}
      >
        <ModalContent>
          <ModalHeader className="flex flex-col gap-1">
            <h4 className="inline-flex font-inter items-center font-black text-2xl text-black dark:text-white tracking-tight">
              <FaFilter className="size-4 me-2" />
              Filters
            </h4>
          </ModalHeader>
          <ModalBody>
            {userId && (
              <Switch
                classNames={{
                  base: cn(
                    "inline-flex flex-row-reverse w-full max-w-md items-center",
                    "justify-between cursor-pointer rounded-lg gap-2 p-4 border border-gray-300 dark:border-white/10",
                  ),
                  wrapper: "p-0 h-4 overflow-visible",
                  thumb: cn(
                    "w-6 h-6 border-2 shadow-lg",
                    "group-data-[hover=true]:border-primary",
                    "group-data-[selected=true]:ms-6",
                    "group-data-[pressed=true]:w-7",
                    "group-data-[selected]:group-data-[pressed]:ms-4",
                  ),
                }}
                isSelected={hasApplied}
                onValueChange={handleHasAppliedChange}
              >
                <div className="flex flex-col gap-1">
                  <p className="text-sm font-semibold">
                    Show applied opportunities
                  </p>
                  <p className="text-sm text-gray-700 dark:text-gray-400">
                    Show/hide opportunities you have applied to before.
                  </p>
                </div>
              </Switch>
            )}
            <FilterAccordion
              listOfCategories={[
                FiltersCategories.TypeOfStudy,
                FiltersCategories.FormatOfResearch,
                FiltersCategories.ResearchTopic,
              ]}
              renderItem={(category) => {
                return categoryDataGenerator(category);
              }}
            />
          </ModalBody>
          <ModalFooter>
            {showClearAllBtn && (
              <Button
                fullWidth
                btnLabel="Clear all filters"
                customVariant="transparent"
                size="sm"
                onClick={handleClearFilters}
              />
            )}
          </ModalFooter>
        </ModalContent>
      </Modal>
      <CTAOpportunities />
    </div>
  );
}

export default OpportunitiesList;
