import { useCallback, useState } from 'react';

import { useDrawerV2 } from '@material-hu/hooks/useDrawerV2';

import { useAuth } from 'src/contexts/JWTContext';
import {
  useGetDayDetail,
  useGetUserPolicies,
  useGetUserSites,
} from 'src/hooks/queryHooks/timeTracking';
import {
  type DaySummary,
  type EntryFormFields,
  type EntrySource,
  EntryType,
  type SiteCoordinates,
  type SiteLocation,
  type UserTrackingStatus,
} from 'src/types/timeTracking';
import { getCompletedDate } from 'src/utils/date';
import {
  buildSelectedSiteFromForm,
  type FormSiteIds,
  type SelectedSite,
} from 'src/utils/timeTracking';

import { REMOTE_WORK_SITE_VALUE } from '../../constants';

import { applyFormSiteOverride } from './applyFormSiteOverride';
import { SiteLocationMapContent } from './components/Entries/SiteLocationDrawer';
import PeriodDetailDrawer from '.';

type DrawerProps = object;

type Props = {
  dayInfo?: DaySummary;
  currentDate?: string;
  loadingSummaries: boolean;
  userStatus?: UserTrackingStatus;
  isDeactivated?: boolean;
};

export const usePeriodDetailDrawer = (props: Props) => {
  const { userStatus, currentDate, loadingSummaries, dayInfo, isDeactivated } =
    props;
  const { user } = useAuth();
  // Track which entry type is currently being viewed in the map
  const [currentViewingEntryType, setCurrentViewingEntryType] =
    useState<EntryType | null>(null);
  // Track if map was explicitly opened by user (clicking "view map")
  // vs automatically opened by changing dropdown
  const [isMapExplicitlyOpen, setIsMapExplicitlyOpen] = useState(false);
  // Counter to force map recenter when clicking "view map" button
  const [mapRecenterKey, setMapRecenterKey] = useState(0);
  // Store site IDs for map building (legacy, still needed for buildSelectedSiteFromForm)
  const [formValues, setFormValues] = useState<FormSiteIds | undefined>(
    undefined,
  );
  // Store selected site directly when clicking from EntryDetail (for entries without siteId)
  const [directSelectedSite, setDirectSelectedSite] =
    useState<SelectedSite | null>(null);
  // Which accordion to open when the drawer shows, and to restore when the map
  // is closed via its own button.
  const [pendingAccordion, setPendingAccordion] = useState<
    string | undefined
  >();

  const { sites = [], isFetching: isFetchingUserSites } = useGetUserSites(
    dayInfo?.userId || 0,
  );

  const { policies } = useGetUserPolicies(dayInfo?.userId || 0);
  const enableRemoteWork = policies?.enableRemoteWork ?? false;

  const { dayDetail } = useGetDayDetail(
    dayInfo?.userId || 0,
    dayInfo?.dateString || '',
    { enabled: !!dayInfo },
  );

  const drawerTitle = dayInfo?.dateString
    ? getCompletedDate(`${dayInfo.dateString}T00:00:00.000`, user!)
    : '';
  const isCurrentDay = currentDate === dayInfo?.dateString;

  const handleSiteClick = (
    siteId: number | null,
    siteName: string,
    location: SiteLocation,
    markingLocation: SiteCoordinates | null,
    entrySource: EntrySource,
    entryType: EntryType,
  ) => {
    // Set which entry type we're currently viewing
    setCurrentViewingEntryType(entryType);
    // Mark that map was explicitly opened by user
    setIsMapExplicitlyOpen(true);
    // Increment counter to force map recenter
    setMapRecenterKey(prev => prev + 1);
    // Store selected site directly (for entries without siteId or when formValues is not available)
    // Use -1 as siteId if null to indicate no site but still show map
    setDirectSelectedSite({
      siteId: siteId ?? -1,
      siteName,
      location,
      markingLocation,
      entrySource,
      entryType,
    });
  };

  const handleCloseMap = (newOpenAccordion?: string) => {
    // Only update pendingAccordion when switching accordions; when hiding map (no arg)
    // keep current value so the accordion that was open stays open after map closes
    if (newOpenAccordion !== undefined) {
      setPendingAccordion(newOpenAccordion);
    }
    setCurrentViewingEntryType(null);
    setIsMapExplicitlyOpen(false);
    setDirectSelectedSite(null);
  };

  const handleOpenMap = (openAccordion?: string) => {
    setPendingAccordion(openAccordion);
  };

  // Reassign the site shown on the map (see applyFormSiteOverride), keeping the
  // entry's own marking location. Only the entry type the map is showing is
  // affected; calls for the other type no-op. Used for live edits and discard.
  const handleSelectedSiteChange = useCallback(
    (newSiteId: number, entryType: EntryType) => {
      // Don't open the map automatically when changing the dropdown
      if (!isMapExplicitlyOpen) return;
      setDirectSelectedSite(prev =>
        prev?.entryType === entryType
          ? applyFormSiteOverride(prev, newSiteId, sites)
          : prev,
      );
    },
    [isMapExplicitlyOpen, sites],
  );

  // Handle form values change from EntryAccordion
  const handleFormValuesChange = useCallback(
    (_values: EntryFormFields, _entryPairId: string, siteIds: FormSiteIds) => {
      // Save site IDs for map building (legacy)
      setFormValues(siteIds);
      // Only clear direct selected site when form values change if:
      // 1. The form has a valid siteId (not -1) for the current viewing entry type
      // 2. This allows directSelectedSite to persist when clicking from EntryDetail
      const formSiteId =
        currentViewingEntryType === EntryType.START
          ? siteIds.start.siteId
          : siteIds.end.siteId;
      // Only clear if form has a valid siteId (form takes precedence over direct selection).
      // When the user explicitly opened the map (e.g. "view map" on an entry), preserve
      // directSelectedSite so the map does not close when the form fires on mount/remount.
      if (
        formSiteId &&
        formSiteId !== -1 &&
        formSiteId !== REMOTE_WORK_SITE_VALUE
      ) {
        setDirectSelectedSite(currentDirectSite => {
          const shouldPreserveDirectSite =
            isMapExplicitlyOpen && !!currentDirectSite;
          return shouldPreserveDirectSite ? currentDirectSite : null;
        });
      }
    },
    [currentViewingEntryType, isMapExplicitlyOpen],
  );

  const handleDrawerClose = (closeDrawer: () => void) => {
    setCurrentViewingEntryType(null);
    setIsMapExplicitlyOpen(false);
    setDirectSelectedSite(null);
    setPendingAccordion(undefined);
    closeDrawer();
  };

  const drawerContent = dayInfo && userStatus && (
    <PeriodDetailDrawer
      dayInfo={dayInfo}
      dayDetail={dayDetail}
      isCurrentDay={isCurrentDay}
      loadingSummaries={loadingSummaries || isFetchingUserSites}
      userSites={sites}
      userStatus={userStatus}
      enableRemoteWork={enableRemoteWork}
      onSiteClick={handleSiteClick}
      onSelectedSiteChange={handleSelectedSiteChange}
      onFormValuesChange={handleFormValuesChange}
      onCloseMap={handleCloseMap}
      onOpenMap={handleOpenMap}
      defaultOpenAccordion={pendingAccordion}
      isDeactivated={isDeactivated}
    />
  );

  const {
    showDrawer: showPeriodDetailDrawer,
    drawer,
    closeDrawer: closePeriodDetailDrawer,
  } = useDrawerV2<DrawerProps>(({ closeDrawer }) => {
    // When a site is selected, show double layout with map
    // First try to build from form values, then fall back to direct selected site
    // (for entries without siteId clicked from EntryDetail)
    const selectedSiteFromForm = currentViewingEntryType
      ? buildSelectedSiteFromForm(
          formValues,
          sites,
          dayInfo,
          currentViewingEntryType,
        )
      : null;

    // directSelectedSite is the source of truth: it carries the clicked entry's
    // own site/marking/source, so the map opens on the location the user picked
    // even when the day has multiple pairs. selectedSiteFromForm is a fallback.
    const selectedSite = directSelectedSite || selectedSiteFromForm;

    // Keep the list always in `primaryContent` (never `children`): toggling
    // between them switches DrawerDoubleLayout <-> DrawerBody, which remounts the
    // list and drops its local state. A stable slot lets the map just
    // appear/collapse in `secondaryContent` with no remount.
    return {
      primaryContent: drawerContent,
      secondaryContent: selectedSite ? (
        <SiteLocationMapContent
          key={`${selectedSite.siteId}-${selectedSite.entryType}-${selectedSite.markingLocation?.latitude}-${selectedSite.markingLocation?.longitude}-${mapRecenterKey}`}
          siteId={selectedSite.siteId}
          siteName={selectedSite.siteName}
          location={selectedSite.location}
          allSites={sites}
          markingLocation={selectedSite.markingLocation}
          entrySource={selectedSite.entrySource}
          openAccordionIdToRestore={pendingAccordion}
          onClose={handleCloseMap}
        />
      ) : null,
      title: drawerTitle,
      hasBackButton: true,
      onClose: () => handleDrawerClose(closeDrawer),
      // The list now always lives in `primaryContent`, so the drawer is stuck at
      // its "double layout" (LARGE) size. Reproduce the original width shift
      // manually: single-pane width when the map is hidden, wider cap when shown.
      PaperProps: {
        sx: selectedSite
          ? { maxWidth: 1000 }
          : { width: 'min(600px, 100%)', maxWidth: '600px' },
      },
      // DoubleLayout composes slotProps.*.sx last, so sx wins over its width:50%
      // default. We omit width here on purpose: a bare width:420 lost to that
      // default, so the open panes stay 50/50.
      slotProps: selectedSite
        ? {
            primaryContent: {
              sx: { flexShrink: 0, overflowY: 'auto' },
            },
            secondaryContent: {
              sx: { flexGrow: 1, minWidth: 400 },
            },
          }
        : {
            // No map: list fills the width; the empty secondary panel collapses.
            primaryContent: {
              sx: { width: '100%', overflowY: 'auto' },
            },
            secondaryContent: {
              sx: { display: 'none' },
            },
          },
    };
  });

  const memoizedShowDrawer = useCallback(
    (initialAccordionId?: string) => {
      setPendingAccordion(initialAccordionId);
      showPeriodDetailDrawer({});
    },
    [showPeriodDetailDrawer],
  );

  return {
    drawer,
    showPeriodDetailDrawer: memoizedShowDrawer,
    closePeriodDetailDrawer,
  };
};
