/**
 * @file Processing.tsx - Processing page component that loads and validates a work item by checking lock status and retrieving all related data.
 *
 * On success, renders a three-panel layout
 * - DetailsFolderPanel (work item details and folder tree)
 * - FieldsNotesTrackingPanel (field editing controls, notes, and tracking records)
 * - DocumentEditorPanel (PDF viewer and document editing tools)
 *
 * Desktop uses resizable panels, while mobile renders panels stacked vertically. Also renders UnsavedChangesModal and CloseWorkItemModal overlays.
 */

import { Navigate, useParams } from 'react-router-dom';
import {
  type ImperativePanelHandle,
  Panel,
  PanelGroup,
} from 'react-resizable-panels';
import { CustomPanelResizeHandle } from '../components/Processing/CustomPanelResizeHandle.tsx';
import { DetailsFolderPanel } from '../components/Processing/DetailsFolderPanel/DetailsFolderPanel.tsx';
import { FieldsNotesTrackingPanel } from '../components/Processing/FieldsNotesTrackingPanel/FieldsNotesTrackingPanel.tsx';
import { DocumentEditorPanel } from '../components/Processing/DocumentEditorPanel/DocumentEditorPanel.tsx';
import { useEffect, useRef, useState } from 'react';
import {
  getInboxWorkItems,
  getIsWorkItemUnlocked,
  getQueueWorkItems,
  lockWorkItem,
  type WorkItem,
} from '../api/workItem.ts';
import { toast } from 'react-toastify';
import { Spinner } from 'react-bootstrap';
import {
  getProcessPermissions,
  listAllowedTemplates,
  listProcesses,
} from '../api/process.ts';
import { listQueues } from '../api/queue.ts';
import { listButtons } from '../api/buttons.ts';
import { listDocuments } from '../api/document.ts';
import { listNotes } from '../api/note.ts';
import { listTrackingRecords } from '../api/trackingRecord.ts';
import { getWorkItemFolderFields } from '../api/folderField.ts';
import { useProcessPermissions } from '../contexts/ProcessPermissionsContext.tsx';
import { AtAllowRights } from '../types/atAllowRights.ts';
import { useReauth } from '../contexts/ReauthContext.tsx';
import { useWorkItem } from '../contexts/WorkItemContext.tsx';
import { useDocument } from '../contexts/DocumentContext.tsx';
import { useProcessingUI } from '../contexts/ProcessingUIContext.tsx';
import { ContentCard } from '../components/ContentCard.tsx';
import { UnsavedChangesModal } from '../components/Processing/modals/UnsavedChangesModal.tsx';
import { CloseWorkItemModal } from '../components/Processing/modals/CloseWorkItemModal.tsx';
import { useTranslation } from 'react-i18next';
import { useAuth } from '../contexts/AuthContext.tsx';

/**
 * Processing view that allows modification/manipulation of work items and their documents. Uses resizable panels on desktop screen sizes.
 */
export const Processing = () => {
  const params = useParams();
  const processId = Number(params.processId);
  const queueId = Number(params.queueId);
  const workItemReferenceId = Number(params.workItemReferenceId);

  const { username } = useAuth();

  // Used to trigger data refresh
  const { reauthToken, oldReauthToken, setOldReauthToken } = useReauth();

  // Required contexts for work item processing
  const {
    setProcess,
    setQueue,
    setWorkItem,
    setFolderFields,
    setButtons,
    setDocumentTemplates,
    setDocuments,
    setNotes,
    setTrackingRecords,
  } = useWorkItem();
  const { setSelectedDocumentId } = useDocument();
  const { setPermissions } = useProcessPermissions();

  // Tri-state render flag: null = loading, true = render content (panels), false = redirect home
  const [shouldRender, setShouldRender] = useState<boolean | null>(null);

  // Retrieves and validates all work item data: process, queue, work item, folder fields, buttons, templates, documents, notes, and tracking records.
  // Returns a RangeError sentinel on validation/permission failures.
  const retrieveBaseData = async () => {
    try {
      // Retrieve processes and permissions
      const [_processes, _permissions] = await Promise.all([
        listProcesses(),
        getProcessPermissions(processId),
      ]);
      setPermissions(_permissions);

      // Find match for process and check permissions
      const _foundProcess = _processes.find(
        (process) => process.id === processId
      );
      if (_foundProcess) {
        setProcess(_foundProcess);
      } else {
        setShouldRender(false);

        if (
          _permissions.length &&
          !_permissions.includes(AtAllowRights.UseProcess)
        ) {
          toast.error(
            t(
              'functions.retrieve_base_data.errors.process_lacking_permission',
              { process_id: processId }
            )
          );
        } else {
          toast.error(
            t('functions.retrieve_base_data.errors.process_not_found', {
              process_id: processId,
            })
          );
        }
        return new RangeError();
      }

      // Retrieve queues for this process and attempt to find the target queue
      const _queues = await listQueues(processId);
      const _foundQueue = _queues.find((queue) => queue.queueId === queueId);

      // If the queue exists, fetch its work items; otherwise fall back to inbox
      let _workItems: WorkItem[];
      if (_foundQueue) {
        setQueue(_foundQueue);
        _workItems = await getQueueWorkItems(queueId);
      } else {
        _workItems = await getInboxWorkItems();
      }
      const _foundWorkItem = _workItems.find(
        (workItem) => workItem.workItemReferenceId === workItemReferenceId
      );
      if (_foundWorkItem) {
        const locked = await checkWorkItemLockStatus();
        if (!locked) {
          return new RangeError();
        }

        setWorkItem(_foundWorkItem);

        // Special case for inbox-based WorkItems, which have special queues
        // This assumes inbox queues should not be signable
        if (!_foundQueue) {
          if (
            _foundWorkItem.queueId === queueId &&
            _foundWorkItem.assignedTo?.toLowerCase() === username?.toLowerCase()
          ) {
            setQueue({
              queueId,
              queueName: _foundWorkItem.queueName,
              queueAlias: _foundWorkItem.queueName,
              signable: false,
            });
          } else {
            setShouldRender(false);
            toast.error(
              t(
                'functions.retrieve_base_data.errors.inbox_lacking_permission',
                {
                  queue_id: queueId,
                }
              )
            );
            return new RangeError();
          }
        }
      } else {
        setShouldRender(false);
        toast.error(
          t('functions.retrieve_base_data.errors.work_item_not_found', {
            reference_id: workItemReferenceId,
            queue_id: queueId,
          })
        );
        return new RangeError();
      }

      // Concurrently retrieve remaining data
      const [_folderFields, _buttons, _documentTemplates, _documents, _notes] =
        await Promise.all([
          getWorkItemFolderFields(workItemReferenceId),
          _foundQueue
            ? listButtons(queueId)
            : listButtons(queueId, workItemReferenceId),
          listAllowedTemplates(processId),
          listDocuments(workItemReferenceId),
          listNotes(workItemReferenceId),
        ]);
      setFolderFields(_folderFields);
      setButtons(_buttons);
      setDocumentTemplates(_documentTemplates);
      setNotes(_notes);

      // Sort work item documents by ID ascending
      const _sortedDocuments = _documents.sort(
        (a, b) => a.documentId - b.documentId
      );
      setDocuments(_sortedDocuments);
      if (_documents.length) {
        setSelectedDocumentId(_sortedDocuments[0].documentId);
      }

      if (_permissions.includes(AtAllowRights.ViewTracking)) {
        // Retrieve work item tracking records
        const _trackingRecords = await listTrackingRecords(workItemReferenceId);
        setTrackingRecords(_trackingRecords);
      } else {
        setTrackingRecords(undefined);
      }
    } catch (error) {
      const isTypeError = error instanceof TypeError;
      if (!isTypeError) {
        toast.error((error as Error).message);
      }

      setProcess(undefined);
      setQueue(undefined);
      setWorkItem(undefined);
      setFolderFields([]);
      setButtons([]);
      setDocumentTemplates([]);
      setDocuments([]);
      setNotes([]);
      setTrackingRecords([]);
    }
  };

  // Checks if the work item is unlocked; if so, acquires the lock for this user
  // Returns true only when the lock is successfully acquired
  const checkWorkItemLockStatus = async () => {
    try {
      const locked = await getIsWorkItemUnlocked(workItemReferenceId);
      let lockSuccess = false;

      if (locked instanceof RangeError) {
        setShouldRender(false);
        toast.error(
          t(
            'functions.check_work_item_lock_status.errors.internal_server_error',
            {
              reference_id: workItemReferenceId,
            }
          )
        );
      } else if (locked) {
        setShouldRender(false);
        toast.error(
          t('functions.check_work_item_lock_status.errors.locked', {
            reference_id: workItemReferenceId,
          })
        );
      } else {
        try {
          // Lock work item for current user
          await lockWorkItem(workItemReferenceId);
          setShouldRender(true);
          lockSuccess = true;
        } catch {
          setShouldRender(false);
          toast.error(
            t('functions.check_work_item_lock_status.errors.failed_to_lock', {
              reference_id: workItemReferenceId,
            })
          );
        }
      }

      return lockSuccess;
    } catch (error) {
      toast.error((error as Error).message);
    }
  };

  // Load all work item data on initial mount
  useEffect(() => {
    retrieveBaseData();
  }, []);
  // Re-fetch data when the reauth token changes (session refresh) while still in loading state
  useEffect(() => {
    const tokenChanged = reauthToken !== oldReauthToken;

    if (tokenChanged && shouldRender === null) {
      retrieveBaseData().finally(() => {
        if (tokenChanged) {
          setOldReauthToken(reauthToken);
        }
      });
    }
  }, [reauthToken, oldReauthToken, setOldReauthToken]);

  const { t } = useTranslation(undefined, {
    keyPrefix: 'pages.processing',
  });

  if (shouldRender === null) {
    return <LoadingSpinner />;
  }

  // Redirect to main app page when work item doesn't exist
  if (!shouldRender) {
    return <Navigate to={'/'} />;
  }

  // Normal page content when work item exists and is unlocked
  return (
    <>
      <ProcessingLayout />
      <UnsavedChangesModal />
      <CloseWorkItemModal />
    </>
  );
};

/**
 * Loading spinner displayed while the work item lock status is being checked and all related data is being fetched.
 */
const LoadingSpinner = () => {
  const { t } = useTranslation(undefined, {
    keyPrefix: 'pages.processing.loading_spinner',
  });

  return (
    <div
      className={
        'd-flex flex-column gap-2 justify-content-center align-items-center flex-grow-1'
      }
    >
      <Spinner animation={'border'} role={'status'} variant={'primary'}>
        <span className={'visually-hidden'}>{t('spinner.default')}...</span>
      </Spinner>
      <div>{t('text')}...</div>
    </div>
  );
};

/**
 * Layout switcher that renders {@link MobileLayout} or {@link DesktopLayout} based on the current viewport size reported by ProcessingUIContext.
 */
const ProcessingLayout = () => {
  const { isMobile } = useProcessingUI();
  return isMobile ? <MobileLayout /> : <DesktopLayout />;
};

/**
 * Mobile layout that stacks the three processing panels vertically without resizable panel handles.
 */
const MobileLayout = () => (
  <>
    <DetailsFolderPanel />
    <FieldsNotesTrackingPanel />
    <DocumentEditorPanel />
  </>
);

/**
 * Desktop layout that arranges the three processing panels in a horizontal resizable panel group.
 * The FieldsNotesTrackingPanel is collapsible and shows a vertical label when collapsed.
 */
const DesktopLayout = () => {
  const { hasPermission } = useProcessPermissions();
  const { pdfViewerRef } = useDocument();

  const fieldsNotesTrackingPanel = useRef<ImperativePanelHandle>(null);
  const [
    fieldsNotesTrackingPanelCollapsed,
    setFieldsNotesTrackingPanelCollapsed,
  ] = useState(false);

  const { t } = useTranslation(undefined, {
    keyPrefix: 'pages.processing.desktop_layout',
  });

  return (
    <PanelGroup
      autoSaveId={'processing'}
      direction={'horizontal'}
      className={'flex-grow-1'}
    >
      <Panel
        id={'DetailsFolderPanel'}
        order={1}
        defaultSize={20}
        minSize={15}
        maxSize={20}
        className={'d-flex flex-column me-2'}
      >
        <DetailsFolderPanel />
      </Panel>
      <CustomPanelResizeHandle />
      <Panel
        id={'FieldsNotesTrackingPanel'}
        ref={fieldsNotesTrackingPanel}
        order={2}
        defaultSize={25}
        minSize={15}
        maxSize={25}
        className={'d-flex flex-column mx-2'}
        collapsible={true}
        collapsedSize={2.5}
        onCollapse={() => {
          setFieldsNotesTrackingPanelCollapsed(true);
          pdfViewerRef.current?.updateViewerContainer();
        }}
        onExpand={() => {
          setFieldsNotesTrackingPanelCollapsed(false);
          pdfViewerRef.current?.updateViewerContainer();
        }}
        onResize={() => pdfViewerRef.current?.updateViewerContainer()}
      >
        {fieldsNotesTrackingPanelCollapsed ? (
          <ContentCard fillPage padding={false}>
            <div
              className={
                'd-flex justify-content-center align-items-center h-100'
              }
              style={{ backgroundColor: 'var(--bs-card-cap-bg)' }}
              onClick={() => fieldsNotesTrackingPanel.current?.expand()}
              role={'button'}
            >
              <h5
                className={'text-bold lh-lg'}
                style={{
                  writingMode: 'sideways-lr',
                }}
              >
                {hasPermission(AtAllowRights.ViewTracking)
                  ? t('panels.fields_notes_tracking.view_tracking_permitted')
                  : t('panels.fields_notes_tracking.default')}
              </h5>
            </div>
          </ContentCard>
        ) : (
          <FieldsNotesTrackingPanel />
        )}
      </Panel>
      <CustomPanelResizeHandle />
      <Panel
        id={'DocumentEditorPanel'}
        order={3}
        defaultSize={60}
        minSize={55}
        maxSize={82.5}
        className={'d-flex flex-column ms-2'}
      >
        <DocumentEditorPanel />
      </Panel>
    </PanelGroup>
  );
};
