// WITH_STDLIB

// DISABLE_IR_VISIBILITY_CHECKS: ANY
// ^ Some functions, calls to which are emitted by the serialization plugin into user code, have internal visibility.
//   See https://github.com/Kotlin/kotlinx.serialization/issues/2703

//
// NOTE: THIS FILE IS AUTO-GENERATED by the TestMatrixIntegration.kt, DO NOT EDIT!
//

@file:UseSerializers(EnumWithUseSerializer::class, NestedEnumWithUseSerializer::class, )
@file:UseContextualSerialization(EnumWithUseContextual::class, Container.NestedEnumWithUseContextual::class, )

import kotlinx.serialization.*
import kotlinx.serialization.descriptors.*
import kotlinx.serialization.json.*
import kotlinx.serialization.encoding.*
import kotlinx.serialization.modules.*
import kotlin.reflect.typeOf

enum class EnumWithDef {
    A,
    B,
}

@Serializable
enum class Enum {
    A,
    B,
}

@Serializable(EnumWithCustomSerializer::class)
enum class EnumWithCustom {
    A,
    B,
}

@Serializable(EnumWithCustomClSerializer::class)
enum class EnumWithCustomCl {
    A,
    B,
}

enum class EnumWithContextual {
    A,
    B,
}

enum class EnumWithUseContextual {
    A,
    B,
}

enum class EnumWithUse {
    A,
    B,
}

@Serializable
@Extra("Enum1")
enum class Enum1 {
    @Extra("A") A,
    @Extra("B") B,
}

class Container {
    enum class NestedEnumWithDef {
        A,
        B,
    }

    @Serializable
    enum class NestedEnum {
        A,
        B,
    }

    @Serializable(NestedEnumWithCustomSerializer::class)
    enum class NestedEnumWithCustom {
        A,
        B,
    }

    @Serializable(NestedEnumWithCustomClSerializer::class)
    enum class NestedEnumWithCustomCl {
        A,
        B,
    }

    enum class NestedEnumWithContextual {
        A,
        B,
    }

    enum class NestedEnumWithUseContextual {
        A,
        B,
    }

    enum class NestedEnumWithUse {
        A,
        B,
    }

}

class Outer {
}

object EnumWithCustomSerializer: ToDoSerializer<EnumWithCustom>("custom|EnumWithCustom")
object NestedEnumWithCustomSerializer: ToDoSerializer<Container.NestedEnumWithCustom>("custom|Container.NestedEnumWithCustom")
class EnumWithCustomClSerializer: ToDoSerializer<EnumWithCustomCl>("custom|EnumWithCustomCl")
class NestedEnumWithCustomClSerializer: ToDoSerializer<Container.NestedEnumWithCustomCl>("custom|Container.NestedEnumWithCustomCl")

object EnumWithContextualSerializer: ToDoSerializer<EnumWithContextual>("contextual|EnumWithContextual")
object NestedEnumWithContextualSerializer: ToDoSerializer<Container.NestedEnumWithContextual>("contextual|Container.NestedEnumWithContextual")
object EnumWithUseContextualSerializer: ToDoSerializer<EnumWithUseContextual>("contextual|EnumWithUseContextual")
object NestedEnumWithUseContextualSerializer: ToDoSerializer<Container.NestedEnumWithUseContextual>("contextual|Container.NestedEnumWithUseContextual")

class EnumWithUseSerializer: ToDoSerializer<EnumWithUse>("useSerializer|EnumWithUse")
class NestedEnumWithUseSerializer: ToDoSerializer<Container.NestedEnumWithUse>("useSerializer|Container.NestedEnumWithUse")

@kotlinx.serialization.SerialInfo
@Target(AnnotationTarget.CLASS, AnnotationTarget.PROPERTY)
annotation class Extra(val value: String)

fun box(): String {
    val module = SerializersModule {
        contextual(EnumWithContextualSerializer)
        contextual(NestedEnumWithContextualSerializer)
        contextual(EnumWithUseContextualSerializer)
        contextual(NestedEnumWithUseContextualSerializer)
    }
    
    serializer<EnumWithDef>().checkElements("A", "B")
    serializer<Container.NestedEnumWithDef>().checkElements("A", "B")
    serializer<Enum>().checkElements("A", "B")
    serializer<Container.NestedEnum>().checkElements("A", "B")
    serializer<EnumWithCustom>().checkElements("A", "B")
    serializer<Container.NestedEnumWithCustom>().checkElements("A", "B")
    serializer<EnumWithCustomCl>().checkElements("A", "B")
    serializer<Container.NestedEnumWithCustomCl>().checkElements("A", "B")
    serializer<EnumWithContextual>().checkElements("A", "B")
    serializer<Container.NestedEnumWithContextual>().checkElements("A", "B")
    serializer<EnumWithUseContextual>().checkElements("A", "B")
    serializer<Container.NestedEnumWithUseContextual>().checkElements("A", "B")
    serializer<EnumWithUse>().checkElements("A", "B")
    serializer<Container.NestedEnumWithUse>().checkElements("A", "B")
    serializer<Enum1>().checkElements("A", "B")
    
    // Call serializer factory function in companion
    Enum.serializer().checkSerialName("Enum")?.let { return it }
    Container.NestedEnum.serializer().checkSerialName("Container.NestedEnum")?.let { return it }
    EnumWithCustom.serializer().checkSerialName("custom|EnumWithCustom")?.let { return it }
    Container.NestedEnumWithCustom.serializer().checkSerialName("custom|Container.NestedEnumWithCustom")?.let { return it }
    EnumWithCustomCl.serializer().checkSerialName("custom|EnumWithCustomCl")?.let { return it }
    Container.NestedEnumWithCustomCl.serializer().checkSerialName("custom|Container.NestedEnumWithCustomCl")?.let { return it }
    Enum1.serializer().checkSerialName("Enum1")?.let { return it }
    
    // Serializer lookup by generic parameter
    serializer<EnumWithDef>().checkSerialName("EnumWithDef")?.let { return it }
    serializer<Container.NestedEnumWithDef>().checkSerialName("Container.NestedEnumWithDef")?.let { return it }
    serializer<Enum>().checkSerialName("Enum")?.let { return it }
    serializer<Container.NestedEnum>().checkSerialName("Container.NestedEnum")?.let { return it }
    serializer<EnumWithCustom>().checkSerialName("custom|EnumWithCustom")?.let { return it }
    serializer<Container.NestedEnumWithCustom>().checkSerialName("custom|Container.NestedEnumWithCustom")?.let { return it }
    serializer<EnumWithCustomCl>().checkSerialName("custom|EnumWithCustomCl")?.let { return it }
    serializer<Container.NestedEnumWithCustomCl>().checkSerialName("custom|Container.NestedEnumWithCustomCl")?.let { return it }
    // generated serializer used in lookup for the empty module  in case of specifying of @file:UseContextualSerialization or @UseSerializers
    serializer<EnumWithUseContextual>().checkSerialName("EnumWithUseContextual")?.let { return it }
    // generated serializer used in lookup for the empty module  in case of specifying of @file:UseContextualSerialization or @UseSerializers
    serializer<Container.NestedEnumWithUseContextual>().checkSerialName("Container.NestedEnumWithUseContextual")?.let { return it }
    // generated serializer used in lookup for the empty module  in case of specifying of @file:UseContextualSerialization or @UseSerializers
    serializer<EnumWithUse>().checkSerialName("EnumWithUse")?.let { return it }
    // generated serializer used in lookup for the empty module  in case of specifying of @file:UseContextualSerialization or @UseSerializers
    serializer<Container.NestedEnumWithUse>().checkSerialName("Container.NestedEnumWithUse")?.let { return it }
    serializer<Enum1>().checkSerialName("Enum1")?.let { return it }
    
    // Serializer lookup by typeOf function
    serializer(typeOf<EnumWithDef>()).checkSerialName("EnumWithDef")?.let { return it }
    serializer(typeOf<Container.NestedEnumWithDef>()).checkSerialName("Container.NestedEnumWithDef")?.let { return it }
    serializer(typeOf<Enum>()).checkSerialName("Enum")?.let { return it }
    serializer(typeOf<Container.NestedEnum>()).checkSerialName("Container.NestedEnum")?.let { return it }
    serializer(typeOf<EnumWithCustom>()).checkSerialName("custom|EnumWithCustom")?.let { return it }
    serializer(typeOf<Container.NestedEnumWithCustom>()).checkSerialName("custom|Container.NestedEnumWithCustom")?.let { return it }
    serializer(typeOf<EnumWithCustomCl>()).checkSerialName("custom|EnumWithCustomCl")?.let { return it }
    serializer(typeOf<Container.NestedEnumWithCustomCl>()).checkSerialName("custom|Container.NestedEnumWithCustomCl")?.let { return it }
    // generated serializer used in lookup for the empty module  in case of specifying of @file:UseContextualSerialization or @UseSerializers
    serializer(typeOf<EnumWithUseContextual>()).checkSerialName("EnumWithUseContextual")?.let { return it }
    // generated serializer used in lookup for the empty module  in case of specifying of @file:UseContextualSerialization or @UseSerializers
    serializer(typeOf<Container.NestedEnumWithUseContextual>()).checkSerialName("Container.NestedEnumWithUseContextual")?.let { return it }
    // generated serializer used in lookup for the empty module  in case of specifying of @file:UseContextualSerialization or @UseSerializers
    serializer(typeOf<EnumWithUse>()).checkSerialName("EnumWithUse")?.let { return it }
    // generated serializer used in lookup for the empty module  in case of specifying of @file:UseContextualSerialization or @UseSerializers
    serializer(typeOf<Container.NestedEnumWithUse>()).checkSerialName("Container.NestedEnumWithUse")?.let { return it }
    serializer(typeOf<Enum1>()).checkSerialName("Enum1")?.let { return it }
    
    // Serializer lookup by generic parameter in custom module
    module.serializer<EnumWithDef>().checkSerialName("EnumWithDef")?.let { return it }
    module.serializer<Container.NestedEnumWithDef>().checkSerialName("Container.NestedEnumWithDef")?.let { return it }
    module.serializer<Enum>().checkSerialName("Enum")?.let { return it }
    module.serializer<Container.NestedEnum>().checkSerialName("Container.NestedEnum")?.let { return it }
    module.serializer<EnumWithCustom>().checkSerialName("custom|EnumWithCustom")?.let { return it }
    module.serializer<Container.NestedEnumWithCustom>().checkSerialName("custom|Container.NestedEnumWithCustom")?.let { return it }
    module.serializer<EnumWithCustomCl>().checkSerialName("custom|EnumWithCustomCl")?.let { return it }
    module.serializer<Container.NestedEnumWithCustomCl>().checkSerialName("custom|Container.NestedEnumWithCustomCl")?.let { return it }
    // !!! for some reason, the generated serializer is still lookup for the custom module in case of specifying of @file:UseContextualSerialization or @UseSerializers
    module.serializer<EnumWithUseContextual>().checkSerialName("EnumWithUseContextual")?.let { return it }
    // !!! for some reason, the generated serializer is still lookup for the custom module in case of specifying of @file:UseContextualSerialization or @UseSerializers
    module.serializer<Container.NestedEnumWithUseContextual>().checkSerialName("Container.NestedEnumWithUseContextual")?.let { return it }
    // !!! for some reason, the generated serializer is still lookup for the custom module in case of specifying of @file:UseContextualSerialization or @UseSerializers
    module.serializer<EnumWithUse>().checkSerialName("EnumWithUse")?.let { return it }
    // !!! for some reason, the generated serializer is still lookup for the custom module in case of specifying of @file:UseContextualSerialization or @UseSerializers
    module.serializer<Container.NestedEnumWithUse>().checkSerialName("Container.NestedEnumWithUse")?.let { return it }
    module.serializer<Enum1>().checkSerialName("Enum1")?.let { return it }
    
    // Serializer lookup by typeOf function in custom module
    module.serializer(typeOf<EnumWithDef>()).checkSerialName("EnumWithDef")?.let { return it }
    module.serializer(typeOf<Container.NestedEnumWithDef>()).checkSerialName("Container.NestedEnumWithDef")?.let { return it }
    module.serializer(typeOf<Enum>()).checkSerialName("Enum")?.let { return it }
    module.serializer(typeOf<Container.NestedEnum>()).checkSerialName("Container.NestedEnum")?.let { return it }
    module.serializer(typeOf<EnumWithCustom>()).checkSerialName("custom|EnumWithCustom")?.let { return it }
    module.serializer(typeOf<Container.NestedEnumWithCustom>()).checkSerialName("custom|Container.NestedEnumWithCustom")?.let { return it }
    module.serializer(typeOf<EnumWithCustomCl>()).checkSerialName("custom|EnumWithCustomCl")?.let { return it }
    module.serializer(typeOf<Container.NestedEnumWithCustomCl>()).checkSerialName("custom|Container.NestedEnumWithCustomCl")?.let { return it }
    // !!! for some reason, the generated serializer is still lookup for the custom module in case of specifying of @file:UseContextualSerialization or @UseSerializers
    module.serializer(typeOf<EnumWithUseContextual>()).checkSerialName("EnumWithUseContextual")?.let { return it }
    // !!! for some reason, the generated serializer is still lookup for the custom module in case of specifying of @file:UseContextualSerialization or @UseSerializers
    module.serializer(typeOf<Container.NestedEnumWithUseContextual>()).checkSerialName("Container.NestedEnumWithUseContextual")?.let { return it }
    // !!! for some reason, the generated serializer is still lookup for the custom module in case of specifying of @file:UseContextualSerialization or @UseSerializers
    module.serializer(typeOf<EnumWithUse>()).checkSerialName("EnumWithUse")?.let { return it }
    // !!! for some reason, the generated serializer is still lookup for the custom module in case of specifying of @file:UseContextualSerialization or @UseSerializers
    module.serializer(typeOf<Container.NestedEnumWithUse>()).checkSerialName("Container.NestedEnumWithUse")?.let { return it }
    module.serializer(typeOf<Enum1>()).checkSerialName("Enum1")?.let { return it }
    
    // Annotation on type should have value same as a class name
    serializer<Enum1>().checkAnnotation("Enum1")
    
    // Annotation on enum entries should have value same as a entry names
    serializer<Enum1>().checkElementAnnotations("A", "B")
    
    return "OK"
}

fun KSerializer<*>.checkSerialName(name: String): String? = if (descriptor.serialName != name) "Wrong serial name: Expected '$name' actual '${descriptor.serialName}'" else null
fun KSerializer<*>.checkAnnotation(value: String): String? = descriptor.annotations.filterIsInstance<Extra>().single().value.let { if (it != value) it else null }

fun KSerializer<*>.checkElementAnnotations(vararg values: String): String? = (0 ..< descriptor.elementsCount).map { descriptor.getElementAnnotations(it).filterIsInstance<Extra>().single().value}.let { if (it != values.toList()) it.toString() else null }

fun KSerializer<*>.checkElements(vararg values: String): String? = (0 ..< descriptor.elementsCount).map { descriptor.getElementName(it) }.let { if (it != values.toList()) it.toString() else null }


abstract class ToDoSerializer<T: Any>(descriptorName: String): KSerializer<T> {
    override val descriptor: SerialDescriptor = PrimitiveSerialDescriptor(descriptorName, PrimitiveKind.STRING)
    override fun deserialize(decoder: Decoder): T = TODO()
    override fun serialize(encoder: Encoder, value: T) = TODO()
}

