import type { PartyMember } from '../game'

export type NavigateAction = 'navigateLeft' | 'navigateRight' | 'navigateUp' | 'navigateDown'

type TargetOptions = {
  livingOnly?: boolean
}

function canTarget(member: PartyMember, options: TargetOptions) {
  return !options.livingOnly || member.health > 0
}

export function selectRelativePartyTarget(
  party: PartyMember[],
  selectedId: string,
  direction: -1 | 1,
  options: TargetOptions = {},
) {
  const candidates = party.filter((member) => canTarget(member, options))
  if (candidates.length === 0) return null
  const currentIndex = candidates.findIndex((member) => member.id === selectedId)
  const nextIndex = currentIndex < 0
    ? 0
    : (currentIndex + direction + candidates.length) % candidates.length
  return candidates[nextIndex]?.id ?? null
}

export function selectDirectionalPartyTarget(
  party: PartyMember[],
  selectedId: string,
  action: NavigateAction,
  columns: number,
  options: TargetOptions = {},
) {
  const currentIndex = party.findIndex((member) => member.id === selectedId)
  if (currentIndex < 0) {
    return party.find((member) => canTarget(member, options))?.id ?? null
  }
  const currentRow = Math.floor(currentIndex / columns)
  const currentColumn = currentIndex % columns
  const horizontal = action === 'navigateLeft' || action === 'navigateRight'
  const candidate = party
    .map((member, index) => ({
      member,
      index,
      row: Math.floor(index / columns),
      column: index % columns,
    }))
    .filter(({ member, index, row, column }) => {
      if (!canTarget(member, options) || index === currentIndex) return false
      if (action === 'navigateLeft') return row === currentRow && column < currentColumn
      if (action === 'navigateRight') return row === currentRow && column > currentColumn
      if (action === 'navigateUp') return row < currentRow
      return row > currentRow
    })
    .sort((left, right) => {
      const leftPrimary = horizontal
        ? Math.abs(left.column - currentColumn)
        : Math.abs(left.row - currentRow)
      const rightPrimary = horizontal
        ? Math.abs(right.column - currentColumn)
        : Math.abs(right.row - currentRow)
      const leftSecondary = horizontal ? 0 : Math.abs(left.column - currentColumn)
      const rightSecondary = horizontal ? 0 : Math.abs(right.column - currentColumn)
      return leftPrimary - rightPrimary || leftSecondary - rightSecondary
    })[0]
  return candidate?.member.id ?? null
}

export function selectDirectPartyTarget(
  party: PartyMember[],
  slot: number,
  options: TargetOptions & {
    targetGroup?: number
    groupSize?: number
  } = {},
) {
  const groupSize = options.groupSize ?? 6
  const index = slot + (options.targetGroup ?? 0) * groupSize
  const member = party[index]
  return member && canTarget(member, options) ? member.id : null
}

export function nextTargetGroupSelection(
  party: PartyMember[],
  selectedId: string,
  currentGroup: number,
  groupSize = 6,
) {
  const groupCount = Math.max(1, Math.ceil(party.length / groupSize))
  const nextGroup = (currentGroup + 1) % groupCount
  const selectedIndex = party.findIndex((member) => member.id === selectedId)
  const slot = selectedIndex < 0 ? 0 : selectedIndex % groupSize
  return {
    group: nextGroup,
    selectedId: party[slot + nextGroup * groupSize]?.id ?? null,
  }
}
