ListCommandHandler.kt

package de.pflugradts.passbird.application.commandhandling.handler
import de.pflugradts.passbird.application.UserInterfaceAdapterPort
import de.pflugradts.passbird.application.commandhandling.command.ListCommand
import de.pflugradts.passbird.domain.model.nest.Nest
import de.pflugradts.passbird.domain.model.shell.Shell
import de.pflugradts.passbird.domain.model.shell.Shell.Companion.shellOf
import de.pflugradts.passbird.domain.model.slot.Slot
import de.pflugradts.passbird.domain.model.transfer.Output.Companion.outputOf
import de.pflugradts.passbird.domain.service.nest.NestService
import de.pflugradts.passbird.domain.service.password.PasswordService
class ListCommandHandler constructor(
    private val nestService: NestService,
    private val passwordService: PasswordService,
    private val userInterfaceAdapterPort: UserInterfaceAdapterPort,
) : TypedCommandHandler<ListCommand>(ListCommand::class.java) {
    override fun handleCommand(command: ListCommand) {
        val output = if (command.showAll) {
            groupByNest(command.argument)
        } else {
            listCurrentNest(command.argument)
        }
        userInterfaceAdapterPort.send(outputOf(output))
        command.invalidateInput()
        userInterfaceAdapterPort.sendLineBreak()
    }
    private fun listCurrentNest(filter: Shell): Shell {
        val eggIds = passwordService.findAllEggIds().toList()
        return try {
            join(filter(eggIds, filter))
        } finally {
            eggIds.scrambleShells()
        }
    }
    private fun join(eggIdShellList: List<Shell>) = if (eggIdShellList.isEmpty()) {
        shellOf("Nest is empty")
    } else {
        shellOf(eggIdShellList.joinToString(", ") { it.asString() })
    }
    private fun groupByNest(filter: Shell): Shell {
        val eggIdShells = mutableListOf<Shell>()
        return try {
            val eggIdsByNest = nestService.all(includeDefault = true)
                .filter { it.isPresent }
                .map { it.get() }
                .map { nest ->
                    val eggIds = passwordService.findAllEggIds(nest.slot).toList()
                    eggIdShells += eggIds
                    nest to filter(eggIds, filter)
                }
                .filter { it.second.isNotEmpty() }
                .toList()
            if (eggIdsByNest.isEmpty()) {
                shellOf("Nest is empty")
            } else {
                shellOf(
                    eggIdsByNest.joinToString("\n") { (nest, eggIds) ->
                        "${nest.label()}\n\t${eggIds.joinToString(", ") { eggId -> eggId.asString() }}"
                    },
                )
            }
        } finally {
            eggIdShells.scrambleShells()
        }
    }
    private fun filter(eggIds: List<Shell>, filter: Shell) = if (filter.isEmpty) {
        eggIds
    } else {
        val searchTerm = filter.asString()
        eggIds.filter { it.asString().contains(searchTerm, ignoreCase = true) }
    }
}
private fun Iterable<Shell>.scrambleShells() = forEach(Shell::scramble)
private fun Nest.label() = if (slot == Slot.DEFAULT) {
    "0: ${viewNestId().asString()}"
} else {
    "${slot.index()}: ${viewNestId().asString()}"
}