/*
 * Copyright 2023 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package androidx.compose.compiler.plugins.kotlin.k1

import com.intellij.psi.PsiElement
import org.jetbrains.kotlin.descriptors.CallableDescriptor
import org.jetbrains.kotlin.descriptors.DeclarationDescriptor
import org.jetbrains.kotlin.descriptors.ValueParameterDescriptor
import org.jetbrains.kotlin.diagnostics.*
import org.jetbrains.kotlin.diagnostics.PositioningStrategies.DECLARATION_SIGNATURE_OR_DEFAULT
import org.jetbrains.kotlin.psi.KtCallableReferenceExpression
import org.jetbrains.kotlin.psi.KtExpression
import org.jetbrains.kotlin.types.KotlinType

object ComposeErrors {

    // error goes on the composable call in a non-composable function
    @JvmField
    val COMPOSABLE_INVOCATION =
        DiagnosticFactory0.create<PsiElement>(
            Severity.ERROR
        )

    // error goes on the non-composable function with composable calls
    @JvmField
    val COMPOSABLE_EXPECTED =
        DiagnosticFactory0.create<PsiElement>(
            Severity.ERROR
        )

    @JvmField
    val COMPOSABLE_FUNCTION_REFERENCE =
        DiagnosticFactory0.create<KtCallableReferenceExpression>(
            Severity.ERROR
        )

    @JvmField
    val COMPOSABLE_PROPERTY_BACKING_FIELD =
        DiagnosticFactory0.create<PsiElement>(
            Severity.ERROR
        )

    @JvmField
    val COMPOSABLE_VAR =
        DiagnosticFactory0.create<PsiElement>(
            Severity.ERROR
        )

    @JvmField
    val COMPOSABLE_SUSPEND_FUN =
        DiagnosticFactory0.create<PsiElement>(
            Severity.ERROR
        )

    @JvmField
    val ABSTRACT_COMPOSABLE_DEFAULT_PARAMETER_VALUE =
        DiagnosticFactory0.create<PsiElement>(
            Severity.ERROR
        )

    @JvmField
    val COMPOSABLE_FUN_MAIN =
        DiagnosticFactory0.create<PsiElement>(
            Severity.ERROR
        )

    @JvmField
    val CAPTURED_COMPOSABLE_INVOCATION =
        DiagnosticFactory2.create<PsiElement, DeclarationDescriptor, DeclarationDescriptor>(
            Severity.ERROR
        )

    @JvmField
    val MISSING_DISALLOW_COMPOSABLE_CALLS_ANNOTATION =
        DiagnosticFactory3.create<
                PsiElement,
                ValueParameterDescriptor, // unmarked
                ValueParameterDescriptor, // marked
                CallableDescriptor
                >(
            Severity.ERROR
        )

    @JvmField
    val NONREADONLY_CALL_IN_READONLY_COMPOSABLE = DiagnosticFactory0.create<PsiElement>(
        Severity.ERROR
    )

    // This error matches Kotlin's CONFLICTING_OVERLOADS error, except that it renders the
    // annotations with the descriptor. This is important to use for errors where the
    // only difference is whether or not it is annotated with @Composable or not.
    @JvmField
    val CONFLICTING_OVERLOADS: DiagnosticFactory1<PsiElement, Collection<DeclarationDescriptor>> =
        DiagnosticFactory1.create(
            Severity.ERROR,
            DECLARATION_SIGNATURE_OR_DEFAULT
        )

    @JvmField
    val ILLEGAL_TRY_CATCH_AROUND_COMPOSABLE =
        DiagnosticFactory0.create<PsiElement>(
            Severity.ERROR
        )

    // This error matches Kotlin's TYPE_MISMATCH error, except that it renders the annotations
    // with the types. This is important to use for type mismatch errors where the only
    // difference is whether or not it is annotated with @Composable or not.
    @JvmField
    val TYPE_MISMATCH =
        DiagnosticFactory2.create<KtExpression, KotlinType, KotlinType>(
            Severity.ERROR
        )

    @JvmField
    val COMPOSE_APPLIER_CALL_MISMATCH =
        DiagnosticFactory2.create<PsiElement, String, String>(
            Severity.WARNING
        )

    @JvmField
    val COMPOSE_APPLIER_PARAMETER_MISMATCH =
        DiagnosticFactory2.create<PsiElement, String, String>(
            Severity.WARNING
        )

    @JvmField
    val COMPOSE_APPLIER_DECLARATION_MISMATCH =
        DiagnosticFactory0.create<PsiElement>(
            Severity.WARNING
        )

    @JvmField
    val COMPOSE_INVALID_DELEGATE =
        DiagnosticFactory0.create<PsiElement>(
            Severity.ERROR
        )

    @JvmField
    val NAMED_ARGUMENTS_NOT_ALLOWED = DiagnosticFactory0.create<PsiElement>(Severity.WARNING)

    @JvmField
    val MISMATCHED_COMPOSABLE_IN_EXPECT_ACTUAL =
        DiagnosticFactory0.create<PsiElement>(
            Severity.ERROR
        )

    @JvmField
    val REDUNDANT_COMPOSABLE_ANNOTATION = DiagnosticFactory0.create<PsiElement>(Severity.WARNING)

    @JvmField
    val COMPOSABLE_INAPPLICABLE_TYPE = DiagnosticFactory1.create<PsiElement, KotlinType>(Severity.ERROR)

    init {
        Errors.Initializer.initializeFactoryNamesAndDefaultErrorMessages(
            ComposeErrors::class.java,
            ComposeErrorMessages()
        )
    }
}
