import type { ColorGroup, MosaicRegion, RGBColor } from '@/types/mosaic'
import { averageColors, deltaE76, rgbToLab } from './colors'

export function sortGroupsByLightness(groups: ColorGroup[]): ColorGroup[] {
  return [...groups]
    .sort((first, second) => first.labColor.l - second.labColor.l)
    .map((group, index) => ({ ...group, number: index + 1 }))
}

export function groupRegionsByColor(regions: MosaicRegion[], tolerance: number): ColorGroup[] {
  const groups: ColorGroup[] = []
  const ordered = [...regions].sort((first, second) => first.labColor.l - second.labColor.l)
  for (const region of ordered) {
    const group = groups.find((candidate) => deltaE76(candidate.labColor, region.labColor) <= tolerance)
    if (group) {
      group.regionIds.push(region.id)
      const members = regionColors(group.regionIds, regions)
      group.color = averageColors(members)
      group.labColor = rgbToLab(group.color)
    } else {
      groups.push({
        id: crypto.randomUUID(),
        number: groups.length + 1,
        color: { ...region.color },
        labColor: { ...region.labColor },
        regionIds: [region.id],
      })
    }
  }
  return sortGroupsByLightness(groups)
}

function regionColors(regionIds: string[], regions: MosaicRegion[]): Array<{ color: RGBColor; weight: number }> {
  const idSet = new Set(regionIds)
  return regions
    .filter((region) => idSet.has(region.id))
    .map((region) => ({ color: region.color, weight: region.pixelCount }))
}

export function refreshGroupColors(groups: ColorGroup[], regions: MosaicRegion[]): ColorGroup[] {
  return sortGroupsByLightness(
    groups
      .filter((group) => group.regionIds.length)
      .map((group) => {
        if (group.customColor) return { ...group, regionIds: [...group.regionIds] }
        const color = averageColors(regionColors(group.regionIds, regions))
        return { ...group, color, labColor: rgbToLab(color), regionIds: [...group.regionIds] }
      }),
  )
}
