/**
 * DICOM Processing Module
 * Handles parsing and image conversion of DICOM files for Claude API analysis
 */

/**
 * Process DICOM files from the current series for Claude API analysis
 * @param {Array|Object} input - Either array of docIds OR array of {folderName, docIds} objects
 * @param {Function} onProgress - Progress callback (0-100)
 * @param {number} projectId - Project ID for on-demand file downloads
 * @returns {Promise<Object>} Processed study data ready for Claude API
 */
export async function processDicomSeries(input, onProgress = () => {}, projectId = null) {
  // Detect if input is folder groups or flat docIds array
  const isFolderGroups = Array.isArray(input) && input.length > 0 && input[0].folderName;

  if (isFolderGroups) {
    const totalDocs = input.reduce((sum, g) => sum + g.docIds.length, 0);
    console.log('[DICOM Processor] Processing', input.length, 'folder groups with', totalDocs, 'total DICOM files');
  } else {
    console.log('[DICOM Processor] Processing', input.length, 'DICOM files');
  }

  onProgress(5);

  const dicomFiles = [];

  // Step 1: Fetch all DICOM files, preserving folder metadata
  if (isFolderGroups) {
    let overallProgress = 0;
    const totalDocs = input.reduce((sum, g) => sum + g.docIds.length, 0);

    for (const group of input) {
      for (let i = 0; i < group.docIds.length; i++) {
        try {
          const response = await chrome.runtime.sendMessage({
            type: 'GET_FILE',
            docId: group.docIds[i],
            projectId: projectId  // Include projectId for on-demand downloads
          });

          if (response && response.file && response.file.dataUrl) {
            const arrayBuffer = await dataUrlToArrayBuffer(response.file.dataUrl);
            dicomFiles.push({
              docId: group.docIds[i],
              arrayBuffer: arrayBuffer,
              filename: response.file.filename || `image_${i + 1}.dcm`,
              folderName: group.folderName  // Preserve folder grouping
            });
          } else {
            console.warn(`[DICOM Processor] No file data returned for docId ${group.docIds[i]} in folder ${group.folderName}`);
          }
        } catch (err) {
          console.warn(`[DICOM Processor] Failed to fetch docId ${group.docIds[i]}:`, err);
        }

        overallProgress++;
        onProgress(5 + Math.floor((overallProgress / totalDocs) * 30));
      }
    }
  } else {
    // Legacy flat array handling
    for (let i = 0; i < input.length; i++) {
      try {
        const response = await chrome.runtime.sendMessage({
          type: 'GET_FILE',
          docId: input[i],
          projectId: projectId  // Include projectId for on-demand downloads
        });

        if (response && response.file && response.file.dataUrl) {
          const arrayBuffer = await dataUrlToArrayBuffer(response.file.dataUrl);
          dicomFiles.push({
            docId: input[i],
            arrayBuffer: arrayBuffer,
            filename: response.file.filename || `image_${i + 1}.dcm`
          });
        } else {
          console.warn(`[DICOM Processor] No file data returned for docId ${input[i]}`);
        }
      } catch (err) {
        console.warn(`[DICOM Processor] Failed to fetch docId ${input[i]}:`, err);
      }

      onProgress(5 + Math.floor((i / input.length) * 30));
    }
  }

  console.log('[DICOM Processor] Fetched', dicomFiles.length, 'files');
  onProgress(40);

  // Step 2: Parse and extract metadata (now folder-aware)
  const seriesData = await parseAndGroupDicomFiles(dicomFiles, isFolderGroups);
  onProgress(50);

  // Step 3: Convert all slices to PNG
  const preparedSeries = await convertSeriesToPng(seriesData, (p) => {
    onProgress(50 + Math.floor(p * 0.45));
  });

  onProgress(100);

  return {
    studyMetadata: seriesData.studyMetadata,
    series: preparedSeries,
    totalImages: preparedSeries.reduce((sum, s) => sum + s.images.length, 0)
  };
}

/**
 * Convert data URL to ArrayBuffer
 */
async function dataUrlToArrayBuffer(dataUrl) {
  const base64 = dataUrl.split(',')[1];
  const binaryString = atob(base64);
  const bytes = new Uint8Array(binaryString.length);
  for (let i = 0; i < binaryString.length; i++) {
    bytes[i] = binaryString.charCodeAt(i);
  }
  return bytes.buffer;
}

/**
 * Parse DICOM files and group by series (or folder if preserveFolders is true)
 * @param {Array} dicomFiles - Array of {docId, arrayBuffer, filename, folderName?}
 * @param {boolean} preserveFolders - If true, group by folderName instead of Series UID
 */
async function parseAndGroupDicomFiles(dicomFiles, preserveFolders = false) {
  const seriesMap = new Map();

  console.log('[DICOM Processor] Parsing and grouping', dicomFiles.length, 'DICOM files');
  console.log('[DICOM Processor] Preserve folders mode:', preserveFolders);

  for (const { docId, arrayBuffer, filename, folderName } of dicomFiles) {
    try {
      const byteArray = new Uint8Array(arrayBuffer);
      const dataSet = dicomParser.parseDicom(byteArray);

      const seriesUID = dataSet.string('x0020000e') || 'unknown';

      // Use folder name as grouping key if preserveFolders is true, otherwise use Series UID
      const groupKey = preserveFolders && folderName ? folderName : seriesUID;

      console.log('[DICOM Processor] File:', filename, '- Series UID:', seriesUID, '- Folder:', folderName || 'none', '- Group Key:', groupKey);

      if (!seriesMap.has(groupKey)) {
        console.log('[DICOM Processor] Creating new series for key:', groupKey);

        // Use folder name as description if preserving folders
        const seriesDescription = preserveFolders && folderName
          ? folderName
          : (dataSet.string('x0008103e') || 'Unknown Series');

        seriesMap.set(groupKey, {
          metadata: {
            description: seriesDescription,
            modality: dataSet.string('x00080060') || 'Unknown',
            bodyPart: dataSet.string('x00180015') || 'Not Specified',
            studyDescription: dataSet.string('x00081030') || 'Unknown Study',
            patientPosition: dataSet.string('x00185100') || 'Unknown',
            rows: dataSet.uint16('x00280010'),
            columns: dataSet.uint16('x00280011'),
            sliceThickness: parseFloat(dataSet.string('x00180050')) || null,
            pixelSpacing: dataSet.string('x00280030'),
            windowCenter: parseWindowValue(dataSet.string('x00281050')),
            windowWidth: parseWindowValue(dataSet.string('x00281051')),
            photometricInterpretation: dataSet.string('x00280004'),
            bitsAllocated: dataSet.uint16('x00280100'),
            manufacturer: dataSet.string('x00080070'),
            institutionName: dataSet.string('x00080080'),
            stationName: dataSet.string('x00081010'),
            seriesNumber: dataSet.intString('x00200011'),
            acquisitionDate: dataSet.string('x00080022'),
            seriesDate: dataSet.string('x00080021'),
          },
          slices: []
        });
      }

      seriesMap.get(groupKey).slices.push({
        docId,
        filename,
        arrayBuffer,
        instanceNumber: dataSet.intString('x00200013') || 0,
        sliceLocation: parseFloat(dataSet.string('x00201041')) || null,
        imagePositionPatient: dataSet.string('x00200032'),
      });

    } catch (err) {
      console.warn(`[DICOM Processor] Failed to parse DICOM file ${filename}:`, err);
    }
  }

  // Extract study-level metadata
  const firstSeries = seriesMap.values().next().value;
  const studyMetadata = firstSeries ? {
    studyDescription: firstSeries.metadata.studyDescription,
    modality: firstSeries.metadata.modality,
    bodyPart: firstSeries.metadata.bodyPart,
    institution: firstSeries.metadata.institutionName,
    manufacturer: firstSeries.metadata.manufacturer,
    seriesCount: seriesMap.size,
    totalSlices: Array.from(seriesMap.values()).reduce((sum, s) => sum + s.slices.length, 0)
  } : {};

  console.log('[DICOM Processor] Grouped into', seriesMap.size, 'series');
  for (const [key, series] of seriesMap.entries()) {
    console.log(`[DICOM Processor]   Series "${key}": ${series.slices.length} slices`);
  }

  return {
    studyMetadata,
    seriesMap
  };
}

/**
 * Convert all series to PNG images
 */
async function convertSeriesToPng(seriesData, onProgress) {
  const { seriesMap } = seriesData;
  const preparedSeries = [];
  const seriesEntries = Array.from(seriesMap.entries());

  for (let i = 0; i < seriesEntries.length; i++) {
    const [seriesUID, seriesInfo] = seriesEntries[i];

    // Sort slices by instance number
    seriesInfo.slices.sort((a, b) => a.instanceNumber - b.instanceNumber);

    // Convert ALL slices to PNG
    const images = [];
    for (let j = 0; j < seriesInfo.slices.length; j++) {
      const slice = seriesInfo.slices[j];
      try {
        const pngBase64 = await convertDicomToPng(slice.arrayBuffer);
        images.push({
          sliceNumber: j + 1,
          instanceNumber: slice.instanceNumber,
          sliceLocation: slice.sliceLocation,
          base64: pngBase64,
          filename: slice.filename
        });
      } catch (err) {
        console.warn(`[DICOM Processor] Failed to convert slice ${j + 1}:`, err);
      }
    }

    preparedSeries.push({
      seriesUID,
      ...seriesInfo.metadata,
      totalSlices: seriesInfo.slices.length,
      convertedSlices: images.length,
      images
    });

    console.log(`[DICOM Processor] Series ${i + 1}: Converted ${images.length}/${seriesInfo.slices.length} slices`);

    onProgress(((i + 1) / seriesEntries.length) * 100);
  }

  console.log(`[DICOM Processor] Total images prepared for analysis: ${preparedSeries.reduce((sum, s) => sum + s.images.length, 0)}`);
  return preparedSeries;
}

/**
 * Convert DICOM ArrayBuffer to PNG base64 using Cornerstone
 */
async function convertDicomToPng(arrayBuffer) {
  // Create a File object for Cornerstone
  const blob = new Blob([arrayBuffer], { type: 'application/dicom' });
  const file = new File([blob], 'temp.dcm', { type: 'application/dicom' });

  // Add to Cornerstone file manager
  const imageId = cornerstoneWADOImageLoader.wadouri.fileManager.add(file);

  try {
    // Load the image through cornerstone
    const image = await cornerstone.loadImage(imageId);

    // Calculate dimensions to fit within Claude's 2000px limit for multi-image requests
    const MAX_DIMENSION = 2000;
    let targetWidth = image.width;
    let targetHeight = image.height;

    if (targetWidth > MAX_DIMENSION || targetHeight > MAX_DIMENSION) {
      const scale = Math.min(MAX_DIMENSION / targetWidth, MAX_DIMENSION / targetHeight);
      targetWidth = Math.floor(targetWidth * scale);
      targetHeight = Math.floor(targetHeight * scale);
      console.log(`[DICOM Processor] Resizing image from ${image.width}x${image.height} to ${targetWidth}x${targetHeight}`);
    }

    // Create canvas for rendering with resized dimensions
    const canvas = document.createElement('canvas');
    canvas.width = targetWidth;
    canvas.height = targetHeight;

    // Add canvas to DOM temporarily (required for Cornerstone)
    canvas.style.position = 'absolute';
    canvas.style.left = '-9999px';
    document.body.appendChild(canvas);

    try {
      // Enable the canvas for cornerstone
      cornerstone.enable(canvas);

      // Calculate viewport
      const windowWidth = (image.maxPixelValue !== undefined && image.minPixelValue !== undefined)
        ? image.maxPixelValue - image.minPixelValue
        : 256;
      const windowCenter = (image.maxPixelValue !== undefined && image.minPixelValue !== undefined)
        ? (image.maxPixelValue + image.minPixelValue) / 2
        : 128;

      const viewport = {
        scale: 1.0,
        translation: { x: 0, y: 0 },
        voi: {
          windowWidth: windowWidth,
          windowCenter: windowCenter
        },
        invert: false,
        pixelReplication: false,
        rotation: 0,
        hflip: false,
        vflip: false
      };

      // Display the image
      cornerstone.displayImage(canvas, image, viewport);

      // Wait a bit for rendering
      await new Promise(resolve => setTimeout(resolve, 50));

      // Get the rendered canvas
      const renderedCanvas = cornerstone.getEnabledElement(canvas).canvas;

      // Convert to PNG base64 (without data:image/png;base64, prefix)
      const pngDataUrl = renderedCanvas.toDataURL('image/png');
      const pngBase64 = pngDataUrl.split(',')[1];

      // Clean up
      cornerstone.disable(canvas);
      document.body.removeChild(canvas);

      return pngBase64;

    } catch (err) {
      // Clean up on error
      try {
        cornerstone.disable(canvas);
        document.body.removeChild(canvas);
      } catch (cleanupErr) {
        // Ignore cleanup errors
      }
      throw err;
    }

  } finally {
    // Clean up the file manager
    cornerstoneWADOImageLoader.wadouri.fileManager.remove(imageId);
  }
}

/**
 * Parse window value (handles multi-value DICOM fields)
 */
function parseWindowValue(value) {
  if (!value) return null;
  if (value.includes('\\')) {
    return parseFloat(value.split('\\')[0]);
  }
  return parseFloat(value);
}
