import React, {
  useCallback,
  useEffect,
  useMemo,
  useRef,
  useState,
} from 'react';
import { useNavigate } from 'react-router-dom';
import {
  DownloadIcon,
  CloseIcon,
  IconZoomInArea,
  DetailPanelCollapseIcon,
  WindowMaximizeIcon,
} from '../ui/icons';
import PreviewZoomOverlay from '../detail/PreviewZoomOverlay';
import {
  buildCorrespondentOptions,
  sortCorrespondents,
} from '../documents/DocumentSummarySection';
import { extractDocumentMetadataPayload } from '../documents/documentMetadata';
import { createDocumentActionState } from '../documents/documentActions';
import { resolveDocumentAssetUrl } from '../asset_manager';
import PanelHeader from '../ui/PanelHeader';
import BreadcrumbTrail from '../ui/BreadcrumbTrail';
import { useAssetNavigator } from '../hooks/useAssetNavigator';
import DocumentViewerLayout from './DocumentViewerLayout';
import useViewerLayoutMode from './useViewerLayoutMode';
import { useSidebarContext } from '../sidebar/SidebarContext';

const DETAIL_PANEL_WIDTH_STORAGE_KEY = 'detailPanelWidth';
const MIN_DETAIL_PANEL_WIDTH = 320;
const MAX_DETAIL_PANEL_WIDTH = 960;

const isBrowser = typeof window !== 'undefined';
const isDocumentAvailable = typeof document !== 'undefined';

const getDetailPanelBounds = () => {
  if (!isBrowser) {
    return {
      min: MIN_DETAIL_PANEL_WIDTH,
      max: MAX_DETAIL_PANEL_WIDTH,
    };
  }
  const viewportWidth = Math.max(window.innerWidth, 1);
  const minFractionWidth = viewportWidth / 5;
  const maxFractionWidth = viewportWidth * 0.75;
  const rawMin = Math.max(MIN_DETAIL_PANEL_WIDTH, minFractionWidth);
  const rawMax = Math.min(MAX_DETAIL_PANEL_WIDTH, maxFractionWidth);
  if (rawMin >= rawMax) {
    const fallback = Math.min(Math.max(rawMin, viewportWidth * 0.5), MAX_DETAIL_PANEL_WIDTH);
    return { min: fallback, max: fallback };
  }
  return {
    min: rawMin,
    max: rawMax,
  };
};

const clampDetailPanelWidth = (value) => {
  if (!Number.isFinite(value) || value <= 0) {
    return null;
  }
  const { min, max } = getDetailPanelBounds();
  return Math.min(Math.max(value, min), max);
};

const loadStoredDetailPanelWidth = () => {
  if (!isBrowser) {
    return null;
  }
  try {
    const raw = window.localStorage?.getItem(DETAIL_PANEL_WIDTH_STORAGE_KEY);
    if (!raw) {
      return null;
    }
    const parsed = parseInt(raw, 10);
    return Number.isFinite(parsed) ? parsed : null;
  } catch (error) {
    console.warn('Failed to read detail panel width', error);
    return null;
  }
};

const applyDetailPanelWidth = (width) => {
  if (!isDocumentAvailable || width == null) {
    return;
  }
  document.documentElement.style.setProperty('--detail-panel-width', `${width}px`);
};

const getSidebarWidthFromRoot = () => {
  if (!isBrowser || !isDocumentAvailable) {
    return null;
  }
  const computed = window.getComputedStyle(document.documentElement);
  const parsed = parseFloat(computed.getPropertyValue('--sidebar-width'));
  return Number.isFinite(parsed) ? parsed : null;
};

const shouldSuppressSidebarForDetailWidth = (detailWidth) => {
  if (!isBrowser || !Number.isFinite(detailWidth)) {
    return false;
  }
  const sidebarWidth = getSidebarWidthFromRoot() || 0;
  const minMainContentWidth = window.innerWidth / 3;
  const occupiedWidth = detailWidth + sidebarWidth;
  const availableWidth = window.innerWidth - occupiedWidth;
  return availableWidth < minMainContentWidth;
};

export const createDocumentViewerHeaderActions = ({
  document,
  actionState,
  previewEntry,
  onZoom,
  canZoom = false,
}) => {
  if (!document) {
    return null;
  }

  const downloadHref = actionState?.downloadHref || previewEntry?.url;
  if (!downloadHref && !(canZoom && onZoom)) {
    return null;
  }

  return (
    <>
      {downloadHref ? (
        <a
          className="icon-button"
          href={downloadHref}
          target="_blank"
          rel="noopener noreferrer"
          aria-label="Download document"
          title="Download document"
        >
          <DownloadIcon />
        </a>
      ) : null}
      {canZoom && onZoom ? (
        <button
          type="button"
          className="icon-button"
          onClick={onZoom}
          aria-label="Open zoom preview"
          title="Open zoom preview"
        >
          <IconZoomInArea />
        </button>
      ) : null}
    </>
  );
};

const DocumentViewerPanel = ({
  document,
  previewEntry,
  hydrateDocument,
  tagLookupById,
  tagOptions,
  onTagAdd,
  onTagRemove,
  correspondents,
  onCorrespondentAdd,
  onCorrespondentRemove,
  onUpdateTitle,
  onUpdateIssued,
  ensureAssetUrl,
  getDocumentAsset,
  ensurePreviewData,
  resolveApiPath,
  notifyApiError,
  sidebarToggle = null,
  onClosePanel,
  resolveFolderPath,
  variant = 'viewer',
  onCollapsePanel,
  onMaximizePanel,
}) => {
  const navigate = useNavigate();
  const isSidebarVariant = variant === 'sidebar';
  const { setSidebarSuppressed } = useSidebarContext();
  const sortedCorrespondents = useMemo(
    () => sortCorrespondents(document?.correspondents || []),
    [document],
  );

  const correspondentOptions = useMemo(
    () => buildCorrespondentOptions(Array.isArray(correspondents) ? correspondents : []),
    [correspondents],
  );

  const metadataPayload = useMemo(
    () => extractDocumentMetadataPayload(document),
    [document],
  );

  const hasOcr = useMemo(() => {
    if (!document || typeof getDocumentAsset !== 'function') {
      return false;
    }
    return Boolean(getDocumentAsset(document, 'ocr-text'));
  }, [document, getDocumentAsset]);

  const summaryProps = useMemo(
    () => ({
      tagLookupById,
      tagOptions,
      onTagAdd,
      onTagRemove,
      correspondents: sortedCorrespondents,
      correspondentOptions,
      onCorrespondentAdd,
      onCorrespondentRemove,
      onUpdateTitle,
      onUpdateIssued,
    }),
    [
      tagLookupById,
      tagOptions,
      onTagAdd,
      onTagRemove,
      sortedCorrespondents,
      correspondentOptions,
      onCorrespondentAdd,
      onCorrespondentRemove,
      onUpdateTitle,
      onUpdateIssued,
    ],
  );

  const loadOcrContent = useCallback(async ({ signal } = {}) => {
    if (!document || !hasOcr || typeof getDocumentAsset !== 'function') {
      return '';
    }

    const updateUrl = () =>
      resolveDocumentAssetUrl(document, 'ocr-text', {
        ensureAssetUrl,
        getAsset: getDocumentAsset,
      });

    const asset = getDocumentAsset(document, 'ocr-text');
    let url = updateUrl();

    if (!url && document.id && asset?.id && typeof ensureAssetUrl === 'function') {
      await ensureAssetUrl(document.id, asset, { start: 1, limit: 1 });
      if (signal?.aborted) {
        throw new DOMException('Aborted', 'AbortError');
      }
      url = updateUrl();
    }

    if (!url) {
      return '';
    }

    const response = await fetch(url, {
      method: 'GET',
      mode: 'cors',
      credentials: 'omit',
      signal,
    });

    if (!response.ok) {
      throw new Error(`Unexpected status: ${response.status}`);
    }

    return response.text();
  }, [document, hasOcr, getDocumentAsset, ensureAssetUrl]);

  const contentTabConfig = useMemo(
    () => ({
      enabled: hasOcr,
      id: 'content',
      label: 'Content',
      loadContent: loadOcrContent,
      loadingMessage: 'Loading OCR content…',
      emptyMessage: 'No OCR content available.',
      unavailableMessage: 'No OCR content available.',
      errorMessage: 'Failed to load OCR content.',
    }),
    [hasOcr, loadOcrContent],
  );

  const [zoomOverlayOpen, setZoomOverlayOpen] = useState(false);
  const previewNavigator = useAssetNavigator({
    document,
    assetType: 'preview',
    ensureAssetUrl,
    getAsset: getDocumentAsset,
    prefetch: 3,
  });
  const navigatorUrl = previewNavigator?.currentUrl;
  const navigatorCanGoPrev = Boolean(previewNavigator?.canGoPrev);
  const navigatorCanGoNext = Boolean(previewNavigator?.canGoNext);
  const navigatorGoPrev = previewNavigator?.goPrev;
  const navigatorGoNext = previewNavigator?.goNext;

  const handleZoomOpen = useCallback(() => {
    if (!previewEntry?.url) {
      return;
    }
    setZoomOverlayOpen(true);
  }, [previewEntry?.url]);

  const handleZoomClose = useCallback(() => {
    setZoomOverlayOpen(false);
  }, []);

  useEffect(() => {
    setZoomOverlayOpen(false);
  }, [previewEntry?.url, document?.id]);

  useEffect(() => {
    if (typeof hydrateDocument === 'function' && document?.id) {
      hydrateDocument(document.id);
    }
  }, [hydrateDocument, document?.id]);

  const panelRef = useRef(null);
  const isStackedLayout = useViewerLayoutMode(panelRef, document?.id);
  const pendingWidthRef = useRef(null);
  const [isResizingPanel, setIsResizingPanel] = useState(false);
  const [detailPanelWidth, setDetailPanelWidth] = useState(() => {
    if (!isBrowser) {
      return null;
    }
    const stored = loadStoredDetailPanelWidth();
    return stored != null ? clampDetailPanelWidth(stored) : null;
  });

  useEffect(() => {
    if (detailPanelWidth != null) {
      const clamped = clampDetailPanelWidth(detailPanelWidth);
      if (clamped != null) {
        applyDetailPanelWidth(clamped);
      }
    }
  }, [detailPanelWidth]);

  useEffect(() => {
    if (!isSidebarVariant) {
      setSidebarSuppressed(false);
      return undefined;
    }
    const updateSuppression = () => {
      if (!isBrowser) {
        setSidebarSuppressed(false);
        return;
      }
      const panelWidth = pendingWidthRef.current
        ?? detailPanelWidth
        ?? panelRef.current?.getBoundingClientRect().width;
      setSidebarSuppressed(shouldSuppressSidebarForDetailWidth(panelWidth));
    };

    updateSuppression();
    const handleWindowResize = () => updateSuppression();
    window.addEventListener('resize', handleWindowResize);
    return () => {
      window.removeEventListener('resize', handleWindowResize);
    };
  }, [isSidebarVariant, detailPanelWidth, setSidebarSuppressed]);

  useEffect(() => {
    if (!isSidebarVariant) {
      setSidebarSuppressed(false);
    }
  }, [isSidebarVariant, setSidebarSuppressed]);

  useEffect(() => {
    if (!isBrowser) {
      return undefined;
    }
    const handleResize = () => {
      setDetailPanelWidth((prev) => {
        if (prev == null) {
          return prev;
        }
        const clamped = clampDetailPanelWidth(prev);
        if (clamped != null && clamped !== prev) {
          applyDetailPanelWidth(clamped);
          try {
            window.localStorage?.setItem(DETAIL_PANEL_WIDTH_STORAGE_KEY, String(Math.round(clamped)));
          } catch (error) {
            console.warn('Failed to persist detail panel width', error);
          }
          return clamped;
        }
        return prev;
      });
    };
    window.addEventListener('resize', handleResize);
    return () => window.removeEventListener('resize', handleResize);
  }, []);

  const handleResizePointerDown = useCallback((event) => {
    if (!isSidebarVariant || !panelRef.current || !isBrowser) {
      return;
    }
    event.preventDefault();
    event.stopPropagation();
    const pointerId = event.pointerId;
    const target = event.currentTarget;
    target.setPointerCapture?.(pointerId);
    setIsResizingPanel(true);

    const rect = panelRef.current.getBoundingClientRect();
    const startWidth = rect.width;
    const startX = event.clientX;

    const updateWidth = (nextWidth) => {
      const clamped = clampDetailPanelWidth(nextWidth);
      if (clamped != null) {
        pendingWidthRef.current = clamped;
        applyDetailPanelWidth(clamped);
        if (isSidebarVariant) {
          setSidebarSuppressed(shouldSuppressSidebarForDetailWidth(clamped));
        }
      }
    };

    const handlePointerMove = (moveEvent) => {
      if (moveEvent.pointerId !== pointerId) {
        return;
      }
      const delta = startX - moveEvent.clientX;
      updateWidth(startWidth + delta);
    };

    const handlePointerUp = (upEvent) => {
      if (upEvent.pointerId !== pointerId) {
        return;
      }
      target.releasePointerCapture?.(pointerId);
      window.removeEventListener('pointermove', handlePointerMove);
      window.removeEventListener('pointerup', handlePointerUp);
      setIsResizingPanel(false);
      if (pendingWidthRef.current != null) {
        const finalizedWidth = pendingWidthRef.current;
        pendingWidthRef.current = null;
        setDetailPanelWidth(finalizedWidth);
        try {
          window.localStorage?.setItem(DETAIL_PANEL_WIDTH_STORAGE_KEY, String(Math.round(finalizedWidth)));
        } catch (error) {
          console.warn('Failed to persist detail panel width', error);
        }
      }
    };

    window.addEventListener('pointermove', handlePointerMove);
    window.addEventListener('pointerup', handlePointerUp);
  }, [isSidebarVariant, setSidebarSuppressed]);

  const handleResizeKeyDown = useCallback((event) => {
    if (!isSidebarVariant || !isBrowser) {
      return;
    }
    if (event.key !== 'ArrowLeft' && event.key !== 'ArrowRight') {
      return;
    }
    const baseWidth = pendingWidthRef.current
      ?? detailPanelWidth
      ?? panelRef.current?.getBoundingClientRect().width;
    if (!Number.isFinite(baseWidth)) {
      return;
    }
    event.preventDefault();
    const step = event.shiftKey ? 40 : 20;
    const delta = event.key === 'ArrowLeft' ? step : -step;
    const nextWidth = clampDetailPanelWidth(baseWidth + delta);
    if (nextWidth == null) {
      return;
    }
    setDetailPanelWidth(nextWidth);
    try {
      window.localStorage?.setItem(DETAIL_PANEL_WIDTH_STORAGE_KEY, String(Math.round(nextWidth)));
    } catch (error) {
      console.warn('Failed to persist detail panel width', error);
    }
  }, [detailPanelWidth, isSidebarVariant]);

  const viewerClassName = isStackedLayout
    ? 'document-viewer document-viewer--stacked'
    : 'document-viewer';

  const actionState = useMemo(
    () =>
      document
        ? createDocumentActionState({
            document,
            resolveApiPath,
            ensurePreviewData,
            ensureAssetUrl,
            getDocumentAsset,
            notifyApiError,
            ocrErrorMessage: 'Unable to open OCR text.',
          })
        : null,
    [
      document,
      resolveApiPath,
      ensurePreviewData,
      ensureAssetUrl,
      getDocumentAsset,
      notifyApiError,
    ],
  );

  const breadcrumbs = useMemo(() => {
    if (!document || typeof resolveFolderPath !== 'function') {
      return [];
    }
    const folderSegments = resolveFolderPath(document.folder_id);
    const normalizedSegments = Array.isArray(folderSegments)
      ? folderSegments
          .filter((segment) => segment && segment.id && segment.name)
          .map((segment) => ({ id: segment.id, name: segment.name }))
      : [];

    return [
      ...normalizedSegments,
      { id: document.id, name: document.title },
    ];
  }, [document, resolveFolderPath]);

  const handleBreadcrumbNavigate = useCallback(
    (crumb) => {
      if (!crumb?.id) {
        return;
      }
      const target = crumb.id === 'root'
        ? '/documents'
        : `/documents/folder/${crumb.id}`;
      navigate(target);
    },
    [navigate],
  );

  const breadcrumbTrailEntries = useMemo(() => {
    if (!breadcrumbs.length) {
      return [];
    }
    const lastIndex = breadcrumbs.length - 1;
    return breadcrumbs.map((crumb, index) => ({
      id: crumb.id,
      label: crumb.name,
      onClick: index < lastIndex ? () => handleBreadcrumbNavigate(crumb) : null,
    }));
  }, [breadcrumbs, handleBreadcrumbNavigate]);

  const zoomDisplay = useMemo(() => {
    if (navigatorUrl && document) {
      return {
        url: navigatorUrl,
        alt: document.title,
        canGoPrev: navigatorCanGoPrev,
        canGoNext: navigatorCanGoNext,
        goPrev: navigatorCanGoPrev ? navigatorGoPrev : undefined,
        goNext: navigatorCanGoNext ? navigatorGoNext : undefined,
      };
    }
    if (previewEntry?.url && document) {
      return {
        url: previewEntry.url,
        alt: document.title,
        canGoPrev: false,
        canGoNext: false,
      };
    }
    return null;
  }, [
    navigatorUrl,
    navigatorCanGoPrev,
    navigatorCanGoNext,
    navigatorGoPrev,
    navigatorGoNext,
    previewEntry?.url,
    document,
  ]);

  const headerActions = createDocumentViewerHeaderActions({
    document,
    actionState,
    previewEntry,
    onZoom: zoomDisplay ? handleZoomOpen : null,
    canZoom: Boolean(zoomDisplay),
  });

  const collapseButton = isSidebarVariant && typeof onCollapsePanel === 'function'
    ? (
        <button
          type="button"
          className="icon-button"
          onClick={() => onCollapsePanel?.()}
          aria-label="Close detail panel"
          title="Close detail panel"
        >
          <DetailPanelCollapseIcon />
        </button>
      )
    : null;

  const maximizeButton = isSidebarVariant && typeof onMaximizePanel === 'function'
    ? (
        <button
          type="button"
          className="icon-button"
          onClick={(event) => {
            event.stopPropagation();
            onMaximizePanel?.(document?.id);
          }}
          aria-label="Maximize"
          title="Maximize"
        >
          <WindowMaximizeIcon className="icon--flip-y" />
        </button>
      )
    : null;

  const closeButton = !isSidebarVariant && onClosePanel
    ? (
        <button
          type="button"
          className="icon-button"
          onClick={() => onClosePanel?.()}
          aria-label="Close preview"
          title="Close preview"
        >
          <CloseIcon />
        </button>
      )
    : null;

  const headerLeadingButtons = isSidebarVariant
    ? [collapseButton, maximizeButton].filter(Boolean)
    : [sidebarToggle, closeButton].filter(Boolean);
  const headerLeadingContent = headerLeadingButtons.length
    ? (
        <>
          {headerLeadingButtons}
        </>
      )
    : null;

  const resizeHandle = isSidebarVariant ? (
    <button
      type="button"
      className={`detail-panel__resize-handle${isResizingPanel ? ' is-active' : ''}`}
      aria-label="Resize detail panel"
      onPointerDown={handleResizePointerDown}
      onKeyDown={handleResizeKeyDown}
    >
      <span aria-hidden="true" />
    </button>
  ) : null;

  const loadingSection = (
    <div className="document-viewer-panel__body">
      <section className="document-viewer document-viewer--loading">
        <div className="document-viewer__details-pane">
          <div className="document-viewer__details">
            <div className="document-viewer__message">Loading document…</div>
          </div>
        </div>
        <div className="document-viewer__viewport">
          <div className="document-viewer__message">Preparing preview…</div>
        </div>
      </section>
    </div>
  );

  const viewerSection = document ? (
    <div
      className={isStackedLayout
        ? 'document-viewer-panel__body document-viewer-panel__body--stacked'
        : 'document-viewer-panel__body'}
    >
      <section className={viewerClassName}>
        <DocumentViewerLayout
          document={document}
          previewEntry={previewEntry}
          summaryProps={summaryProps}
          metadataPayload={metadataPayload}
          contentTabConfig={contentTabConfig}
          previewLoadingMessage="Loading preview…"
          layoutMode={isStackedLayout ? 'stacked' : 'split'}
        />
      </section>
    </div>
  ) : loadingSection;

  const headerTitle = breadcrumbTrailEntries.length ? (
    <BreadcrumbTrail
      entries={breadcrumbTrailEntries}
      separator="/"
      className="panel-header__breadcrumbs"
      truncateFromStart={isSidebarVariant}
    />
  ) : (
    document?.title || 'Document preview'
  );

  const overlay = (
    <PreviewZoomOverlay
      open={Boolean(zoomOverlayOpen && zoomDisplay)}
      display={zoomDisplay}
      onClose={handleZoomClose}
    />
  );

  if (isSidebarVariant) {
    return (
      <>
        <aside className={`detail-panel panel${isResizingPanel ? ' detail-panel--resizing' : ''}`} ref={panelRef}>
          {resizeHandle}
          <PanelHeader
            leading={headerLeadingContent}
            title={headerTitle}
            titleTag="h3"
            actions={headerActions}
          />
          <div className="panel-body detail-panel__content">{viewerSection}</div>
        </aside>
        {overlay}
      </>
    );
  }

  return (
    <>
      <section className="document-viewer-panel" ref={panelRef}>
        <PanelHeader
          leading={headerLeadingContent}
          title={headerTitle}
          titleTag="h3"
          actions={headerActions}
        />
        {viewerSection}
      </section>
      {overlay}
    </>
  );
};

export default DocumentViewerPanel;

export const createDocumentViewerSurface = ({
  document,
  previewEntry,
  ensureAssetUrl,
  ensurePreviewData,
  getDocumentAsset,
  resolveApiPath,
  notifyApiError,
  onClose,
  renderSidebarToggle,
  tagLookupById,
  tagOptions,
  onTagAdd,
  onTagRemove,
  correspondents,
  onCorrespondentAdd,
  onCorrespondentRemove,
  onUpdateTitle,
  onUpdateIssued,
  resolveFolderPath,
}) => {
  if (!document) {
    return null;
  }

  const sidebarToggle = typeof renderSidebarToggle === 'function'
    ? renderSidebarToggle()
    : null;

  return {
    key: 'preview',
    variant: 'preview',
    header: null,
    content: (
      <DocumentViewerPanel
        document={document}
        previewEntry={previewEntry}
        hydrateDocument={ensurePreviewData}
        tagLookupById={tagLookupById}
        tagOptions={tagOptions}
        onTagAdd={onTagAdd}
        onTagRemove={onTagRemove}
        correspondents={correspondents}
        onCorrespondentAdd={onCorrespondentAdd}
        onCorrespondentRemove={onCorrespondentRemove}
        onUpdateTitle={onUpdateTitle}
        onUpdateIssued={onUpdateIssued}
        ensureAssetUrl={ensureAssetUrl}
        getDocumentAsset={getDocumentAsset}
        ensurePreviewData={ensurePreviewData}
        resolveApiPath={resolveApiPath}
        notifyApiError={notifyApiError}
        sidebarToggle={sidebarToggle}
        onClosePanel={onClose}
        resolveFolderPath={resolveFolderPath}
      />
    ),
    supportsDetail: false,
  };
};
