import React, { useCallback, useMemo, useState } from 'react';
import { useQueryClient } from '@tanstack/react-query';
import styled from 'styled-components';
import { SearchTableWithPermissionCheck } from './Table';
import { DateDisplay } from './DateDisplay';
import { PatientNameDisplay } from './PatientNameDisplay';
import { useMedicationsContext } from '../contexts/Medications';
import { TranslatedText } from './Translation/TranslatedText';
import { useAuth } from '../contexts/Auth';
import { MEDICATIONS_SEARCH_KEYS } from '../constants/medication';
import { Colors } from '../constants';
import { MenuButton } from './MenuButton';
import { useDateTime } from '@tamanu/ui-components';
import { MedicationLabelPrintModal } from './PatientPrinting/modals/MedicationLabelPrintModal';
import { getDrugUnitLabel } from '@tamanu/shared/utils/medication';
import {
  DispensedMedicationName,
  getDispensedMedication,
  getMedicationLabelData,
  getTranslatedMedicationName,
  isDispenseModifiedByPharmacy,
  PharmacyModifiedFootnote,
} from '../utils/medications';
import { useTranslation } from '../contexts/Translation';
import { useFacilityQuery } from '../api/queries/useFacilityQuery';
import { useApi } from '../api';
import { CancelDispensedMedicationModal } from './Medication/CancelDispensedMedicationModal';
import { EditMedicationDispenseModal } from './Medication/EditMedicationDispenseModal';
import { DispensedMedicationDetailsModal } from './Medication/DispensedMedicationDetailsModal';
import { PrescriptionChangeHistoryModal } from './Medication/PrescriptionChangeHistoryModal';

const NoDataContainer = styled.div`
  height: 500px;
  font-size: 14px;
  font-weight: 500;
  border-radius: 3px;
  display: flex;
  align-items: center;
  justify-content: center;
  background: ${Colors.hoverGrey};
  color: ${Colors.primary};
`;

const StyledSearchTableWithPermissionCheck = styled(SearchTableWithPermissionCheck)`
  .MuiTableCell-body {
    height: 57px;
    padding: ${props => (props.$noData ? '20px 25px' : '10px 15px')};
    &:last-child {
      padding-right: 20px;
    }
    &:first-child {
      padding-left: 20px;
    }
  }
  .MuiTableBody-root .MuiTableRow-root:not(.statusRow) {
    cursor: pointer;
    &:hover {
      background-color: ${Colors.veryLightBlue};
    }
  }
`;

const getPatientDisplayId = ({ pharmacyOrderPrescription }) =>
  pharmacyOrderPrescription?.pharmacyOrder?.encounter?.patient?.displayId;
const getPatientName = ({ pharmacyOrderPrescription }) => (
  <PatientNameDisplay patient={pharmacyOrderPrescription?.pharmacyOrder?.encounter?.patient} />
);
const getMedication = dispense => <DispensedMedicationName dispense={dispense} />;
const getDateDispensed = ({ dispensedAt }) => (
  <DateDisplay date={dispensedAt} timeOnlyTooltip shortYear data-testid="datedisplay-date-sent" />
);
const getDispensedBy = ({ dispensedBy }) => dispensedBy?.displayName;
const getRequestNumber = ({ pharmacyOrderPrescription }) => pharmacyOrderPrescription?.displayId;

export const MedicationDispensesTable = () => {
  const api = useApi();
  const queryClient = useQueryClient();
  const { ability, facilityId } = useAuth();
  const { getReferenceDataTranslation, getEnumTranslation } = useTranslation();
  const { searchParameters } = useMedicationsContext(MEDICATIONS_SEARCH_KEYS.DISPENSED);
  const { data: facility } = useFacilityQuery(facilityId);
  const { getCurrentDateTime } = useDateTime();

  const [medicationDispenses, setMedicationDispenses] = useState([]);
  const [printModalOpen, setPrintModalOpen] = useState(false);
  const [selectedLabelData, setSelectedLabelData] = useState([]);
  const [hoveredRow, setHoveredRow] = useState(null);
  const [isCancelModalOpen, setIsCancelModalOpen] = useState(false);
  const [selectedDispense, setSelectedDispense] = useState(null);
  const [isEditModalOpen, setIsEditModalOpen] = useState(false);
  const [refreshCount, setRefreshCount] = useState(0);
  const [isDetailModalOpen, setIsDetailModalOpen] = useState(false);
  const [selectedDetailItem, setSelectedDetailItem] = useState(null);
  const [historyDispenseId, setHistoryDispenseId] = useState(null);

  const canWriteMedicationDispense = ability.can('write', 'MedicationDispense');

  const onMedicationDispensesFetched = useCallback(({ data }) => {
    setMedicationDispenses(data);
  }, []);

  const handlePrintLabel = item => {
    const { pharmacyOrderPrescription, quantity, dispensedAt, id, instructions = '' } = item;
    const prescription = pharmacyOrderPrescription?.prescription;
    const patient = pharmacyOrderPrescription?.pharmacyOrder?.encounter?.patient;

    const medication = getDispensedMedication(item);
    const labelItems = [
      {
        id,
        medicationName: getTranslatedMedicationName(medication, getReferenceDataTranslation),
        instructions,
        quantity,
        dispensingUnit: prescription?.dispensingUnit,
        remainingRepeats: pharmacyOrderPrescription?.remainingRepeats,
        prescriberName: prescription?.prescriber?.displayName,
        requestNumber: pharmacyOrderPrescription?.displayId,
        dispensedAt,
      },
    ];

    const labelData = getMedicationLabelData({
      items: labelItems,
      patient,
      facility,
      currentDateTime: getCurrentDateTime(),
    });
    setSelectedLabelData(labelData);
    setPrintModalOpen(true);
  };

  const handleTableRefresh = useCallback(() => {
    setRefreshCount(prev => prev + 1);
  }, []);

  const handleCancelClick = dispense => {
    setSelectedDispense(dispense);
    setIsCancelModalOpen(true);
  };

  const handleCancelConfirm = async () => {
    await api.delete(`medication/medication-dispenses/${selectedDispense.id}`);
    setIsCancelModalOpen(false);
    setSelectedDispense(null);
    // Cancelling puts the request back in the patient's dispensable list, which the dispense
    // modal reads from its own cache.
    await queryClient.invalidateQueries({ queryKey: ['dispensableMedications'] });
    // Trigger table refresh
    handleTableRefresh();
  };

  const handleCancelCancel = () => {
    setIsCancelModalOpen(false);
    setSelectedDispense(null);
  };

  const handleEditClick = dispense => {
    setSelectedDispense(dispense);
    setIsEditModalOpen(true);
  };

  const handleEditCancel = () => {
    setIsEditModalOpen(false);
    setSelectedDispense(null);
  };

  const handleEditConfirm = async () => {
    await queryClient.invalidateQueries({ queryKey: ['dispensableMedications'] });
    // Trigger table refresh
    handleTableRefresh();
  };

  const columns = [
    {
      key: 'pharmacyOrderPrescription.pharmacyOrder.encounter.patient.displayId',
      title: (
        <TranslatedText
          stringId="medication-requests.table.column.patientId"
          fallback="Patient ID"
        />
      ),
      accessor: getPatientDisplayId,
      sortable: true,
    },
    {
      key: 'patientName',
      title: (
        <TranslatedText
          stringId="medication-requests.table.column.patientName"
          fallback="Patient name"
        />
      ),
      accessor: getPatientName,
      sortable: false,
    },
    {
      key: 'pharmacyOrderPrescription.prescription.medication.name',
      title: (
        <TranslatedText
          stringId="medication-requests.table.column.medication"
          fallback="Medication"
        />
      ),
      accessor: getMedication,
      sortable: true,
    },
    {
      key: 'dispensedAt',
      title: (
        <TranslatedText
          stringId="medication-dispenses.table.column.date"
          fallback="Date dispensed"
        />
      ),
      accessor: getDateDispensed,
      sortable: true,
    },
    {
      key: 'quantity',
      title: (
        <TranslatedText
          stringId="medication-dispenses.table.column.quantity"
          fallback="Qty dispensed"
        />
      ),
      accessor: ({ quantity, pharmacyOrderPrescription }) => {
        const dispensingUnit = pharmacyOrderPrescription?.prescription?.dispensingUnit;
        if (!dispensingUnit) return quantity;
        return `${quantity} ${getDrugUnitLabel(dispensingUnit, quantity, getEnumTranslation)}`;
      },
      sortable: false,
    },
    {
      key: 'dispensedById',
      title: (
        <TranslatedText
          stringId="medication-dispenses.table.column.dispensedById"
          fallback="Dispensed by"
        />
      ),
      accessor: getDispensedBy,
      sortable: false,
    },
    {
      key: 'requestNumber',
      title: (
        <TranslatedText
          stringId="medication-dispenses.table.column.requestNumber"
          fallback="Request no."
        />
      ),
      accessor: getRequestNumber,
      sortable: false,
    },
    ...(canWriteMedicationDispense
      ? [
          {
            key: 'actions',
            title: '',
            allowExport: false,
            accessor: row => {
              const actions = [
                {
                  label: (
                    <TranslatedText stringId="general.action.printLabel" fallback="Print label" />
                  ),
                  action: () => handlePrintLabel(row),
                },
                {
                  label: <TranslatedText stringId="general.action.edit" fallback="Edit" />,
                  action: () => handleEditClick(row),
                },
                {
                  label: <TranslatedText stringId="general.action.cancel" fallback="Cancel" />,
                  action: () => handleCancelClick(row),
                },
                isDispenseModifiedByPharmacy(row) && {
                  label: (
                    <TranslatedText
                      stringId="medication.action.viewModifyHistory"
                      fallback="View modify history"
                    />
                  ),
                  action: () => setHistoryDispenseId(row.id),
                },
              ].filter(Boolean);
              return (
                <div onMouseEnter={() => hoveredRow !== row && setHoveredRow(row.id)}>
                  <MenuButton
                    a11yLabel={
                      <TranslatedText
                        stringId="medication-dispenses.table.actions"
                        fallback="Dispense actions"
                      />
                    }
                    actions={actions}
                  />
                </div>
              );
            },
            sortable: false,
            dontCallRowInput: true,
          },
        ]
      : []),
  ];

  const fetchOptions = useMemo(
    () => ({
      ...searchParameters,
      facilityId,
    }),
    [searchParameters, facilityId],
  );

  const handleRowClick = (_, dispenseData) => {
    const patient = dispenseData.pharmacyOrderPrescription?.pharmacyOrder?.encounter?.patient;
    const mappedItem = {
      id: dispenseData.id,
      displayId: dispenseData.pharmacyOrderPrescription?.displayId,
      quantity: dispenseData.quantity,
      instructions: dispenseData.instructions,
      remainingRepeats: dispenseData.pharmacyOrderPrescription?.remainingRepeats,
      dispensedAt: dispenseData.dispensedAt,
      dispensedBy: dispenseData.dispensedBy,
      // The medication actually dispensed, which differs from the prescription's when
      // pharmacy substituted it during dispensing (mirrors the table's Medication column).
      medication: getDispensedMedication(dispenseData),
      // Full prescription so the modal can derive the original Instructions text.
      prescription: dispenseData.pharmacyOrderPrescription?.prescription,
      medicationPresetLabel: dispenseData.medicationPresetLabel,
      patient,
    };
    setSelectedDetailItem(mappedItem);
    setIsDetailModalOpen(true);
  };

  const handleCloseDetailModal = () => {
    setIsDetailModalOpen(false);
    setSelectedDetailItem(null);
  };

  return (
    <>
      <CancelDispensedMedicationModal
        open={isCancelModalOpen}
        onClose={handleCancelCancel}
        onConfirm={handleCancelConfirm}
      />
      <EditMedicationDispenseModal
        open={isEditModalOpen}
        medicationDispense={selectedDispense}
        onClose={handleEditCancel}
        onConfirm={handleEditConfirm}
      />
      <StyledSearchTableWithPermissionCheck
        refreshCount={refreshCount}
        verb="read"
        noun="MedicationDispense"
        autoRefresh={true}
        endpoint="medication/medication-dispenses"
        columns={columns}
        noDataMessage={
          <NoDataContainer>
            <TranslatedText
              stringId="medication-dispenses.list.noData"
              fallback="No dispensed medications to display."
            />
          </NoDataContainer>
        }
        fetchOptions={fetchOptions}
        elevated={false}
        data-testid="searchtablewithpermissioncheck-medication-dispenses"
        $noData={medicationDispenses.length === 0}
        onDataFetched={onMedicationDispensesFetched}
        onClickRow={handleRowClick}
        allowExport={false}
        initialSort={{
          order: 'desc',
          orderBy: 'dispensedAt',
        }}
      />
      {medicationDispenses.some(isDispenseModifiedByPharmacy) && <PharmacyModifiedFootnote />}
      <PrescriptionChangeHistoryModal
        open={Boolean(historyDispenseId)}
        dispenseId={historyDispenseId}
        onClose={() => setHistoryDispenseId(null)}
      />
      <MedicationLabelPrintModal
        open={printModalOpen}
        onClose={() => setPrintModalOpen(false)}
        labels={selectedLabelData}
      />
      <DispensedMedicationDetailsModal
        open={isDetailModalOpen}
        onClose={handleCloseDetailModal}
        item={selectedDetailItem}
      />
    </>
  );
};
