"use client";

import { keepPreviousData } from "@tanstack/react-query";
import {
  useReactTable,
  getCoreRowModel,
  getFilteredRowModel,
  getSortedRowModel,
  createColumnHelper,
} from "@tanstack/react-table";
// eslint-disable-next-line no-restricted-imports
import startCase from "lodash/startCase";
import Link from "next/link";
import { useMemo, useId } from "react";
import { z } from "zod";

import dayjs from "@calcom/dayjs";
import {
  DataTableWrapper,
  DataTableFilters,
  DataTableSkeleton,
  dataTableFilter,
  useDataTable,
  DateRangeFilter,
  ColumnFilterType,
  type FilterableColumn,
} from "@calcom/features/data-table";
import classNames from "@calcom/lib/classNames";
import { useCopy } from "@calcom/lib/hooks/useCopy";
import { useLocale } from "@calcom/lib/hooks/useLocale";
import { BookingStatus } from "@calcom/prisma/enums";
import { RoutingFormFieldType } from "@calcom/routing-forms/lib/FieldTypes";
import { trpc, type RouterOutputs } from "@calcom/trpc";
import {
  Badge,
  Avatar,
  Icon,
  HoverCard,
  HoverCardContent,
  HoverCardTrigger,
  type BadgeProps,
  HoverCardPortal,
} from "@calcom/ui";

import { RoutingFormResponsesDownload } from "../filters/Download";
import { OrgTeamsFilter } from "../filters/OrgTeamsFilter";
import { useInsightsParameters } from "../hooks/useInsightsParameters";
import {
  ZResponseMultipleValues,
  ZResponseSingleValue,
  ZResponseTextValue,
  ZResponseNumericValue,
} from "../lib/types";
import { RoutingKPICards } from "./RoutingKPICards";

type RoutingFormTableRow = RouterOutputs["viewer"]["insights"]["routingFormResponses"]["data"][number];

const statusOrder: Record<BookingStatus, number> = {
  [BookingStatus.ACCEPTED]: 1,
  [BookingStatus.PENDING]: 2,
  [BookingStatus.AWAITING_HOST]: 3,
  [BookingStatus.CANCELLED]: 4,
  [BookingStatus.REJECTED]: 5,
};

function CellWithOverflowX({ children, className }: { children: React.ReactNode; className?: string }) {
  return (
    <div className={classNames("group relative max-w-[200px]", className)}>
      <div
        className="no-scrollbar flex gap-1 overflow-x-auto whitespace-nowrap"
        ref={(el) => {
          // Only add shadow if the scroll width is greater than 200px
          if (!el) return;
          const nextElement = el.nextElementSibling;
          if (!nextElement) return;

          if (el.scrollWidth > 200) {
            nextElement.classList.remove("hidden");
          } else {
            nextElement.classList.add("hidden");
          }
        }}>
        {children}
      </div>
      <div className="from-default absolute right-0 top-0 hidden h-full w-8 bg-gradient-to-l to-transparent " />
    </div>
  );
}

// Upper case the first letter of each word and replace underscores with spaces
function bookingStatusToText(status: BookingStatus) {
  return status
    .toLowerCase()
    .replace(/_/g, " ")
    .replace(/\b\w/g, (c) => c.toUpperCase());
}

function BookedByCell({
  attendees,
  rowId,
}: {
  attendees: RoutingFormTableRow["bookingAttendees"] | undefined;
  rowId: number;
}) {
  const cellId = useId();
  if (!attendees || attendees.length === 0) return <div className="min-w-[200px]" />;

  return (
    <div className="flex min-w-[200px] flex-wrap gap-1">
      {attendees.map((attendee) => (
        <CellWithOverflowX key={`${cellId}-${attendee.email}-${rowId}`} className="w-[200px]">
          <Badge variant="gray" className="whitespace-nowrap" title={attendee.email}>
            {attendee.name}
          </Badge>
        </CellWithOverflowX>
      ))}
    </div>
  );
}

function ResponseValueCell({
  optionMap,
  values,
  rowId,
}: {
  optionMap: Record<string, string>;
  values: string[];
  rowId: number;
}) {
  const cellId = useId();
  if (values.length === 0) return <div className="h-6 w-[200px]" />;

  return (
    <CellWithOverflowX className="flex w-[200px] gap-1">
      {values.length > 2 ? (
        <>
          {values.slice(0, 2).map((id: string, i: number) => (
            <Badge key={`${cellId}-${i}-${rowId}`} variant="gray">
              {optionMap[id] ?? id}
            </Badge>
          ))}
          <HoverCard>
            <HoverCardTrigger>
              <Badge variant="gray">+{values.length - 2}</Badge>
            </HoverCardTrigger>
            <HoverCardPortal>
              <HoverCardContent side="bottom" align="start" className="w-fit">
                <div className="flex flex-col gap-1">
                  {values.slice(2).map((id: string, i: number) => (
                    <span key={`${cellId}-overflow-${i}-${rowId}`} className="text-default text-sm">
                      {optionMap[id] ?? id}
                    </span>
                  ))}
                </div>
              </HoverCardContent>
            </HoverCardPortal>
          </HoverCard>
        </>
      ) : (
        values.map((id: string, i: number) => (
          <Badge key={`${cellId}-${i}-${rowId}`} variant="gray">
            {optionMap[id] ?? id}
          </Badge>
        ))
      )}
    </CellWithOverflowX>
  );
}

function BookingStatusBadge({ bookingStatus }: { bookingStatus: BookingStatus | null }) {
  let badgeVariant: BadgeProps["variant"] = "success";

  if (!bookingStatus) return null;

  switch (bookingStatus) {
    case BookingStatus.REJECTED:
    case BookingStatus.AWAITING_HOST:
    case BookingStatus.PENDING:
    case BookingStatus.CANCELLED:
      badgeVariant = "warning";
      break;
  }

  return <Badge variant={badgeVariant}>{bookingStatusToText(bookingStatus)}</Badge>;
}

function BookingAtCell({
  row,
  rowId,
  copyToClipboard,
  t,
}: {
  row: RoutingFormTableRow;
  rowId: number;
  copyToClipboard: (text: string) => void;
  t: (key: string) => string;
}) {
  const cellId = useId();

  if (!row.bookingUserId || !row.bookingCreatedAt) {
    return <div className="w-[250px]" />;
  }

  return (
    <HoverCard>
      <HoverCardTrigger asChild>
        <div className="flex items-center gap-2" key={`${cellId}-booking-${rowId}`}>
          <Avatar size="xs" imageSrc={row.bookingUserAvatarUrl ?? ""} alt={row.bookingUserName ?? ""} />
          <Link href={`/booking/${row.bookingUid}`}>
            <Badge variant="gray">{dayjs(row.bookingCreatedAt).format("MMM D, YYYY HH:mm")}</Badge>
          </Link>
        </div>
      </HoverCardTrigger>
      <HoverCardPortal>
        <HoverCardContent>
          <div className="flex flex-col">
            <div className="flex items-center gap-2">
              <Avatar size="sm" imageSrc={row.bookingUserAvatarUrl ?? ""} alt={row.bookingUserName ?? ""} />
              <div>
                <p className="text-sm font-medium">{row.bookingUserName}</p>
                <p className="group/booking_status_email text-subtle flex items-center text-xs">
                  <span className="truncate">{row.bookingUserEmail}</span>
                  <button
                    className="invisible ml-2 group-hover/booking_status_email:visible"
                    onClick={(e) => {
                      e.preventDefault();
                      e.stopPropagation();
                      copyToClipboard(row.bookingUserEmail ?? "");
                    }}>
                    <Icon name="copy" />
                  </button>
                </p>
              </div>
            </div>
            <div className="text-emphasis mt-4 flex items-center gap-2 text-xs">
              <span>Status:</span>
              <BookingStatusBadge bookingStatus={row.bookingStatus} />
            </div>
          </div>
        </HoverCardContent>
      </HoverCardPortal>
    </HoverCard>
  );
}

export type RoutingFormTableType = ReturnType<typeof useReactTable<RoutingFormTableRow>>;

const createdAtColumn: Extract<FilterableColumn, { type: ColumnFilterType.DATE_RANGE }> = {
  id: "createdAt",
  title: "createdAt",
  type: ColumnFilterType.DATE_RANGE,
};

export function RoutingFormResponsesTable() {
  const { t } = useLocale();
  const { copyToClipboard } = useCopy();
  const { isAll, teamId, userId, memberUserIds, routingFormId, startDate, endDate, columnFilters } =
    useInsightsParameters();

  const {
    data: headers,
    isLoading: isHeadersLoading,
    isSuccess: isHeadersSuccess,
  } = trpc.viewer.insights.routingFormResponsesHeaders.useQuery({
    userId,
    teamId,
    isAll,
    routingFormId,
  });

  const { data: forms } = trpc.viewer.insights.getRoutingFormsForFilters.useQuery({
    userId,
    teamId,
    isAll,
  });

  const { data: users } = trpc.viewer.insights.userList.useQuery({
    teamId: teamId ?? -1,
    isAll,
  });

  const { sorting } = useDataTable();

  const { data, fetchNextPage, isFetching, hasNextPage, isLoading } =
    trpc.viewer.insights.routingFormResponses.useInfiniteQuery(
      {
        teamId,
        startDate,
        endDate,
        userId,
        memberUserIds,
        isAll,
        routingFormId,
        columnFilters,
        sorting,
        limit: 30,
      },
      {
        getNextPageParam: (lastPage) => lastPage.nextCursor,
        placeholderData: keepPreviousData,
        trpc: {
          context: { skipBatch: true },
        },
      }
    );

  const processedData = useMemo(() => {
    if (!isHeadersSuccess) return [];
    return (data?.pages?.flatMap((page) => page.data) ?? []) as RoutingFormTableRow[];
  }, [data, isHeadersSuccess]);

  const columnHelper = createColumnHelper<RoutingFormTableRow>();

  const columns = useMemo(() => {
    if (!isHeadersSuccess) {
      return [];
    }
    return [
      columnHelper.accessor("formId", {
        id: "formId",
        header: t("routing_forms"),
        enableColumnFilter: true,
        enableSorting: false,
        meta: {
          filter: { type: ColumnFilterType.SINGLE_SELECT },
        },
        cell: () => null,
        filterFn: (row, id, filterValue) => {
          const cellValue = row.original.formId;
          return dataTableFilter(cellValue, filterValue);
        },
      }),
      columnHelper.accessor("bookingUserId", {
        id: "bookingUserId",
        header: t("people"),
        enableColumnFilter: true,
        enableSorting: false,
        meta: {
          filter: { type: ColumnFilterType.MULTI_SELECT },
        },
        cell: () => null,
        filterFn: (row, id, filterValue) => {
          const cellValue = row.original.bookingUserId;
          return dataTableFilter(cellValue, filterValue);
        },
      }),
      columnHelper.accessor("bookingAttendees", {
        id: "bookingAttendees",
        header: t("routing_form_insights_booked_by"),
        size: 200,
        enableColumnFilter: false,
        enableSorting: false,
        cell: (info) => {
          return <BookedByCell attendees={info.getValue()} rowId={info.row.original.id} />;
        },
      }),

      ...((headers || []).map((fieldHeader) => {
        const isText = [
          RoutingFormFieldType.TEXT,
          RoutingFormFieldType.EMAIL,
          RoutingFormFieldType.PHONE,
          RoutingFormFieldType.TEXTAREA,
        ].includes(fieldHeader.type as RoutingFormFieldType);

        const isNumber = fieldHeader.type === RoutingFormFieldType.NUMBER;

        const isSingleSelect = fieldHeader.type === RoutingFormFieldType.SINGLE_SELECT;
        const isMultiSelect = fieldHeader.type === RoutingFormFieldType.MULTI_SELECT;

        const filterType = isSingleSelect
          ? ColumnFilterType.SINGLE_SELECT
          : isNumber
          ? ColumnFilterType.NUMBER
          : isText
          ? ColumnFilterType.TEXT
          : ColumnFilterType.MULTI_SELECT;

        const optionMap =
          fieldHeader.options?.reduce((acc, option) => {
            if (option.id) {
              acc[option.id] = option.label;
            }
            return acc;
          }, {} as Record<string, string>) ?? {};

        return columnHelper.accessor(`response.${fieldHeader.id}`, {
          id: fieldHeader.id,
          header: startCase(fieldHeader.label),
          enableSorting: false,
          cell: (info) => {
            const values = info.getValue();
            if (isMultiSelect || isSingleSelect) {
              const result = z.union([ZResponseMultipleValues, ZResponseSingleValue]).safeParse(values);
              return (
                result.success && (
                  <ResponseValueCell
                    optionMap={optionMap}
                    values={Array.isArray(result.data.value) ? result.data.value : [result.data.value]}
                    rowId={info.row.original.id}
                  />
                )
              );
            } else if (isText || isNumber) {
              const result = z.union([ZResponseTextValue, ZResponseNumericValue]).safeParse(values);
              return (
                result.success && (
                  <div className="truncate">
                    <span title={String(result.data.value)}>{result.data.value}</span>
                  </div>
                )
              );
            } else {
              return null;
            }
          },
          meta: {
            filter: { type: filterType },
          },
          filterFn: (row, id, filterValue) => {
            const cellValue = row.original.response[id].value;
            return dataTableFilter(cellValue, filterValue);
          },
        });
      }) ?? []),
      columnHelper.accessor("bookingStatusOrder", {
        id: "bookingStatusOrder",
        header: t("routing_form_insights_booking_status"),
        sortDescFirst: false,
        cell: (info) => (
          <div className="max-w-[250px]">
            <BookingStatusBadge bookingStatus={info.row.original.bookingStatus} />
          </div>
        ),
        meta: {
          filter: { type: ColumnFilterType.MULTI_SELECT, icon: "circle" },
        },
        filterFn: (row, id, filterValue) => {
          const cellValue = row.original.bookingStatusOrder;
          return dataTableFilter(cellValue, filterValue);
        },
        sortingFn: (rowA, rowB) => {
          const statusA = rowA.original.bookingStatusOrder ?? 6; // put it at the end if bookingStatusOrder is null
          const statusB = rowB.original.bookingStatusOrder ?? 6;
          return statusA - statusB;
        },
      }),
      columnHelper.accessor("bookingCreatedAt", {
        id: "bookingCreatedAt",
        header: t("routing_form_insights_booking_at"),
        enableColumnFilter: false,
        cell: (info) => (
          <div className="max-w-[250px]">
            <BookingAtCell
              row={info.row.original}
              rowId={info.row.original.id}
              copyToClipboard={copyToClipboard}
              t={t}
            />
          </div>
        ),
        sortingFn: (rowA, rowB) => {
          const dateA = rowA.original.bookingCreatedAt;
          const dateB = rowB.original.bookingCreatedAt;
          if (!dateA && !dateB) return 0;
          if (!dateA) return -1;
          if (!dateB) return 1;
          if (!(dateA instanceof Date) || !(dateB instanceof Date)) return 0;

          return dateA.getTime() - dateB.getTime();
        },
      }),
      columnHelper.accessor("bookingAssignmentReason", {
        id: "bookingAssignmentReason",
        header: t("routing_form_insights_assignment_reason"),
        enableColumnFilter: true,
        enableSorting: false,
        meta: {
          filter: { type: ColumnFilterType.TEXT },
        },
        cell: (info) => {
          const assignmentReason = info.getValue();
          return <div className="max-w-[250px]">{assignmentReason}</div>;
        },
        filterFn: (row, id, filterValue) => {
          const reason = row.original.bookingAssignmentReason;
          return dataTableFilter(reason, filterValue);
        },
      }),
      columnHelper.accessor("createdAt", {
        id: "createdAt",
        header: t("routing_form_insights_submitted_at"),
        // exclude from "Filters" component
        // because we already have a DateRangeFilter component
        enableColumnFilter: false,
        cell: (info) => (
          <div className="whitespace-nowrap">
            <Badge variant="gray">{dayjs(info.getValue()).format("MMM D, YYYY HH:mm")}</Badge>
          </div>
        ),
        filterFn: (row, id, filterValue) => {
          const createdAt = row.original.createdAt;
          return dataTableFilter(createdAt, filterValue);
        },
      }),
    ];
  }, [isHeadersSuccess, headers, t, copyToClipboard]);

  const table = useReactTable<RoutingFormTableRow>({
    data: processedData,
    columns,
    getCoreRowModel: getCoreRowModel(),
    getFilteredRowModel: getFilteredRowModel(),
    getSortedRowModel: getSortedRowModel(),
    defaultColumn: {
      size: 150,
    },
    initialState: {
      columnVisibility: {
        formId: false,
        bookingUserId: false,
      },
    },
    getFacetedUniqueValues: (_, columnId) => () => {
      if (!headers) {
        return new Map();
      }

      const fromArrayToMap = (array: { label: string; value: string | number }[]) => {
        return new Map(array.map((option) => [{ label: option.label, value: option.value }, 1]));
      };

      const fieldHeader = headers.find((h) => h.id === columnId);
      if (fieldHeader?.options) {
        return fromArrayToMap(
          fieldHeader.options
            .filter((option): option is { id: string; label: string } => option.id !== null)
            .map((option) => ({
              label: option.label,
              value: option.id,
            }))
        );
      } else if (columnId === "bookingStatusOrder") {
        return fromArrayToMap(
          Object.keys(statusOrder).map((status) => ({
            value: statusOrder[status as BookingStatus],
            label: bookingStatusToText(status as BookingStatus),
          }))
        );
      } else if (columnId === "formId") {
        return fromArrayToMap(
          forms?.map((form) => ({
            label: form.name,
            value: form.id,
          })) ?? []
        );
      } else if (columnId === "bookingUserId") {
        return fromArrayToMap(
          users?.map((user) => ({
            label: user.name ?? user.email,
            value: user.id,
          })) ?? []
        );
      }
      return new Map();
    },
  });

  if ((isHeadersLoading && !headers) || ((isFetching || isLoading) && !data)) {
    return <DataTableSkeleton columns={4} columnWidths={[200, 200, 250, 250]} />;
  }

  return (
    <>
      <div className="flex-1">
        <DataTableWrapper
          table={table}
          isPending={isFetching && !data}
          hasNextPage={hasNextPage}
          fetchNextPage={fetchNextPage}
          isFetching={isFetching}
          ToolbarLeft={
            <>
              <OrgTeamsFilter />
              <DataTableFilters.AddFilterButton table={table} />
              <DataTableFilters.ActiveFilters table={table} />
              <DataTableFilters.ClearFiltersButton exclude={["createdAt"]} />
            </>
          }
          ToolbarRight={
            <>
              <DateRangeFilter column={createdAtColumn} />
              <RoutingFormResponsesDownload sorting={sorting} />
              <DataTableFilters.ColumnVisibilityButton table={table} />
            </>
          }>
          <RoutingKPICards />
        </DataTableWrapper>
      </div>
    </>
  );
}
