"use client";

import Image from "next/image";
import {
  FaChevronLeft,
  FaChevronRight,
  FaLock,
  FaStar,
  FaCheck,
} from "react-icons/fa6";
import { useRouter } from "next/navigation";
import { Card, Chip, useDisclosure, Divider } from "@nextui-org/react";
import React, {
  useCallback,
  useMemo,
  useRef,
  useState,
  useTransition,
} from "react";
import LoadingBar from "react-top-loading-bar";
import DOMPurify from "isomorphic-dompurify";
import parse, { domToReact } from "html-react-parser";
import toast from "react-hot-toast";
import Link from "next/link";
import {
  FcCalendar,
  FcClock,
  FcCollaboration,
  FcGlobe,
  FcMoneyTransfer,
  FcReading,
} from "react-icons/fc";
import { FaUser, FaUserPlus } from "react-icons/fa";

import { Opportunity } from "@/types/opportunities/opportunity-types";
import Breadcrumbs from "@/components/common/breadcrumbs";
import Button from "@/components/common/button";
import { INTERNAL_PAGES } from "@/constants/pages-mapping/internal-pages-mapping";
import { Options } from "@/types/options/options-data-types";
import { FinancialEmploymentProfileSchema } from "@/types/profile/profile-update-schema";
import ProfilePrompt from "@/components/pages/participant/opportunities/partials/profile-prompt";
import ExclusiveToClientModal from "@/components/pages/participant/opportunities/partials/modal-exclusive-client";
import { formatIncentives } from "@/utils/format-incentive";
import { SM_LINKS_PARTICIPANT } from "@/constants/pages-mapping/external-pages-mapping";
import { formatDateRange } from "@/components/pages/participant/opportunities/other-utils";
import { FRIENDLY_TOPICS_ICONS } from "@/utils/opportunities/job-topic-icons";
import useStickyCTA from "@/hooks/useStickyCTA";
import ShareOppSocialMedia from "@/components/pages/participant/opportunities/partials/share-opportunity";

type ParsedOpportunity = {
  currency: string | string[];
  typeOfStudy: string[];
  paymentMethods: string[];
  jobTopic: string;
  formatOfResearch: string[];
};

type OpportunityDetailProps = {
  opportunity: Opportunity;
  parsedOpportunity: ParsedOpportunity;
  hasApplied?: boolean | null;
  userDetails?: FinancialEmploymentProfileSchema;
  employmentOptions: Options;
  industryOptions: Options;
  seniorityOptions: Options;
  isLoggedIn?: boolean;
  userId: number;
  isExclusiveToClient?: boolean;
  currencyOptions: Options;
  jobId: number;
  isExperianJob: boolean; // this is a temp. fix!
  isFullJourney?: boolean; // this is a temp. fix!
  utmParams?: { [key: string]: string | string[] };
};

function OpportunityDetail({
  opportunity,
  parsedOpportunity,
  hasApplied,
  userDetails,
  employmentOptions,
  industryOptions,
  seniorityOptions,
  userId,
  isExclusiveToClient,
  isLoggedIn = false,
  currencyOptions,
  jobId,
  isExperianJob,
  isFullJourney,
  utmParams,
}: OpportunityDetailProps) {
  const { isOpen, onOpen, onClose } = useDisclosure();
  const {
    isOpen: isModalExclusiveOpen,
    onOpen: onOpenModalExclusive,
    onClose: onCloseModalExclusive,
  } = useDisclosure();
  const router = useRouter();
  const [progress, setProgress] = useState(0);
  const ctaRef = useRef<HTMLDivElement | null>(null);
  const { disableProfilePrompt, isModerated } = opportunity.jobInformation;
  const [isPending, startTransition] = useTransition();
  const showProfilePrompt = !!disableProfilePrompt;
  const isUnmoderated = !isModerated;
  const [isLoading, setIsLoading] = useState<string | null>(null);

  const {
    title,
    durationText,
    location,
    startDate,
    description,
    isFeatured,
    incentives,
    thumbnail,
    altTextThumbnail,
    buttonLink,
    buttonText,
    hideLocation,
    startingTime,
    endingTime,
  } = opportunity;

  const { endDate } = opportunity.jobInformation;
  const dateRange = formatDateRange("long", startDate, endDate, true);
  const formattedStart =
    typeof dateRange === "string" ? dateRange : dateRange.formattedStart;
  const formattedEnd =
    typeof dateRange === "string" ? dateRange : dateRange.formattedEnd;

  const buttonTextToShow = buttonText || "Apply to this opportunity";

  const { typeOfStudy, jobTopic, formatOfResearch, paymentMethods } =
    parsedOpportunity;
  const topicLabel = typeof jobTopic === "string" ? jobTopic.trim() : "";
  const TopicIcon =
    FRIENDLY_TOPICS_ICONS[topicLabel as keyof typeof FRIENDLY_TOPICS_ICONS];

  const formatTime = useCallback((timeString: string) => {
    const [hour, minute] = timeString.split(":");
    return `${hour}:${minute}`;
  }, []);

  const cities =
    (location !== null &&
      location.split(/,|&/).map((location) => location.trim())) ||
    [];

  const displayCity =
    !location || hideLocation
      ? "To be confirmed"
      : cities.length > 2
        ? `${cities.slice(0, -1).join(", ")} and ${cities[cities.length - 1]}`
        : cities.length === 2
          ? `${cities[0]} and ${cities[1]}`
          : cities[0];

  const GRID_ITEMS = useMemo(
    () => [
      {
        icon: FcGlobe,
        title: "Location",
        value:
          displayCity === "To be confirmed"
            ? "To be confirmed."
            : `${displayCity}.`,
      },
      {
        icon: FcCalendar,
        title: "Date & time",
        value: isUnmoderated
          ? formattedStart === formattedEnd
            ? `${formattedStart}.`
            : `${formattedStart} to ${formattedEnd}.`
          : formattedStart === formattedEnd
            ? `${formattedStart}${
                startingTime && endingTime
                  ? `, between ${formatTime(startingTime)} and ${formatTime(
                      endingTime,
                    )}`
                  : ""
              }.`
            : `${formattedStart} to ${formattedEnd}${
                startingTime && endingTime
                  ? `, between ${formatTime(startingTime)} and ${formatTime(
                      endingTime,
                    )}`
                  : ""
              }.`,
      },
      {
        icon: FcClock,
        title: "Duration of the study",
        value:
          durationText && durationText.endsWith(".")
            ? durationText
            : `${durationText}.`,
      },
      {
        icon: FcMoneyTransfer,
        title: "Incentive",
        value:
          incentives.length > 0
            ? `${formatIncentives(
                incentives,
                currencyOptions,
                true,
              )} ${paymentMethods
                .map((paymentMethod) => paymentMethod)
                .join(", ")
                .toLowerCase()}.`
            : "To be confirmed.",
      },
      {
        icon: FcReading,
        title: "Study format",
        value:
          typeOfStudy && typeOfStudy.length > 0
            ? `${
                typeOfStudy.length === 1
                  ? typeOfStudy[0].charAt(0).toUpperCase() +
                    typeOfStudy[0].slice(1).toLowerCase()
                  : typeOfStudy[0].charAt(0).toUpperCase() +
                    typeOfStudy[0].slice(1).toLowerCase() +
                    (typeOfStudy.length > 1
                      ? ", " +
                        typeOfStudy
                          .slice(1, -1)
                          .map((format) => format.toLowerCase())
                          .join(", ") +
                        (typeOfStudy.length > 2 ? ", " : " ") +
                        "and " +
                        typeOfStudy[typeOfStudy.length - 1].toLowerCase()
                      : "")
              }.`
            : "Not available.",
      },
      {
        icon: FcCollaboration,
        title: "Format of research",
        value:
          formatOfResearch && formatOfResearch.length > 0
            ? `${
                formatOfResearch.length === 1
                  ? formatOfResearch[0].charAt(0).toUpperCase() +
                    formatOfResearch[0].slice(1).toLowerCase()
                  : formatOfResearch[0].charAt(0).toUpperCase() +
                    formatOfResearch[0].slice(1).toLowerCase() +
                    (formatOfResearch.length > 1
                      ? ", " +
                        formatOfResearch
                          .slice(1, -1)
                          .map((format) => format.toLowerCase())
                          .join(", ") +
                        (formatOfResearch.length > 2 ? ", " : " ") +
                        "and " +
                        formatOfResearch[
                          formatOfResearch.length - 1
                        ].toLowerCase()
                      : "")
              }.`
            : "Not available.",
      },
    ],
    [
      displayCity,
      isUnmoderated,
      formattedStart,
      formattedEnd,
      startingTime,
      endingTime,
      formatTime,
      durationText,
      incentives,
      currencyOptions,
      paymentMethods,
      typeOfStudy,
      formatOfResearch,
    ],
  );

  const isUserDetailsValid = useMemo(() => {
    if (!userDetails) return false;

    const {
      financialProductsIds,
      seniorityLevelIds,
      benefitsIds,
      industryId,
      employmentStatusIds,
      individualIncomeId,
      occupation,
    } = userDetails;

    return Boolean(
      financialProductsIds.length ||
      seniorityLevelIds.length ||
      benefitsIds.length ||
      industryId ||
      employmentStatusIds.length ||
      individualIncomeId ||
      occupation,
    );
  }, [userDetails]);

  const sanitizedDescription = DOMPurify.sanitize(description, {
    ADD_TAGS: ["a", "ol", "li", "ul", "span", "h2", "h3"],
  });

  const liContent = (domNode: any, options: any) => {
    const children = domNode.children || [];

    if (children.length === 1 && children[0].name === "p") {
      return domToReact(children[0].children, options);
    }
    return domToReact(children, options);
  };

  const renderOrderedList = (domNode: any, options: any) => {
    return (
      <ol className="list-decimal list-inside ms-6">
        {domToReact(domNode.children, options)}
      </ol>
    );
  };

  const renderUnorderedList = (domNode: any, options: any) => {
    return (
      <ul className="list-disc list-inside ms-6">
        {domToReact(domNode.children, options)}
      </ul>
    );
  };

  const renderListItem = (domNode: any, options: any) => {
    return <li className="mb-0">{liContent(domNode, options)}</li>;
  };

  const renderAnchor = (domNode: any, options: any) => {
    const url = new URL(domNode.attribs.href);
    url.searchParams.delete("ref");

    return (
      <Link
        className="font-semibold underline decoration-dotted"
        href={url.toString()}
        rel="noopener noreferrer"
        target="_blank"
      >
        {domToReact(domNode.children, options)}
      </Link>
    );
  };

  const options = {
    replace: (domNode: any) => {
      if (!domNode.name) return;

      switch (domNode.name) {
        case "hr":
          return (
            <Divider className="my-4 border-gray-300 dark:border-gray-500" />
          );
        case "a":
          return renderAnchor(domNode, options);
        case "ol":
          return renderOrderedList(domNode, options);
        case "ul":
          return renderUnorderedList(domNode, options);
        case "li":
          return renderListItem(domNode, options);
        case "h2":
          return (
            <h2 className="text-xl font-bold mt-4 mb-1 font-inter">
              {domToReact(domNode.children, options)}
            </h2>
          );
        case "h3":
          return (
            <h3 className="text-lg font-semibold mt-3 mb-1 font-inter">
              {domToReact(domNode.children, options)}
            </h3>
          );
        case "p":
          if (!domNode.children || domNode.children.length === 0) return null;

          return (
            <p className="my-2">{domToReact(domNode.children, options)}</p>
          );
        case "span":
          if (domNode.attribs?.["data-type"] === "emoji") {
            return <span>{domNode.children?.[0]?.data}</span>;
          }
          break;
      }
    },
  };

  const handleBackOpportunities = () => {
    router.push(INTERNAL_PAGES.participant.opportunities.main);
  };

  const handleCampaignSelection = (type: "new-account" | "login") => {
    setIsLoading(type);

    if (type === "new-account") {
      const params = new URLSearchParams();

      params.set("jobIdRef", jobId.toString());

      if (utmParams) {
        Object.entries(utmParams).forEach(([key, value]) => {
          if (value) {
            params.set(key, value as string);
          }
        });
      }

      const queryString = params.toString();
      router.push(`${INTERNAL_PAGES.participant.signup.main}?${queryString}`);
    }

    if (type === "login") {
      const params = new URLSearchParams();

      params.set("jobIdRef", jobId.toString());

      if (utmParams) {
        Object.entries(utmParams).forEach(([key, value]) => {
          if (value) {
            params.set(key, value as string);
          }
        });
      }

      const queryString = params.toString();
      router.push(`${INTERNAL_PAGES.participant.login}?${queryString}`);
    }
  };

  const handleApplyOpportunity = useCallback(() => {
    const params = new URLSearchParams();

    if (utmParams) {
      Object.entries(utmParams).forEach(([key, value]) => {
        if (value) {
          params.set(key, value as string);
        }
      });
    }

    if (!isLoggedIn) {
      params.set("jobIdRef", jobId.toString());
      router.push(`${INTERNAL_PAGES.participant.login}?${params.toString()}`);
      return;
    }

    if (hasApplied) {
      toast.error("You have already applied to this opportunity.");
      return;
    }

    if (buttonLink) {
      router.push(buttonLink);
      return;
    }

    startTransition(async () => {
      setProgress(50);

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

      setProgress(75);

      const queryString = params.toString();
      const surveyUrl = `${INTERNAL_PAGES.participant.opportunities.main}/${jobId}/survey`;
      router.push(queryString ? `${surveyUrl}?${queryString}` : surveyUrl);

      setProgress(100);
    });
  }, [isLoggedIn, jobId, hasApplied, buttonLink, router, utmParams]);

  const handleOnClickSocial = (socialId: string) => {
    const baseUrl = process.env.NEXT_PUBLIC_PFR_WEBSITE_BASE_URL;
    const currentPath = window.location.pathname;
    const encodedUrl = encodeURIComponent(`${baseUrl}${currentPath}`);

    let shareUrl = "";

    switch (socialId) {
      case "facebook":
        shareUrl = `https://www.facebook.com/sharer/sharer.php?u=${encodedUrl}`;
        break;
      case "twitter":
        shareUrl = `https://twitter.com/intent/tweet?url=${encodedUrl}`;
        break;
      case "linkedin":
        shareUrl = `https://www.linkedin.com/shareArticle?mini=true&url=${encodedUrl}`;
        break;
      case "whatsapp":
        shareUrl = `https://api.whatsapp.com/send?text=Check%20out%20this%20opportunity%20from%20People%20for%20Research:%20${encodedUrl}`;
        break;
      default:
        shareUrl = "";
        return;
    }

    window.open(shareUrl, "_blank");
  };

  // Note: This can be removed in a near future.
  // It's aimed to help the transition of opportunities with no markup yet.
  const prepareDescription = (text: string) => {
    const trimmedText = text.trim();
    const singleParagraphMatch = /^<p>([\s\S]*)<\/p>$/i.test(trimmedText);

    if (singleParagraphMatch) {
      const unwrapped = trimmedText.replace(/^<p>|<\/p>$/gi, "").trim();
      const hasOtherBlockTags =
        /<\/?(?:p|div|section|article|h[1-6]|ul|ol|li|blockquote)[\s\S]*?>/i.test(
          unwrapped,
        );

      if (!hasOtherBlockTags) {
        const sentences = unwrapped.split(/\.\s+(?=[A-Z])/);

        const paragraphs: string[] = [];
        let currentParagraph = "";

        sentences.forEach((sentence, index) => {
          const sentenceWithPeriod =
            sentence.endsWith(".") ||
            sentence.endsWith(".)") ||
            sentence.endsWith(".</a>")
              ? sentence
              : sentence + ".";

          currentParagraph +=
            (currentParagraph ? " " : "") + sentenceWithPeriod;

          const sentenceCount = currentParagraph.split(/\.\s+/).length;
          if (
            sentenceCount >= 3 ||
            currentParagraph.length > 250 ||
            index === sentences.length - 1
          ) {
            paragraphs.push(currentParagraph.trim());
            currentParagraph = "";
          }
        });

        return paragraphs
          .filter((p) => p.length > 0)
          .map((p) => `<p>${p}</p>`)
          .join("");
      }
    }

    const hasBlockTags =
      /<\/?(?:p|div|section|article|h[1-6]|ul|ol|li|blockquote)[\s\S]*?>/i.test(
        trimmedText,
      );

    if (hasBlockTags) {
      return trimmedText;
    }

    return trimmedText
      .split(/\n\s*\n|\r?\n/)
      .filter((p) => p.trim().length > 0)
      .map((p) => `<p>${p.trim()}</p>`)
      .join("");
  };

  const preparedDescription = prepareDescription(sanitizedDescription);

  const showStickyCTA = useStickyCTA(ctaRef);

  const imageSrc = useMemo(
    () => `data:image/png;base64,${thumbnail}`,
    [thumbnail],
  );

  const handlePrimaryCTA = useCallback(() => {
    if (isExclusiveToClient) {
      onOpenModalExclusive();
      return;
    }

    if (showProfilePrompt && isUserDetailsValid) {
      onOpen();
      return;
    }

    handleApplyOpportunity();
  }, [
    isExclusiveToClient,
    showProfilePrompt,
    isUserDetailsValid,
    onOpen,
    onOpenModalExclusive,
    handleApplyOpportunity,
  ]);

  const ApplyButton = ({ fullWidth = false }: { fullWidth?: boolean }) => (
    <Button
      btnLabel={
        isLoggedIn
          ? isPending
            ? "Loading survey..."
            : buttonTextToShow
          : "Log in to apply to this opportunity"
      }
      customVariant="primary"
      endContent={isLoggedIn ? <FaChevronRight /> : <FaLock />}
      fullWidth={fullWidth}
      isDisabled={!!hasApplied}
      isLoading={isPending}
      onClick={handlePrimaryCTA}
    />
  );

  const BackButton = ({ fullWidth = false }: { fullWidth?: boolean }) => (
    <Button
      btnLabel="Back to opportunities"
      customVariant="transparent"
      fullWidth={fullWidth}
      startContent={<FaChevronLeft />}
      onClick={handleBackOpportunities}
    />
  );

  const SignupButton = ({ fullWidth = false }: { fullWidth?: boolean }) => (
    <Button
      customVariant="primary"
      fullWidth={fullWidth}
      isDisabled={isLoading !== null}
      isLoading={isLoading === "new-account"}
      startContent={isLoading !== "new-account" && <FaUserPlus />}
      onClick={() => handleCampaignSelection("new-account")}
    >
      <span className="text-sm sm:text-base">
        {isLoading === "new-account"
          ? "Redirecting..."
          : "Create a new account"}
      </span>
    </Button>
  );

  const LoginButton = ({ fullWidth = false }: { fullWidth?: boolean }) => (
    <Button
      customVariant="transparent"
      fullWidth={fullWidth}
      isDisabled={isLoading !== null}
      isLoading={isLoading === "login"}
      startContent={isLoading !== "login" && <FaUser />}
      onClick={() => handleCampaignSelection("login")}
    >
      <span className="text-sm sm:text-base">
        {isLoading === "login" ? "Redirecting..." : "Log in"}
      </span>
    </Button>
  );

  return (
    <div className="flex flex-col xl:flex-row bg-participant-light dark:bg-participant-dark">
      {/* Left side - job image */}
      {/* Desktop */}
      <div className="xl:flex justify-start hidden xl:w-1/3 relative">
        <div className="relative w-full overflow-hidden">
          <Image
            unoptimized
            alt=""
            className={`"w-full h-full ${
              isExperianJob ? "object-contain" : "object-cover"
            }`}
            height={5000}
            src={imageSrc}
            width={5000}
          />
        </div>
      </div>

      {/* Mobile and tablet */}
      <div className="relative xl:hidden w-full">
        <div className="flex items-center justify-center w-full h-full relative">
          <Image
            alt={altTextThumbnail || ""}
            className="object-cover rounded-none shadow-none"
            height={1000}
            src={imageSrc}
            width={1000}
          />
        </div>
      </div>

      {/* Right side - job details */}
      <div className="w-full xl:w-2/3 pb-8 px-16 pt-4 xl:pt-0">
        <div className="flex flex-col md:flex-row md:justify-between md:items-center md:py-1">
          <Breadcrumbs />
          <ShareOppSocialMedia
            onClick={(socialId) =>
              handleOnClickSocial(socialId as keyof typeof SM_LINKS_PARTICIPANT)
            }
          />
        </div>
        <div className="space-y-4">
          <div className="flex items-center space-x-2">
            <h1 className="font-inter font-black text-3xl lg:text-4xl">
              {title}
            </h1>
          </div>
          <div className="flex flex-row gap-2">
            <Chip
              key={`topic-${topicLabel || "general"}`}
              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",
              }}
              startContent={TopicIcon && <TopicIcon className="mx-1" />}
            >
              <span>{topicLabel || "General"}</span>
            </Chip>
            {isFeatured && (
              <Chip
                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",
                }}
                startContent={<FaStar className="mx-1" />}
              >
                Featured
              </Chip>
            )}
            {hasApplied && (
              <Chip
                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",
                }}
                startContent={<FaCheck className="mx-1" />}
              >
                Applied
              </Chip>
            )}
          </div>
          <div className="space-y-4">
            <article className="font-noto leading-6">
              {parse(preparedDescription, options)}
            </article>
            <div className="grid grid-cols-1 sm:grid-cols-3 lg:grid-cols-3 gap-1 mt-12">
              {GRID_ITEMS.map((item, index) => {
                const Icon = item.icon;

                return (
                  <Card
                    key={index}
                    className="shadow-none p-6 rounded light bg-gray-400/10 dark:bg-white/5 space-y-1"
                  >
                    <Icon className="text-4xl" />
                    <h2 className="text-base font-inter font-bold pb-1 text-gray-800 dark:text-white">
                      {item.title}
                    </h2>
                    <p className="mb-4 text-gray-800 dark:text-white font-noto text-sm">
                      {item.value}
                    </p>
                  </Card>
                );
              })}
            </div>
            <p className="text-xs font-noto">
              Disclaimer: Applying for this research does not guarantee
              participation. If you do not hear from a member of our team, on
              this occasion you have not been successful. Please do keep
              applying as there is a piece of research to suit everyone.
            </p>
          </div>
          {!isFullJourney && (
            <div
              ref={ctaRef}
              className="flex flex-col space-y-4 sm:space-y-0 sm:flex-row sm:justify-between"
            >
              <BackButton />
              <ApplyButton />
            </div>
          )}

          {isFullJourney && (
            <div ref={ctaRef} className="flex flex-col space-y-4">
              <SignupButton fullWidth />
              <LoginButton fullWidth />
            </div>
          )}
        </div>
      </div>

      {/* Sticky CTA - action buttons on mobile */}
      {showStickyCTA && (
        <div className="sticky bottom-0 left-0 right-0 pb-1 sm:pb-4 px-4 z-30 backdrop-blur flex flex-col space-y-3 lg:hidden">
          {(isFullJourney
            ? [
                <SignupButton key="signup" fullWidth />,
                <LoginButton key="login" fullWidth />,
              ]
            : [<ApplyButton key="apply" />, <BackButton key="back" />]
          ).map((ButtonComponent) => ButtonComponent)}
        </div>
      )}

      {/* Profile prompt modal */}
      {showProfilePrompt && !isExclusiveToClient && isOpen && userDetails && (
        <ProfilePrompt
          employmentOptions={employmentOptions}
          industryOptions={industryOptions}
          isOpen={isOpen}
          seniorityOptions={seniorityOptions}
          userDetails={userDetails}
          userId={userId}
          onClickContinue={handleApplyOpportunity}
          onClose={onClose}
        />
      )}

      {/* Exclusive to client modal */}
      {isExclusiveToClient && isModalExclusiveOpen && (
        <ExclusiveToClientModal
          isOpen={isModalExclusiveOpen}
          onClose={onCloseModalExclusive}
        />
      )}

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

export default OpportunityDetail;
