"use client";

import * as Sentry from "@sentry/nextjs";
import {
  differenceInDays,
  endOfMonth,
  endOfQuarter,
  endOfYear,
  format,
  startOfDay,
  startOfMonth,
  startOfQuarter,
  startOfYear,
  subDays,
  subMonths,
  subQuarters,
  subYears,
} from "date-fns";
import { TFunction } from "i18next";
import { Loader2 } from "lucide-react";
import { useCallback, useEffect, useMemo, useRef, useState } from "react";
import toast from "react-hot-toast";
import { useTranslation } from "react-i18next";
import { TSurvey } from "@formbricks/types/surveys/types";
import {
  DateRange,
  useResponseFilter,
} from "@/app/(app)/environments/[environmentId]/surveys/[surveyId]/(analysis)/components/response-filter-context";
import { getResponsesDownloadUrlAction } from "@/app/(app)/environments/[environmentId]/surveys/[surveyId]/actions";
import { downloadResponsesFile } from "@/app/(app)/environments/[environmentId]/surveys/[surveyId]/utils";
import { getFormattedFilters, getTodayDate } from "@/app/lib/surveys/surveys";
import { useClickOutside } from "@/lib/utils/hooks/useClickOutside";
import { Calendar } from "@/modules/ui/components/calendar";
import {
  DropdownMenu,
  DropdownMenuContent,
  DropdownMenuItem,
  DropdownMenuTrigger,
} from "@/modules/ui/components/dropdown-menu";
import { PopoverTriggerButton, ResponseFilter } from "./ResponseFilter";

enum DateSelected {
  FROM = "common.from",
  TO = "common.to",
}

enum FilterDownload {
  ALL = "common.all",
  FILTER = "common.filter",
}

const getFilterDropDownLabels = (t: TFunction) => ({
  ALL_TIME: t("environments.surveys.summary.all_time"),
  LAST_7_DAYS: t("environments.surveys.summary.last_7_days"),
  LAST_30_DAYS: t("environments.surveys.summary.last_30_days"),
  THIS_MONTH: t("environments.surveys.summary.this_month"),
  LAST_MONTH: t("environments.surveys.summary.last_month"),
  LAST_6_MONTHS: t("environments.surveys.summary.last_6_months"),
  THIS_QUARTER: t("environments.surveys.summary.this_quarter"),
  LAST_QUARTER: t("environments.surveys.summary.last_quarter"),
  THIS_YEAR: t("environments.surveys.summary.this_year"),
  LAST_YEAR: t("environments.surveys.summary.last_year"),
  CUSTOM_RANGE: t("environments.surveys.summary.custom_range"),
});

interface CustomFilterProps {
  survey: TSurvey;
}

const getDateRangeLabel = (from: Date, to: Date, t: TFunction) => {
  const dateRanges = [
    {
      label: getFilterDropDownLabels(t).LAST_7_DAYS,
      matches: () => differenceInDays(to, from) === 7,
    },
    {
      label: getFilterDropDownLabels(t).LAST_30_DAYS,
      matches: () => differenceInDays(to, from) === 30,
    },
    {
      label: getFilterDropDownLabels(t).THIS_MONTH,
      matches: () =>
        format(from, "yyyy-MM-dd") === format(startOfMonth(new Date()), "yyyy-MM-dd") &&
        format(to, "yyyy-MM-dd") === format(getTodayDate(), "yyyy-MM-dd"),
    },
    {
      label: getFilterDropDownLabels(t).LAST_MONTH,
      matches: () =>
        format(from, "yyyy-MM-dd") === format(startOfMonth(subMonths(new Date(), 1)), "yyyy-MM-dd") &&
        format(to, "yyyy-MM-dd") === format(endOfMonth(subMonths(getTodayDate(), 1)), "yyyy-MM-dd"),
    },
    {
      label: getFilterDropDownLabels(t).LAST_6_MONTHS,
      matches: () =>
        format(from, "yyyy-MM-dd") === format(startOfMonth(subMonths(new Date(), 6)), "yyyy-MM-dd") &&
        format(to, "yyyy-MM-dd") === format(endOfMonth(getTodayDate()), "yyyy-MM-dd"),
    },
    {
      label: getFilterDropDownLabels(t).THIS_QUARTER,
      matches: () =>
        format(from, "yyyy-MM-dd") === format(startOfQuarter(new Date()), "yyyy-MM-dd") &&
        format(to, "yyyy-MM-dd") === format(endOfQuarter(getTodayDate()), "yyyy-MM-dd"),
    },
    {
      label: getFilterDropDownLabels(t).LAST_QUARTER,
      matches: () =>
        format(from, "yyyy-MM-dd") === format(startOfQuarter(subQuarters(new Date(), 1)), "yyyy-MM-dd") &&
        format(to, "yyyy-MM-dd") === format(endOfQuarter(subQuarters(getTodayDate(), 1)), "yyyy-MM-dd"),
    },
    {
      label: getFilterDropDownLabels(t).THIS_YEAR,
      matches: () =>
        format(from, "yyyy-MM-dd") === format(startOfYear(new Date()), "yyyy-MM-dd") &&
        format(to, "yyyy-MM-dd") === format(endOfYear(getTodayDate()), "yyyy-MM-dd"),
    },
    {
      label: getFilterDropDownLabels(t).LAST_YEAR,
      matches: () =>
        format(from, "yyyy-MM-dd") === format(startOfYear(subYears(new Date(), 1)), "yyyy-MM-dd") &&
        format(to, "yyyy-MM-dd") === format(endOfYear(subYears(getTodayDate(), 1)), "yyyy-MM-dd"),
    },
  ];

  const matchedRange = dateRanges.find((range) => range.matches());
  return matchedRange ? matchedRange.label : getFilterDropDownLabels(t).CUSTOM_RANGE;
};

export const CustomFilter = ({ survey }: CustomFilterProps) => {
  const { t } = useTranslation();
  const { selectedFilter, dateRange, setDateRange, resetState } = useResponseFilter();
  const [filterRange, setFilterRange] = useState(
    dateRange.from && dateRange.to
      ? getDateRangeLabel(dateRange.from, dateRange.to, t)
      : getFilterDropDownLabels(t).ALL_TIME
  );
  const [selectingDate, setSelectingDate] = useState<DateSelected>(DateSelected.FROM);
  const [isDatePickerOpen, setIsDatePickerOpen] = useState<boolean>(false);
  const [isFilterDropDownOpen, setIsFilterDropDownOpen] = useState<boolean>(false);
  const [isDownloadDropDownOpen, setIsDownloadDropDownOpen] = useState<boolean>(false);
  const [hoveredRange, setHoveredRange] = useState<DateRange | null>(null);
  const [isDownloading, setIsDownloading] = useState<boolean>(false);

  const firstMountRef = useRef(true);

  useEffect(() => {
    if (!firstMountRef.current) {
      firstMountRef.current = false;
      return;
    }
  }, []);

  useEffect(() => {
    if (!firstMountRef.current) {
      resetState();
    }
  }, [survey?.id, resetState]);

  const filters = useMemo(
    () => getFormattedFilters(survey, selectedFilter, dateRange),

    // eslint-disable-next-line react-hooks/exhaustive-deps
    [selectedFilter, dateRange]
  );

  const datePickerRef = useRef<HTMLDivElement>(null);

  const extractMetadataKeys = useCallback((obj: Record<string, unknown>, parentKey = "") => {
    let keys: string[] = [];

    for (let key in obj) {
      if (typeof obj[key] === "object" && obj[key] !== null) {
        keys = keys.concat(extractMetadataKeys(obj[key] as Record<string, unknown>, parentKey + key + " - "));
      } else {
        keys.push(parentKey + key);
      }
    }

    return keys;
  }, []);

  const handleDateHoveredChange = (date: Date) => {
    if (selectingDate === DateSelected.FROM) {
      const startOfRange = new Date(date);
      startOfRange.setHours(0, 0, 0, 0); // Set to the start of the selected day

      // Check if the selected date is after the current 'to' date
      if (startOfRange > dateRange?.to!) {
        return;
      } else {
        setHoveredRange({ from: startOfRange, to: dateRange.to });
      }
    } else {
      const endOfRange = new Date(date);
      endOfRange.setHours(23, 59, 59, 999); // Set to the end of the selected day

      // Check if the selected date is before the current 'from' date
      if (endOfRange < dateRange?.from!) {
        return;
      } else {
        setHoveredRange({ from: dateRange.from, to: endOfRange });
      }
    }
  };

  const handleDateChange = (date: Date) => {
    if (selectingDate === DateSelected.FROM) {
      const startOfRange = new Date(date);
      startOfRange.setHours(0, 0, 0, 0); // Set to the start of the selected day

      // Check if the selected date is after the current 'to' date
      if (startOfRange > dateRange?.to!) {
        const nextDay = new Date(startOfRange);
        nextDay.setDate(nextDay.getDate() + 1);
        nextDay.setHours(23, 59, 59, 999);
        setDateRange({ from: startOfRange, to: nextDay });
      } else {
        setDateRange((prevData) => ({ from: startOfRange, to: prevData.to }));
      }
      setSelectingDate(DateSelected.TO);
    } else {
      const endOfRange = new Date(date);
      endOfRange.setHours(23, 59, 59, 999); // Set to the end of the selected day

      // Check if the selected date is before the current 'from' date
      if (endOfRange < dateRange?.from!) {
        const previousDay = new Date(endOfRange);
        previousDay.setDate(previousDay.getDate() - 1);
        previousDay.setHours(0, 0, 0, 0); // Set to the start of the selected day
        setDateRange({ from: previousDay, to: endOfRange });
      } else {
        setDateRange((prevData) => ({ from: prevData?.from, to: endOfRange }));
      }
      setIsDatePickerOpen(false);
      setSelectingDate(DateSelected.FROM);
    }
  };

  const handleDatePickerClose = () => {
    setIsDatePickerOpen(false);
    setSelectingDate(DateSelected.FROM);
  };

  const handleDownloadResponses = async (filter: FilterDownload, fileType: "csv" | "xlsx") => {
    try {
      const responseFilters = filter === FilterDownload.ALL ? {} : filters;
      setIsDownloading(true);

      const responsesDownloadUrlResponse = await getResponsesDownloadUrlAction({
        surveyId: survey.id,
        format: fileType,
        filterCriteria: responseFilters,
      });

      if (responsesDownloadUrlResponse?.data) {
        downloadResponsesFile(
          responsesDownloadUrlResponse.data.fileName,
          responsesDownloadUrlResponse.data.fileContents,
          fileType
        );
      } else {
        toast.error(t("environments.surveys.responses.error_downloading_responses"));
      }
    } catch (err) {
      Sentry.captureException(err);
      toast.error(t("environments.surveys.responses.error_downloading_responses"));
    } finally {
      setIsDownloading(false);
    }
  };

  useClickOutside(datePickerRef, () => handleDatePickerClose());
  return (
    <div className="relative flex justify-between">
      <div className="flex justify-stretch gap-x-1.5">
        <ResponseFilter survey={survey} />
        <DropdownMenu
          onOpenChange={(value) => {
            value && handleDatePickerClose();
            setIsFilterDropDownOpen(value);
          }}>
          <DropdownMenuTrigger asChild>
            <PopoverTriggerButton isOpen={isFilterDropDownOpen}>
              {filterRange === getFilterDropDownLabels(t).CUSTOM_RANGE
                ? `${dateRange?.from ? format(dateRange?.from, "dd LLL") : "Select first date"} - ${
                    dateRange?.to ? format(dateRange.to, "dd LLL") : "Select last date"
                  }`
                : filterRange}
            </PopoverTriggerButton>
          </DropdownMenuTrigger>
          <DropdownMenuContent>
            <DropdownMenuItem
              onClick={() => {
                setFilterRange(getFilterDropDownLabels(t).ALL_TIME);
                setDateRange({ from: undefined, to: getTodayDate() });
              }}>
              <p className="text-slate-700">{getFilterDropDownLabels(t).ALL_TIME}</p>
            </DropdownMenuItem>
            <DropdownMenuItem
              onClick={() => {
                setFilterRange(getFilterDropDownLabels(t).LAST_7_DAYS);
                setDateRange({ from: startOfDay(subDays(new Date(), 7)), to: getTodayDate() });
              }}>
              <p className="text-slate-700">{getFilterDropDownLabels(t).LAST_7_DAYS}</p>
            </DropdownMenuItem>
            <DropdownMenuItem
              onClick={() => {
                setFilterRange(getFilterDropDownLabels(t).LAST_30_DAYS);
                setDateRange({ from: startOfDay(subDays(new Date(), 30)), to: getTodayDate() });
              }}>
              <p className="text-slate-700">{getFilterDropDownLabels(t).LAST_30_DAYS}</p>
            </DropdownMenuItem>
            <DropdownMenuItem
              onClick={() => {
                setFilterRange(getFilterDropDownLabels(t).THIS_MONTH);
                setDateRange({ from: startOfMonth(new Date()), to: getTodayDate() });
              }}>
              <p className="text-slate-700">{getFilterDropDownLabels(t).THIS_MONTH}</p>
            </DropdownMenuItem>
            <DropdownMenuItem
              onClick={() => {
                setFilterRange(getFilterDropDownLabels(t).LAST_MONTH);
                setDateRange({
                  from: startOfMonth(subMonths(new Date(), 1)),
                  to: endOfMonth(subMonths(getTodayDate(), 1)),
                });
              }}>
              <p className="text-slate-700">{getFilterDropDownLabels(t).LAST_MONTH}</p>
            </DropdownMenuItem>
            <DropdownMenuItem
              onClick={() => {
                setFilterRange(getFilterDropDownLabels(t).THIS_QUARTER);
                setDateRange({ from: startOfQuarter(new Date()), to: endOfQuarter(getTodayDate()) });
              }}>
              <p className="text-slate-700">{getFilterDropDownLabels(t).THIS_QUARTER}</p>
            </DropdownMenuItem>
            <DropdownMenuItem
              onClick={() => {
                setFilterRange(getFilterDropDownLabels(t).LAST_QUARTER);
                setDateRange({
                  from: startOfQuarter(subQuarters(new Date(), 1)),
                  to: endOfQuarter(subQuarters(getTodayDate(), 1)),
                });
              }}>
              <p className="text-slate-700">{getFilterDropDownLabels(t).LAST_QUARTER}</p>
            </DropdownMenuItem>
            <DropdownMenuItem
              onClick={() => {
                setFilterRange(getFilterDropDownLabels(t).LAST_6_MONTHS);
                setDateRange({
                  from: startOfMonth(subMonths(new Date(), 6)),
                  to: endOfMonth(getTodayDate()),
                });
              }}>
              <p className="text-slate-700">{getFilterDropDownLabels(t).LAST_6_MONTHS}</p>
            </DropdownMenuItem>
            <DropdownMenuItem
              onClick={() => {
                setFilterRange(getFilterDropDownLabels(t).THIS_YEAR);
                setDateRange({ from: startOfYear(new Date()), to: endOfYear(getTodayDate()) });
              }}>
              <p className="text-slate-700">{getFilterDropDownLabels(t).THIS_YEAR}</p>
            </DropdownMenuItem>
            <DropdownMenuItem
              onClick={() => {
                setFilterRange(getFilterDropDownLabels(t).LAST_YEAR);
                setDateRange({
                  from: startOfYear(subYears(new Date(), 1)),
                  to: endOfYear(subYears(getTodayDate(), 1)),
                });
              }}>
              <p className="text-slate-700">{getFilterDropDownLabels(t).LAST_YEAR}</p>
            </DropdownMenuItem>
            <DropdownMenuItem
              onClick={() => {
                setIsDatePickerOpen(true);
                setFilterRange(getFilterDropDownLabels(t).CUSTOM_RANGE);
                setSelectingDate(DateSelected.FROM);
              }}>
              <p className="text-sm text-slate-700 hover:ring-0">{getFilterDropDownLabels(t).CUSTOM_RANGE}</p>
            </DropdownMenuItem>
          </DropdownMenuContent>
        </DropdownMenu>
        <DropdownMenu
          onOpenChange={(value) => {
            value && handleDatePickerClose();
            setIsDownloadDropDownOpen(value);
          }}>
          <DropdownMenuTrigger asChild>
            <PopoverTriggerButton isOpen={isDownloadDropDownOpen} disabled={isDownloading}>
              <span className="flex items-center gap-2">
                {t("common.download")}
                {isDownloading && <Loader2 className="h-3 w-3 animate-spin" strokeWidth={1.5} />}
              </span>
            </PopoverTriggerButton>
          </DropdownMenuTrigger>

          <DropdownMenuContent align="start">
            <DropdownMenuItem
              data-testid="fb__custom-filter-download-all-csv"
              onClick={async () => {
                await handleDownloadResponses(FilterDownload.ALL, "csv");
              }}>
              <p className="text-slate-700">{t("environments.surveys.summary.all_responses_csv")}</p>
            </DropdownMenuItem>
            <DropdownMenuItem
              data-testid="fb__custom-filter-download-all-xlsx"
              onClick={async () => {
                await handleDownloadResponses(FilterDownload.ALL, "xlsx");
              }}>
              <p className="text-slate-700">{t("environments.surveys.summary.all_responses_excel")}</p>
            </DropdownMenuItem>
            <DropdownMenuItem
              data-testid="fb__custom-filter-download-filtered-csv"
              onClick={async () => {
                await handleDownloadResponses(FilterDownload.FILTER, "csv");
              }}>
              <p className="text-slate-700">{t("environments.surveys.summary.filtered_responses_csv")}</p>
            </DropdownMenuItem>
            <DropdownMenuItem
              data-testid="fb__custom-filter-download-filtered-xlsx"
              onClick={async () => {
                await handleDownloadResponses(FilterDownload.FILTER, "xlsx");
              }}>
              <p className="text-slate-700">{t("environments.surveys.summary.filtered_responses_excel")}</p>
            </DropdownMenuItem>
          </DropdownMenuContent>
        </DropdownMenu>
      </div>
      {isDatePickerOpen && (
        <div ref={datePickerRef} className="absolute top-full z-50 my-2 rounded-md border bg-white">
          <Calendar
            autoFocus
            mode="range"
            defaultMonth={dateRange?.from}
            selected={hoveredRange || dateRange}
            numberOfMonths={2}
            onDayClick={(date) => handleDateChange(date)}
            onDayMouseEnter={handleDateHoveredChange}
            onDayMouseLeave={() => setHoveredRange(null)}
            classNames={{
              day_today: "hover:bg-slate-200 bg-white",
            }}
          />
        </div>
      )}
    </div>
  );
};
