import { useEffect, useState } from 'react';
import { type Path, useFormContext } from 'react-hook-form';
import { useInfiniteQuery } from 'react-query';
import { useNavigate, useOutletContext, useParams } from 'react-router';

import { IconZoomExclamation } from '@material-hu/icons/tabler';
import Stack from '@material-hu/mui/Stack';
import Typography from '@material-hu/mui/Typography/Typography';

import CardContainer from '@material-hu/components/design-system/CardContainer';
import Checkbox from '@material-hu/components/design-system/Checkbox/Checkbox';
import Search from '@material-hu/components/design-system/Inputs/Search';
import Skeleton from '@material-hu/components/design-system/Skeleton';
import StateCard from '@material-hu/components/design-system/StateCard';
import Switcher from '@material-hu/components/design-system/Switcher';
import Title from '@material-hu/components/design-system/Title';

import { useDebounce } from 'src/hooks/useDebounce';
import useGeneralError from 'src/hooks/useGeneralError';
import {
  getHourCategories,
  getPolicyHourCategories,
  type PolicyHourCategory,
} from 'src/services/timeTrackingService';
import { type PolicyOutletContext } from 'src/types/timeTracking';
import { useLokaliseTranslation } from 'src/utils/i18n';
import { flatPages } from 'src/utils/tableUtils';

import { InfiniteList } from 'src/components/InfiniteList';

import { timeTrackingKeys } from '../../../queries';
import { timeTrackingRoutes } from '../../../routes';
import { type HourCategory } from '../../../Settings/types';
import { type PolicyFormFields, policiesFields } from '../../form';

const PAGE_LIMIT = 10;

type HourCategoryItemBase = {
  id: number;
  name: string;
  description: string;
};

type HourCategoryItemProps = {
  hourCategory: HourCategoryItemBase;
};

const HourCategoryItem = ({ hourCategory }: HourCategoryItemProps) => {
  const { watch, setValue } = useFormContext<PolicyFormFields>();
  const { t } = useLokaliseTranslation('time_tracker');

  const isEnabled = watch(
    policiesFields.hoursManagement.hourCategoryId(
      hourCategory.id,
    ) as keyof PolicyFormFields,
  );

  const requiresApproval = watch(
    policiesFields.hoursManagement.hourCategoryApproval(
      hourCategory.id,
    ) as keyof PolicyFormFields,
  );

  const handleSwitchChange = () => {
    const categoryFieldName = policiesFields.hoursManagement.hourCategoryId(
      hourCategory.id,
    ) as keyof PolicyFormFields;
    const approvalFieldName =
      policiesFields.hoursManagement.hourCategoryApproval(
        hourCategory.id,
      ) as keyof PolicyFormFields;
    const currentValue = watch(categoryFieldName);
    const nextEnabled = !currentValue;
    setValue(categoryFieldName, nextEnabled, { shouldDirty: true });
    if (!nextEnabled) {
      setValue(approvalFieldName, false, { shouldDirty: true });
    }
  };

  const handleCheckboxChange = () => {
    const fieldName = policiesFields.hoursManagement.hourCategoryApproval(
      hourCategory.id,
    ) as keyof PolicyFormFields;
    const currentValue = watch(fieldName);
    setValue(fieldName, !currentValue, { shouldDirty: true });
  };

  return (
    <Stack
      sx={{
        p: 2,
        borderRadius: 2,
        backgroundColor: theme => theme.palette.new.background.layout.default,
      }}
    >
      <Switcher
        title={hourCategory.name}
        description={hourCategory.description}
        value={!!isEnabled}
        onChange={handleSwitchChange}
        titleProps={{
          sx: {
            fontWeight: 'fontWeightSemiBold',
          },
        }}
      />
      {isEnabled && (
        <Stack sx={{ mt: 2 }}>
          <Checkbox
            label={t('policies.requires_approval')}
            checked={!!requiresApproval}
            onChange={handleCheckboxChange}
          />
        </Stack>
      )}
    </Stack>
  );
};

const HoursManagement = () => {
  const { t } = useLokaliseTranslation('time_tracker');
  const showGeneralError = useGeneralError();
  const [search, setSearch] = useState('');
  const { debouncedValue: debouncedSearch, isDebouncing } = useDebounce(
    search,
    300,
  );
  const navigate = useNavigate();
  const { id: policyIdParam } = useParams<{ id: string }>();
  const policyId = policyIdParam ? parseInt(policyIdParam) : undefined;
  const isEditMode = !!policyId;

  const { getValues, register, resetField } =
    useFormContext<PolicyFormFields>();

  const { loadingPolicy = false } =
    useOutletContext<PolicyOutletContext>() || {};

  const createQuery = useInfiniteQuery(
    [
      ...timeTrackingKeys.categorizedHours({ page: 1, limit: PAGE_LIMIT }),
      { search: debouncedSearch },
    ],
    ({ pageParam = 1 }) =>
      getHourCategories({
        page: pageParam,
        limit: PAGE_LIMIT,
        search: debouncedSearch || undefined,
      }),
    {
      enabled: !isEditMode,
      getNextPageParam: lastPage => {
        const { page, totalPages } = lastPage.data;
        return page < totalPages ? page + 1 : undefined;
      },
      onError: err => {
        showGeneralError(err, t('settings.error_fetch'));
      },
    },
  );

  const editQuery = useInfiniteQuery(
    [
      ...timeTrackingKeys.policyHourCategories(policyId!),
      { search: debouncedSearch },
    ],
    ({ pageParam = 1 }) =>
      getPolicyHourCategories(policyId!, {
        page: pageParam,
        limit: PAGE_LIMIT,
        search: debouncedSearch || undefined,
      }),
    {
      enabled: isEditMode,
      getNextPageParam: lastPage => {
        const { page, totalPages } = lastPage.data;
        return page < totalPages ? page + 1 : undefined;
      },
      onError: err => {
        showGeneralError(err, t('settings.error_fetch'));
      },
    },
  );

  const activeQuery = isEditMode ? editQuery : createQuery;
  const {
    data: infiniteCategorizedHours,
    isFetching: isFetchingCategorizedHours,
    fetchNextPage,
    hasNextPage,
    isFetchingNextPage,
  } = activeQuery;

  const categorizedHours = flatPages<HourCategory | PolicyHourCategory>(
    infiniteCategorizedHours,
  );

  const hasCategorizedHours = categorizedHours && categorizedHours.length > 0;

  // Register dynamic fields when categories are loaded.
  // register + resetField align each path's default in RHF so toggling back to the server value clears isDirty
  // (setValue alone does not register fields; defaults stayed {} and isDirty could stick after revert).
  useEffect(() => {
    if (categorizedHours && !isFetchingCategorizedHours) {
      const currentValues = getValues();

      categorizedHours.forEach(hourCategory => {
        const categoryPath = policiesFields.hoursManagement.hourCategoryId(
          hourCategory.id,
        ) as Path<PolicyFormFields>;
        const approvalPath =
          policiesFields.hoursManagement.hourCategoryApproval(
            hourCategory.id,
          ) as Path<PolicyFormFields>;

        if (currentValues.hourCategoryIds?.[hourCategory.id] === undefined) {
          register(categoryPath);
          register(approvalPath);

          if (isEditMode) {
            const policyHourCategory = hourCategory as PolicyHourCategory;
            const assigned = policyHourCategory.assigned ?? false;
            const requiresApproval =
              policyHourCategory.requiresApproval ?? false;
            resetField(categoryPath, { defaultValue: assigned });
            resetField(approvalPath, { defaultValue: requiresApproval });
          } else {
            resetField(categoryPath, { defaultValue: false });
            resetField(approvalPath, { defaultValue: false });
          }
        }
      });
    }
  }, [
    isEditMode,
    categorizedHours,
    isFetchingCategorizedHours,
    register,
    resetField,
    getValues,
  ]);

  const isLoading =
    (isFetchingCategorizedHours && !isFetchingNextPage) ||
    isDebouncing ||
    loadingPolicy;

  const showEmptyState = !hasCategorizedHours && !isLoading;

  return (
    <Stack sx={{ flex: 1, minHeight: 0 }}>
      <Title
        variant="L"
        title={t('policies.hours_management')}
        description={t('policies.hours_management_description')}
        sx={{ mb: 3 }}
      />
      <CardContainer
        fullWidth
        sx={{
          mb: 1,
          flex: 1,
          flexDirection: 'column',
        }}
      >
        <Stack sx={{ mb: 2 }}>
          <Typography
            variant="globalM"
            sx={{ fontWeight: 'fontWeightSemiBold' }}
          >
            {t('categorized_hours.title')}
          </Typography>
          <Typography
            variant="globalXS"
            sx={{
              color: theme => theme.palette.new.text.neutral.lighter,
            }}
          >
            {t('categorized_hours.description_policy')}
          </Typography>
        </Stack>
        {showEmptyState && (
          <StateCard
            title={t('categorized_hours.empty_state_title')}
            description={t('categorized_hours.empty_state_description_policy')}
            icon={IconZoomExclamation}
            primaryAction={{
              label: t('categorized_hours.categorize_hours'),
              onClick: () => {
                navigate(timeTrackingRoutes.newCategorizedHours());
              },
            }}
          />
        )}
        {!showEmptyState && (
          <Stack sx={{ gap: 2, flex: 1, minHeight: 0 }}>
            <Search
              placeholder={t('general:search')}
              value={search}
              onChange={setSearch}
            />

            <Stack sx={{ flex: 1, minHeight: 0, overflowY: 'auto' }}>
              <InfiniteList
                isSuccess={!!infiniteCategorizedHours}
                isLoading={isLoading}
                fetchNextPage={fetchNextPage}
                hasNextPage={hasNextPage}
                isFetchingNextPage={isFetchingNextPage}
                loadingFallback={
                  <Stack sx={{ gap: 2 }}>
                    {Array.from({ length: 3 }).map((_, index) => (
                      <Skeleton
                        key={index}
                        isLoading
                        sx={{ borderRadius: 2, height: 64, maxWidth: 'unset' }}
                      />
                    ))}
                  </Stack>
                }
                sx={{ p: 0 }}
              >
                <Stack sx={{ gap: 2 }}>
                  {!isLoading &&
                    categorizedHours?.map(hourCategory => (
                      <HourCategoryItem
                        key={hourCategory.id}
                        hourCategory={hourCategory}
                      />
                    ))}
                </Stack>
              </InfiniteList>
            </Stack>
          </Stack>
        )}
      </CardContainer>
    </Stack>
  );
};

export default HoursManagement;
