import { useCallback, useEffect, useMemo, useState } from 'react';
import { useForm } from 'react-hook-form';

import { zodResolver } from '@hookform/resolvers/zod';
import { isEqual } from 'lodash-es';
import { parseAsString, parseAsStringLiteral, useQueryState } from 'nuqs';
import { useDebounce } from '@material-hu/hooks/useDebounce';

import {
  defaultHistoryApprovalsFilters,
  defaultPendingApprovalsFilters,
} from '../agentWorkspace/constants';
import {
  type HistoryApprovalsFormValues,
  historyApprovalsFiltersSchema,
  type PendingApprovalsFormValues,
  pendingApprovalsFiltersSchema,
} from '../agentWorkspace/types';
import {
  createFilterParser,
  parseToValidURLDate,
  trimFormValues,
} from '../agentWorkspace/utils';
import { type ApprovalTabsType, approvalTabs } from '../constants';

const TAB_PARAM = 'tab';
const PENDING_FILTERS_PARAM = 'pendingFilters';
const HISTORY_FILTERS_PARAM = 'historyFilters';
const SEARCH_PARAM = 'q';

const tabParser = parseAsStringLiteral([
  approvalTabs.pending,
  approvalTabs.history,
] as const).withDefault(approvalTabs.pending);

const pendingApprovalsFiltersParser =
  createFilterParser<PendingApprovalsFormValues>(
    pendingApprovalsFiltersSchema,
  ).withDefault(defaultPendingApprovalsFilters);

const historyApprovalsFiltersParser =
  createFilterParser<HistoryApprovalsFormValues>(
    historyApprovalsFiltersSchema,
  ).withDefault(defaultHistoryApprovalsFilters);

// Strips passthrough fields from filter entities to keep the encoded URL blob
// small, and normalizes dates with `parseToValidURLDate` (drops time/timezone).
const minimizePendingFilters = (
  filters: PendingApprovalsFormValues,
): PendingApprovalsFormValues => ({
  ...filters,
  parentTaskCatalogItemsIn: filters.parentTaskCatalogItemsIn?.map(item => ({
    id: item.id,
    name: item.name,
  })),
  parentTaskInitiatorsIn: filters.parentTaskInitiatorsIn?.map(initiator => ({
    id: initiator.id,
    firstName: initiator.firstName,
    lastName: initiator.lastName,
    employeeInternalId: initiator.employeeInternalId,
  })),
  createdAtFrom: filters.createdAtFrom
    ? parseToValidURLDate(filters.createdAtFrom)
    : null,
  createdAtTo: filters.createdAtTo
    ? parseToValidURLDate(filters.createdAtTo)
    : null,
});

const minimizeHistoryFilters = (
  filters: HistoryApprovalsFormValues,
): HistoryApprovalsFormValues => ({
  ...filters,
  parentTaskCatalogItemsIn: filters.parentTaskCatalogItemsIn?.map(item => ({
    id: item.id,
    name: item.name,
  })),
  statesIn: filters.statesIn?.map(state => ({
    id: state.id,
    name: state.name,
    terminal: state.terminal,
  })),
  parentTaskInitiatorsIn: filters.parentTaskInitiatorsIn?.map(initiator => ({
    id: initiator.id,
    firstName: initiator.firstName,
    lastName: initiator.lastName,
    employeeInternalId: initiator.employeeInternalId,
  })),
  completedAtFrom: filters.completedAtFrom
    ? parseToValidURLDate(filters.completedAtFrom)
    : null,
  completedAtTo: filters.completedAtTo
    ? parseToValidURLDate(filters.completedAtTo)
    : null,
});

// Counts active filters for either tab. Array fields with at least one item
// each count as 1; the date filter counts as 1 when a preset is selected
// or when `dateRange` is on with at least one bound set.
const countAppliedFilters = (
  filters: PendingApprovalsFormValues | HistoryApprovalsFormValues,
  tab: ApprovalTabsType,
) => {
  let count = 0;

  if (filters.parentTaskCatalogItemsIn?.length) count += 1;
  if (filters.parentTaskInitiatorsIn?.length) count += 1;

  if (
    tab === approvalTabs.history &&
    (filters as HistoryApprovalsFormValues).statesIn?.length
  ) {
    count += 1;
  }

  if (filters.dateOptions) {
    const { dateRange, ...predefinedOptions } = filters.dateOptions;
    const hasPredefinedOption = Object.values(predefinedOptions).some(Boolean);

    const fromDate =
      tab === approvalTabs.pending
        ? (filters as PendingApprovalsFormValues).createdAtFrom
        : (filters as HistoryApprovalsFormValues).completedAtFrom;
    const toDate =
      tab === approvalTabs.pending
        ? (filters as PendingApprovalsFormValues).createdAtTo
        : (filters as HistoryApprovalsFormValues).completedAtTo;

    const hasValidDateRange = dateRange && (fromDate || toDate);

    if (hasPredefinedOption || hasValidDateRange) {
      count += 1;
    }
  }

  return count;
};

// Centralizes URL state for both `Approvals.tsx` (agent workspace) and
// `ApprovalsPortal.tsx` (service portal): persists the active tab and a
// per-tab filter slice (`pendingFilters` and `historyFilters`) so deep
// links and tab switches preserve each tab's own filters independently.
const useApprovalsUrlState = () => {
  const [tab, setTab] = useQueryState(TAB_PARAM, tabParser);
  const [pendingApplied, setPendingApplied] = useQueryState(
    PENDING_FILTERS_PARAM,
    pendingApprovalsFiltersParser,
  );
  const [historyApplied, setHistoryApplied] = useQueryState(
    HISTORY_FILTERS_PARAM,
    historyApprovalsFiltersParser,
  );
  const [search, setSearch] = useQueryState(
    SEARCH_PARAM,
    parseAsString.withDefault(''),
  );
  const debouncedSearch = useDebounce(search);

  const [filterDrawerOpen, setFilterDrawerOpen] = useState(false);
  const [filtersHydrated, setFiltersHydrated] = useState(false);

  const pendingForm = useForm<PendingApprovalsFormValues>({
    defaultValues: defaultPendingApprovalsFilters,
    mode: 'onChange',
    resolver: zodResolver(pendingApprovalsFiltersSchema),
  });

  const historyForm = useForm<HistoryApprovalsFormValues>({
    defaultValues: defaultHistoryApprovalsFilters,
    mode: 'onChange',
    resolver: zodResolver(historyApprovalsFiltersSchema),
  });

  const isPending = tab === approvalTabs.pending;

  // One-shot: hydrate both drawer forms from the URL on mount when non-default
  // filters are present (deep-linked URL after a hard refresh).
  useEffect(() => {
    if (filtersHydrated) return;

    if (!isEqual(pendingApplied, defaultPendingApprovalsFilters)) {
      pendingForm.reset(pendingApplied);
    }

    if (!isEqual(historyApplied, defaultHistoryApprovalsFilters)) {
      historyForm.reset(historyApplied);
    }

    setFiltersHydrated(true);
  }, [
    filtersHydrated,
    pendingApplied,
    historyApplied,
    pendingForm,
    historyForm,
  ]);

  const appliedFilters = isPending ? pendingApplied : historyApplied;

  const appliedFiltersCount = useMemo(
    () => countAppliedFilters(appliedFilters, tab),
    [appliedFilters, tab],
  );

  const handleApplyFilters = useCallback(() => {
    if (isPending) {
      const values = pendingForm.getValues();
      trimFormValues(values);
      if (isEqual(values, defaultPendingApprovalsFilters)) {
        setPendingApplied(null);
      } else {
        setPendingApplied(minimizePendingFilters(values));
      }
    } else {
      const values = historyForm.getValues();
      trimFormValues(values);
      if (isEqual(values, defaultHistoryApprovalsFilters)) {
        setHistoryApplied(null);
      } else {
        setHistoryApplied(minimizeHistoryFilters(values));
      }
    }
    setFilterDrawerOpen(false);
  }, [
    isPending,
    pendingForm,
    historyForm,
    setPendingApplied,
    setHistoryApplied,
  ]);

  const handleResetFilters = useCallback(() => {
    if (isPending) {
      pendingForm.reset(defaultPendingApprovalsFilters);
    } else {
      historyForm.reset(defaultHistoryApprovalsFilters);
    }
  }, [isPending, pendingForm, historyForm]);

  // Restore the active form to the currently applied filters as the source of
  // truth so cancelling the drawer discards any in-progress edits.
  const handleCloseFilterDrawer = useCallback(() => {
    setFilterDrawerOpen(false);
    if (isPending) {
      pendingForm.reset(pendingApplied);
    } else {
      historyForm.reset(historyApplied);
    }
  }, [isPending, pendingForm, historyForm, pendingApplied, historyApplied]);

  const handleTabChange = useCallback(
    (newTab: ApprovalTabsType) => {
      setTab(newTab === approvalTabs.pending ? null : newTab);
      // Clear search when switching tabs to avoid stale results.
      setSearch(null);
    },
    [setTab, setSearch],
  );

  const handleSearchChange = useCallback(
    (value: string) => {
      setSearch(value || null);
    },
    [setSearch],
  );

  const openFilterDrawer = useCallback(() => {
    setFilterDrawerOpen(true);
  }, []);

  return {
    tab,
    setTab: handleTabChange,
    isPending,
    search,
    debouncedSearch,
    setSearch: handleSearchChange,
    appliedFilters,
    pendingAppliedFilters: pendingApplied,
    historyAppliedFilters: historyApplied,
    appliedFiltersCount,
    filtersForm: isPending ? pendingForm : historyForm,
    filterDrawerOpen,
    openFilterDrawer,
    handleApplyFilters,
    handleResetFilters,
    handleCloseFilterDrawer,
  };
};

export default useApprovalsUrlState;
