import { metaData } from '@cornerstonejs/core'
// import { InstanceMetadata } from '@cornerstonejs/calculate-suv'
import cornerstoneDICOMImageLoader from '@cornerstonejs/dicom-image-loader'
import { getPTClinicalDataForInstance } from '@/utils/ptClinicalDataCache'

function parseImageId(imageId) {
  const firstColonIndex = imageId.indexOf(':')
  const urlPart = imageId.substring(firstColonIndex + 1)
  let url = urlPart
  let frame
  const qIndex = urlPart.indexOf('?')

  if (qIndex !== -1) {
    const base = urlPart.slice(0, qIndex)
    const query = urlPart.slice(qIndex + 1)
    const parts = query.split('&').filter(Boolean)
    const preservedParts = []

    parts.forEach((part) => {
      const eqIndex = part.indexOf('=')
      const key = eqIndex === -1 ? part : part.slice(0, eqIndex)
      const value = eqIndex === -1 ? '' : part.slice(eqIndex + 1)

      if (key === 'frame') {
        const n = value !== '' ? parseInt(value, 10) : NaN
        if (Number.isFinite(n)) {
          frame = n
        }
        return
      }

      preservedParts.push(part)
    })

    url = preservedParts.length ? `${base}?${preservedParts.join('&')}` : base
  }

  return {
    scheme: imageId.substr(0, firstColonIndex),
    url,
    frame
  }
}

function getMetaData(type, imageId) {
  // const { dicomParser } = cornerstoneDICOMImageLoader.external
  const parsedImageId = parseImageId(imageId)
  const url = parsedImageId.url
  const dataSet = cornerstoneDICOMImageLoader.wadouri.dataSetCacheManager.get(url)

  if (!dataSet) {
    return
  }

  if (type === 'petImageModule') {
    // 1340137.4196974 240000
    // console.log(dataSet.string('x00541300'), dataSet.string('x00181242'))
    return {
      frameReferenceTime: dataSet.string('x00541300') || '',
      actualFrameDuration: dataSet.string('x00181242')
    }
  }
}

export default function getPTImageIdInstanceMetadata(imageId) {
  const instanceId = getInstanceIdFromImageId(imageId)
  const ptClinicalData = instanceId ? getPTClinicalDataForInstance(instanceId) : null
  const petSequenceModule = metaData.get('petIsotopeModule', imageId)
  const generalSeriesModule = metaData.get('generalSeriesModule', imageId) || {}
  const patientStudyModule = metaData.get('patientStudyModule', imageId) || {}
  const ptSeriesModule = metaData.get('petSeriesModule', imageId) || {}
  const ptImageModule = getMetaData('petImageModule', imageId) || {}
  const radiopharmaceuticalInfo = petSequenceModule?.radiopharmaceuticalInfo

  if (!radiopharmaceuticalInfo) {
    throw new Error('petSequenceModule metadata is required')
  }

  const patientWeight = toNumber(
    ptClinicalData?.PatientWeight,
    patientStudyModule.patientWeight
  )
  const totalDose = toNumber(
    ptClinicalData?.RadionuclideTotalDose,
    radiopharmaceuticalInfo.radionuclideTotalDose
  )
  const halfLife = toNumber(
    ptClinicalData?.RadionuclideHalfLife,
    radiopharmaceuticalInfo.radionuclideHalfLife
  )
  const startTime = firstValue(
    normalizeDicomTime(ptClinicalData?.RadiopharmaceuticalStartTime),
    toDicomTimeString(radiopharmaceuticalInfo.radiopharmaceuticalStartTime)
  )
  const acquisitionTime = firstValue(
    normalizeDicomTime(ptClinicalData?.AcquisitionTime),
    toDicomTimeString(generalSeriesModule.acquisitionTime)
  )

  const instanceMetadata = {
    CorrectedImage: ptSeriesModule.correctedImage,
    Units: ptSeriesModule.units,
    RadionuclideHalfLife: halfLife,
    RadionuclideTotalDose: totalDose,
    DecayCorrection: ptSeriesModule.decayCorrection,
    PatientWeight: patientWeight,
    SeriesDate: toDicomDateString(generalSeriesModule.seriesDate),
    SeriesTime: toDicomTimeString(generalSeriesModule.seriesTime),
    AcquisitionDate: toDicomDateString(generalSeriesModule.acquisitionDate),
    AcquisitionTime: acquisitionTime
  }

  assignIfPresent(
    instanceMetadata,
    'RadiopharmaceuticalStartDateTime',
    toDicomDateString(radiopharmaceuticalInfo.radiopharmaceuticalStartDateTime)
  )
  assignIfPresent(instanceMetadata, 'RadiopharmaceuticalStartTime', startTime)
  assignIfPresent(instanceMetadata, 'FrameReferenceTime', ptImageModule.frameReferenceTime)
  assignIfPresent(instanceMetadata, 'ActualFrameDuration', ptImageModule.actualFrameDuration)
  assignIfPresent(
    instanceMetadata,
    'PatientSex',
    firstValue(ptClinicalData?.PatientSex, patientStudyModule.patientSex)
  )
  assignIfPresent(instanceMetadata, 'PatientSize', patientStudyModule.patientSize)

  return instanceMetadata
}

function convertInterfaceTimeToString(time) {
  const hours = `${time.hours || '00'}`.padStart(2, '0')
  const minutes = `${time.minutes || '00'}`.padStart(2, '0')
  const seconds = `${time.seconds || '00'}`.padStart(2, '0')

  const fractionalSeconds = `${time.fractionalSeconds || '000000'}`.padEnd(
    6,
    '0'
  )

  return `${hours}${minutes}${seconds}.${fractionalSeconds}`
}

function convertInterfaceDateToString(date) {
  const month = `${date.month}`.padStart(2, '0')
  const day = `${date.day}`.padStart(2, '0')
  return `${date.year}${month}${day}`
}

export { getPTImageIdInstanceMetadata }

function getInstanceIdFromImageId(imageId) {
  try {
    const qIndex = imageId.indexOf('?')
    if (qIndex === -1) return null
    const params = new URLSearchParams(imageId.slice(qIndex + 1))
    const instanceId = params.get('instanceId')
    if (!instanceId) return null
    return String(instanceId).trim() || null
  } catch (e) {
    return null
  }
}

function normalizeDicomTime(value) {
  if (!hasValue(value)) return null
  if (typeof value === 'object') {
    return convertInterfaceTimeToString(value)
  }
  const raw = String(value).trim()
  if (!raw) return null

  if (raw.includes(':')) {
    const parts = raw.split(':')
    const hh = `${parts[0] || '00'}`.padStart(2, '0')
    const mm = `${parts[1] || '00'}`.padStart(2, '0')
    const ss = `${parts[2] || '00'}`.padStart(2, '0')
    return `${hh}${mm}${ss}.000000`
  }

  const cleaned = raw.replace(/[^\d.]/g, '')
  if (!cleaned) return null
  const [baseRaw, fracRaw] = cleaned.split('.')
  const base = `${baseRaw || ''}`.padStart(6, '0').slice(-6)
  const frac = `${fracRaw || ''}`.padEnd(6, '0').slice(0, 6)
  return `${base}.${frac}`
}

function hasValue(value) {
  return value !== undefined && value !== null && value !== ''
}

function firstValue(...values) {
  return values.find(hasValue)
}

function toNumber(...values) {
  const value = firstValue(...values)
  if (!hasValue(value)) return value

  const numberValue = Number(value)
  return Number.isFinite(numberValue) ? numberValue : value
}

function toDicomTimeString(value) {
  if (!hasValue(value)) return undefined
  return typeof value === 'string' ? value : convertInterfaceTimeToString(value)
}

function toDicomDateString(value) {
  if (!hasValue(value)) return undefined
  return typeof value === 'string' ? value : convertInterfaceDateToString(value)
}

function assignIfPresent(target, key, value) {
  if (!hasValue(value)) return
  target[key] = value
}
