"use client";
import { ConfirmationModalProps } from "@/components/ui/ConfirmationModal";
import { SortDirection } from "@/components/ui/tableSortContext";
import { apiDefaultParams } from "@/constants/Api_Params";
import { useTendersQuery } from "@/hooks/queries";
import { useHybridPagination } from "@/hooks/useHybridPagination";
import { TCustomer } from "@/lib/api";
import {
  buildQueryString,
  deleteEmptyKeys,
  getPaginatedRowNumber,
  isDateRangeActive,
  unixToDate,
} from "@/utils/shared";
import gsap from "gsap";
import { useIsMutating } from "@tanstack/react-query";
import { usePathname, useRouter, useSearchParams } from "next/navigation";
import { useCallback, useEffect, useLayoutEffect, useMemo, useRef, useState } from "react";
import { useForm } from "react-hook-form";

const COMMA_SEPARATED_FILTER_KEYS = new Set([
  "country_codes",
  "notice_types",
  "form_types",
  "classifications",
]);

const parseCpvCodesFilter = (value: string): string[] =>
  value
    .split(",")
    .map((item) => item.trim())
    .filter(Boolean);

const normalizeFilterValues = (values: Record<string, any>) => {
  const normalized: Record<string, any> = {};

  for (const [key, value] of Object.entries(values)) {
    if (value === undefined || value === null) {
      continue;
    }

    if (typeof value === "string") {
      const trimmedValue = value.trim();
      if (!trimmedValue) {
        continue;
      }

      if (key === "cpv_codes") {
        const codes = parseCpvCodesFilter(trimmedValue);
        if (codes.length) {
          normalized[key] = codes;
        }
        continue;
      }

      if (COMMA_SEPARATED_FILTER_KEYS.has(key)) {
        normalized[key] = trimmedValue
          .split(",")
          .map((item) => item.trim())
          .filter(Boolean)
          .join(",");
        continue;
      }

      normalized[key] = trimmedValue;
      continue;
    }

    normalized[key] = value;
  }

  return normalized;
};

const parseUnixParam = (v: unknown): number | undefined => {
  if (v === undefined || v === null || v === "") return undefined;
  const n = typeof v === "number" ? v : Number(v);
  return Number.isFinite(n) ? n : undefined;
};

const buildRangeFormValue = (
  params: Record<string, any>,
  fromKey: string,
  toKey: string,
  legacy?: { from: string; to: string },
): number[] | undefined => {
  let from = parseUnixParam(params[fromKey]);
  let to = parseUnixParam(params[toKey]);
  if (legacy) {
    if (from === undefined) from = parseUnixParam(params[legacy.from]);
    if (to === undefined) to = parseUnixParam(params[legacy.to]);
  }
  if (from !== undefined && to !== undefined) return [from, to];
  if (from !== undefined) return [from];
  if (to !== undefined) return [to];
  return undefined;
};

const formatCpvCodesForForm = (value: unknown): string => {
  if (Array.isArray(value)) {
    return value.map(String).filter(Boolean).join(", ");
  }
  return typeof value === "string" ? value : "";
};

const parseCpvCodesFromSearchParams = (
  searchParams: URLSearchParams,
): string[] | undefined => {
  const values = searchParams.getAll("cpv_codes").filter(Boolean);
  if (!values.length) {
    return undefined;
  }

  if (values.length === 1 && values[0].includes(",")) {
    const legacy = parseCpvCodesFilter(values[0]);
    return legacy.length ? legacy : undefined;
  }

  return values;
};

const buildTenderFilterFormValues = (params: Record<string, any>) => ({
  ...params,
  documents_scraped:
    params.documents_scraped === true || params.documents_scraped === "true",
  cpv_codes: formatCpvCodesForForm(params.cpv_codes),
  created_at_range: buildRangeFormValue(
    params,
    "created_at_from",
    "created_at_to",
  ),
  tender_deadline_range: buildRangeFormValue(
    params,
    "tender_deadline_from",
    "tender_deadline_to",
    { from: "deadline_from", to: "deadline_to" },
  ),
  publication_date_range: buildRangeFormValue(
    params,
    "publication_date_from",
    "publication_date_to",
  ),
  submission_deadline_range: buildRangeFormValue(
    params,
    "submission_deadline_from",
    "submission_deadline_to",
    { from: "submission_date_from", to: "submission_date_to" },
  ),
});

/** RHF `reset` does not drop fields omitted from the payload — merge empties so URL sync / clear-all match the UI. */
const TENDER_FILTER_FORM_DEFAULTS: Record<string, any> = {
  q: "",
  title: "",
  description: "",
  country: "",
  country_codes: "",
  notice_type: "",
  notice_types: "",
  form_types: "",
  main_classification: "",
  classifications: "",
  cpv_codes: "",
  documents_scraped: false,
  created_at_range: [],
  tender_deadline_range: [],
  publication_date_range: [],
  submission_deadline_range: [],
};

const mergeTenderFilterFormState = (params: Record<string, any>) => {
  const built = buildTenderFilterFormValues(params);
  return {
    ...TENDER_FILTER_FORM_DEFAULTS,
    ...built,
    created_at_range: built.created_at_range ?? [],
    tender_deadline_range: built.tender_deadline_range ?? [],
    publication_date_range: built.publication_date_range ?? [],
    submission_deadline_range: built.submission_deadline_range ?? [],
  };
};

const mapDateRangeFilters = (values: Record<string, any>) => {
  const mappedValues = { ...values };

  const mapRange = ({
    key,
    fromKey,
    toKey,
    aliases = [],
  }: {
    key: string;
    fromKey: string;
    toKey: string;
    aliases?: Array<{ from: string; to: string }>;
  }) => {
    const rangeValue = mappedValues[key];
    const isCleared =
      rangeValue === "" ||
      rangeValue === null ||
      (Array.isArray(rangeValue) && rangeValue.length === 0);

    if (isCleared) {
      delete mappedValues[key];
      delete mappedValues[fromKey];
      delete mappedValues[toKey];
      aliases.forEach((alias) => {
        delete mappedValues[alias.from];
        delete mappedValues[alias.to];
      });
      return;
    }

    if (!Array.isArray(rangeValue)) {
      return;
    }

    const [from, to] = rangeValue;
    if (from !== undefined && from !== null && from !== "") {
      mappedValues[fromKey] = from;
      aliases.forEach((alias) => {
        mappedValues[alias.from] = from;
      });
    }

    if (to !== undefined && to !== null && to !== "") {
      mappedValues[toKey] = to;
      aliases.forEach((alias) => {
        mappedValues[alias.to] = to;
      });
    }

    delete mappedValues[key];
  };

  mapRange({
    key: "created_at_range",
    fromKey: "created_at_from",
    toKey: "created_at_to",
  });
  mapRange({
    key: "tender_deadline_range",
    fromKey: "tender_deadline_from",
    toKey: "tender_deadline_to",
    aliases: [{ from: "deadline_from", to: "deadline_to" }],
  });
  mapRange({
    key: "publication_date_range",
    fromKey: "publication_date_from",
    toKey: "publication_date_to",
  });
  mapRange({
    key: "submission_deadline_range",
    fromKey: "submission_deadline_from",
    toKey: "submission_deadline_to",
    aliases: [{ from: "submission_date_from", to: "submission_date_to" }],
  });

  return mappedValues;
};

const TENDER_RANGE_PARAM_KEYS = [
  "created_at_from",
  "created_at_to",
  "tender_deadline_from",
  "tender_deadline_to",
  "deadline_from",
  "deadline_to",
  "publication_date_from",
  "publication_date_to",
  "submission_deadline_from",
  "submission_deadline_to",
  "submission_date_from",
  "submission_date_to",
  "created_at_range",
  "tender_deadline_range",
  "publication_date_range",
  "submission_deadline_range",
] as const;

/** Query keys owned by TenderListFilters — drop from URL when cleared (not in normalized submit). */
const TENDER_FILTER_MODAL_KEYS = new Set<string>([
  "q",
  "title",
  "description",
  "country",
  "country_codes",
  "notice_type",
  "notice_types",
  "form_types",
  "main_classification",
  "classifications",
  "cpv_codes",
  ...TENDER_RANGE_PARAM_KEYS,
]);

/**
 * Applying a date-range filter retargets the table sort to that same field, so the
 * results lead with the dates the user just narrowed to. Ordered by precedence — the
 * first active range wins when several are applied at once. `sortOrder` mirrors each
 * column's default click direction (see `columnDefaultOrder`).
 */
const SORT_TARGET_BY_RANGE_KEY: Array<{
  rangeKey: string;
  sortBy: string;
  sortOrder: SortDirection;
}> = [
  {
    rangeKey: "publication_date_range",
    sortBy: "publication_date",
    sortOrder: "desc",
  },
  {
    rangeKey: "submission_deadline_range",
    sortBy: "submission_deadline",
    sortOrder: "asc",
  },
  {
    rangeKey: "tender_deadline_range",
    sortBy: "tender_deadline",
    sortOrder: "asc",
  },
  { rangeKey: "created_at_range", sortBy: "created_at", sortOrder: "desc" },
];

const resolveSortFromActiveDateFilters = (formData: Record<string, any>) =>
  SORT_TARGET_BY_RANGE_KEY.find((target) =>
    isDateRangeActive(formData[target.rangeKey]),
  ) ?? null;

const useTenderListPresenter = () => {
  const [isModalOpen, setIsModalOpen] = useState(false);
  const [currentTender, setCurrentTender] = useState<TCustomer | null>(null);
  const [modalConfig, setModalConfig] = useState<
    Partial<ConfirmationModalProps>
  >({});
  const [params, setParams] = useState<Record<string, any>>({});
  const router = useRouter();
  const pathName = usePathname();
  const searchParams = useSearchParams();
  const isMutating = useIsMutating();

  const tableSectionRef = useRef<HTMLDivElement>(null);
  const skeletonTbodyRef = useRef<HTMLTableSectionElement>(null);
  const dataTbodyRef = useRef<HTMLTableSectionElement>(null);

  const {
    register: filterRegister,
    handleSubmit: handleFilterSubmit,
    reset: filterFormReset,
    watch,
    setValue: setFilterValue,
    control,
  } = useForm();

  useEffect(() => {
    const newParams: Record<string, any> = {
      ...apiDefaultParams,
      offset: 0,
      limit: 10,
    };
    for (const key of searchParams.keys()) {
      if (key === "cpv_codes") {
        continue;
      }
      newParams[key] = searchParams.get(key);
    }

    const cpvCodes = parseCpvCodesFromSearchParams(searchParams);
    if (cpvCodes) {
      newParams.cpv_codes = cpvCodes;
    }
    setParams(newParams);
    filterFormReset(mergeTenderFilterFormState(newParams));
  }, [searchParams, filterFormReset]);

  const { data, error, isPending, isError } = useTendersQuery(params);

  const { pagination, handlePaginationChange, buildFirstPageParams } =
    useHybridPagination({
      meta: data?.meta,
      params,
      setParams,
      isError,
      error,
    });

  const MAX_PAGE_JUMP = 10;
  const paginationWindow = useMemo(() => {
    const windowStart = Math.max(0, pagination.currentPage - MAX_PAGE_JUMP);
    const windowEnd = Math.min(
      Math.max(pagination.totalPages - 1, 0),
      pagination.currentPage + MAX_PAGE_JUMP,
    );
    return {
      windowStart,
      virtualCurrent: pagination.currentPage - windowStart,
      virtualTotal: Math.max(windowEnd - windowStart + 1, 1),
    };
  }, [pagination.currentPage, pagination.totalPages]);

  const handleWindowedPaginationChange = useCallback(
    (e: { selected: number }) => {
      handlePaginationChange({
        selected: e.selected + paginationWindow.windowStart,
      });
    },
    [handlePaginationChange, paginationWindow.windowStart],
  );

  const buildWindowedPageLabel = useCallback(
    (virtualPage: number) => String(virtualPage + paginationWindow.windowStart),
    [paginationWindow.windowStart],
  );

  const windowedPagination = useMemo(
    () => ({
      currentPage: paginationWindow.virtualCurrent,
      totalPages: paginationWindow.virtualTotal,
    }),
    [paginationWindow.virtualCurrent, paginationWindow.virtualTotal],
  );

  const shouldShowPagination =
    !isPending && (data?.meta?.pages ?? 1) * (data?.meta?.limit ?? 10) > 10;

  const tendersList = data?.data?.tenders ?? [];

  const langQuerySuffix = useMemo(() => {
    const lang = params?.lang;
    return typeof lang === "string" && lang.trim()
      ? `?lang=${encodeURIComponent(lang)}`
      : "";
  }, [params?.lang]);

  const navigateToTenderDetails = useCallback(
    (tenderId: string) => {
      router.push(`${pathName}/${tenderId}${langQuerySuffix}`);
    },
    [pathName, router, langQuerySuffix],
  );

  const navigateToTenderFeedback = useCallback(
    (tenderId: string) => {
      router.push(`${pathName}/feedback/${tenderId}${langQuerySuffix}`);
    },
    [pathName, router, langQuerySuffix],
  );

  const getRowNumber = useCallback(
    (index: number) =>
      getPaginatedRowNumber({
        index,
        page: data?.meta?.page,
        limit: data?.meta?.limit,
      }),
    [data?.meta?.limit, data?.meta?.page],
  );

  const formatDateTimeCell = useCallback((unix: number) => {
    return unixToDate({ unix, hasTime: true });
  }, []);

  useLayoutEffect(() => {
    const reduced =
      typeof window !== "undefined" &&
      window.matchMedia("(prefers-reduced-motion: reduce)").matches;

    const ctx = gsap.context(() => {
      if (reduced) return;

      if (isPending) {
        const tbody = skeletonTbodyRef.current;
        if (!tbody) return;
        const rows = tbody.querySelectorAll("tr");
        gsap.fromTo(
          rows,
          { opacity: 0.35, y: 14 },
          {
            opacity: 1,
            y: 0,
            duration: 0.42,
            stagger: 0.032,
            ease: "power2.out",
          },
        );
        return;
      }

      const tbody = dataTbodyRef.current;
      if (!tbody) return;
      const rows = tbody.querySelectorAll("tr");
      if (!rows.length) return;
      gsap.fromTo(
        rows,
        { opacity: 0, y: 18 },
        {
          opacity: 1,
          y: 0,
          duration: 0.46,
          stagger: 0.05,
          ease: "power3.out",
          clearProps: "opacity,transform",
        },
      );
    }, tableSectionRef);

    return () => ctx.revert();
  }, [isPending, data]);

  const search = (data: any) => {
    const mappedDateRanges = mapDateRangeFilters(data);
    const normalizedData = normalizeFilterValues(mappedDateRanges);
    const merged: Record<string, any> = { ...params };
    for (const key of TENDER_FILTER_MODAL_KEYS) {
      if (!(key in normalizedData)) {
        delete merged[key];
      }
    }

    // Retarget the sort to a freshly applied date filter, but only while the user is
    // on the implicit default sort — never clobber a column they sorted on by hand.
    const sortTarget = resolveSortFromActiveDateFilters(data);
    const currentSortBy = params.sort_by as string | undefined;
    const isDefaultSort = !currentSortBy || currentSortBy === "created_at";
    const sortOverride =
      sortTarget && isDefaultSort
        ? { sort_by: sortTarget.sortBy, sort_order: sortTarget.sortOrder }
        : {};

    const newParams = buildFirstPageParams({
      ...merged,
      ...normalizedData,
      ...sortOverride,
    });
    const cleanedParams = deleteEmptyKeys(newParams);
    const queryString = buildQueryString(cleanedParams);
    router.push(`${pathName}?${queryString}`);
  };

  const clearAllFilters = useCallback(() => {
    const cleared = buildFirstPageParams({
      ...apiDefaultParams,
      limit: 10,
    });
    filterFormReset(mergeTenderFilterFormState(cleared));
    router.push(pathName);
  }, [buildFirstPageParams, pathName, router, filterFormReset]);
  const columns = [
    "row",
    "title",
    "country code",
    "publication date",
    "submission deadline",
    "tender deadline",
    "created at",
    "status",
    "actions",
  ];

  /** Column label → BSON `sort_by` field (see bp-panel-list-sorting.md). */
  const columnSortKeys: Record<string, string> = {
    title: "title",
    "country code": "country_code",
    "publication date": "publication_date",
    "submission deadline": "submission_deadline",
    "tender deadline": "tender_deadline",
    "created at": "created_at",
    status: "status",
  };

  /** First-click direction per column, following the BP sorting UX guide. */
  const columnDefaultOrder: Record<string, SortDirection> = {
    title: "asc",
    "submission deadline": "asc",
    "tender deadline": "asc",
  };

  const sortBy = (params.sort_by as string) ?? "created_at";
  const sortOrder = (params.sort_order as SortDirection) ?? "desc";

  const handleSortChange = useCallback(
    (key: string, order: SortDirection) => {
      const newParams = buildFirstPageParams({
        ...params,
        sort_by: key,
        sort_order: order,
      });
      const cleanedParams = deleteEmptyKeys(newParams);
      const queryString = buildQueryString(cleanedParams);
      router.push(`${pathName}?${queryString}`);
    },
    [buildFirstPageParams, params, pathName, router],
  );

  return {
    currentTender,
    columns,
    columnSortKeys,
    columnDefaultOrder,
    sortBy,
    sortOrder,
    handleSortChange,
    setCurrentTender,
    router,
    allTenders: data,
    error,
    isPending,
    setParams,
    params,
    data,
    pathName,
    isModalOpen,
    setIsModalOpen,
    modalConfig,
    setModalConfig,
    clearAllFilters,
    filterRegister,
    handleFilterSubmit,
    search,
    watch,
    setFilterValue,
    control,
    isMutating,
    shouldShowPagination,
    tableSectionRef,
    skeletonTbodyRef,
    dataTbodyRef,
    tendersList,
    navigateToTenderDetails,
    navigateToTenderFeedback,
    handlePaginationChange: handleWindowedPaginationChange,
    buildWindowedPageLabel,
    getRowNumber,
    formatDateTimeCell,
    pagination: windowedPagination,
    absoluteCurrentPage: pagination.currentPage,
  };
};

export default useTenderListPresenter;
