package p.packer.services;

import p.packer.messages.assets.OutputCodecCatalog;
import p.packer.messages.assets.OutputFormatCatalog;
import p.packer.messages.assets.PackerCodecConfigurationFieldType;
import p.packer.models.PackerCodecConfigurationField;

import java.util.*;

final class PackerOutputContractCatalog {
    private PackerOutputContractCatalog() {
    }

    static OutputContractDefinition definitionFor(OutputFormatCatalog outputFormat, OutputCodecCatalog selectedCodec) {
        final List<OutputCodecCatalog> availableCodecs = availableCodecsFor(outputFormat, selectedCodec);
        return new OutputContractDefinition(
                availableCodecs,
                availableCodecs.stream().collect(java.util.stream.Collectors.toMap(
                        codec -> codec,
                        codec -> fieldsFor(outputFormat, codec),
                        (left, right) -> left,
                        java.util.LinkedHashMap::new)),
                metadataFieldsFor(outputFormat));
    }

    static List<OutputCodecCatalog> availableCodecsFor(OutputFormatCatalog outputFormat, OutputCodecCatalog selectedCodec) {
        final Set<OutputCodecCatalog> codecs = new LinkedHashSet<>();
        if (outputFormat != OutputFormatCatalog.UNKNOWN) {
            codecs.addAll(outputFormat.availableCodecs());
        } else {
            final String normalizedFormat = outputFormat.manifestValue().trim().toUpperCase(Locale.ROOT);
            if (normalizedFormat.startsWith("GLYPH/")
                    || normalizedFormat.startsWith("PALETTE/")
                    || normalizedFormat.startsWith("SOUND/")
                    || normalizedFormat.startsWith("AUDIO/")) {
                codecs.add(OutputCodecCatalog.NONE);
            }
        }

        final OutputCodecCatalog normalizedSelectedCodec = Objects.requireNonNullElse(selectedCodec, OutputCodecCatalog.UNKNOWN);
        if (normalizedSelectedCodec != OutputCodecCatalog.UNKNOWN) {
            codecs.add(normalizedSelectedCodec);
        }

        if (codecs.isEmpty()) {
            codecs.add(OutputCodecCatalog.NONE);
        }
        return List.copyOf(codecs);
    }

    private static List<PackerCodecConfigurationField> fieldsFor(OutputFormatCatalog outputFormat, OutputCodecCatalog codec) {
        return List.of();
    }

    private static List<PackerCodecConfigurationField> metadataFieldsFor(OutputFormatCatalog outputFormat) {
        return switch (outputFormat) {
            case GLYPH_INDEXED_V1 -> List.of(new PackerCodecConfigurationField(
                    "tile_size",
                    "TileSize",
                    PackerCodecConfigurationFieldType.ENUM,
                    "16x16",
                    true,
                    List.of("8x8", "16x16", "32x32")));
            case SOUND_V1 -> List.of(
                    new PackerCodecConfigurationField(
                            "sample_rate",
                            "Sample Rate",
                            PackerCodecConfigurationFieldType.ENUM,
                            "44100",
                            true,
                            List.of("11025", "22050", "44100", "48000")),
                    new PackerCodecConfigurationField(
                            "channels",
                            "Channels",
                            PackerCodecConfigurationFieldType.ENUM,
                            "1",
                            true,
                            List.of("1", "2")));
            default -> List.of();
        };
    }

    record OutputContractDefinition(
            List<OutputCodecCatalog> availableCodecs,
            Map<OutputCodecCatalog, List<PackerCodecConfigurationField>> codecConfigurationFieldsByCodec,
            List<PackerCodecConfigurationField> metadataFields) {
    }
}
