// Copyright 2000-2025 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.

package org.jetbrains.kotlin.idea.base.searching.usages.dialogs;

import com.intellij.find.findUsages.FindClassUsagesDialog;
import com.intellij.find.findUsages.FindUsagesHandler;
import com.intellij.find.findUsages.FindUsagesOptions;
import com.intellij.find.findUsages.JavaClassFindUsagesOptions;
import com.intellij.java.JavaBundle;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.Key;
import com.intellij.psi.PsiClass;
import com.intellij.psi.PsiElement;
import com.intellij.psi.PsiElementFactory;
import com.intellij.psi.PsiModifier;
import com.intellij.ui.SimpleColoredComponent;
import com.intellij.ui.StateRestoringCheckBox;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.kotlin.asJava.LightClassUtilsKt;
import org.jetbrains.kotlin.idea.base.resources.KotlinBundle;
import org.jetbrains.kotlin.idea.base.searching.usages.KotlinClassFindUsagesOptions;
import org.jetbrains.kotlin.idea.search.KotlinSearchUsagesSupport;
import org.jetbrains.kotlin.psi.KtClass;
import org.jetbrains.kotlin.psi.KtClassOrObject;
import org.jetbrains.kotlin.psi.psiUtil.PsiUtilsKt;

import javax.swing.*;

import static org.jetbrains.kotlin.asJava.LightClassUtilsKt.toLightClass;

public class KotlinFindClassUsagesDialog extends FindClassUsagesDialog {
    private StateRestoringCheckBox derivedClasses;
    private StateRestoringCheckBox derivedTraits;
    private StateRestoringCheckBox expectedUsages;

    public KotlinFindClassUsagesDialog(
            KtClassOrObject classOrObject,
            Project project,
            FindUsagesOptions findUsagesOptions,
            boolean toShowInNewTab,
            boolean mustOpenInNewTab,
            boolean isSingleFile,
            FindUsagesHandler handler
    ) {
        super(getRepresentingPsiClass(classOrObject), project, findUsagesOptions, toShowInNewTab, mustOpenInNewTab, isSingleFile, handler);
    }

    private static final Key<KtClassOrObject> ORIGINAL_CLASS = Key.create("ORIGINAL_CLASS");

    private static @NotNull PsiClass getRepresentingPsiClass(@NotNull KtClassOrObject classOrObject) {
        PsiClass lightClass = toLightClass(classOrObject);
        if (lightClass != null) return lightClass;

        // TODO: Remove this code when light classes are generated for builtins
      PsiElementFactory factory = PsiElementFactory.getInstance(classOrObject.getProject());

        String name = classOrObject.getName();
        if (name == null || name.isEmpty()) {
            name = KotlinBundle.message("find.usages.class.name.anonymous");
        }

        PsiClass javaClass;
        if (classOrObject instanceof KtClass klass) {
          javaClass = !klass.isInterface()
                        ? factory.createClass(name)
                        : klass.isAnnotation()
                          ? factory.createAnnotationType(name)
                          : factory.createInterface(name);
        }
        else {
            javaClass = factory.createClass(name);
        }

        //noinspection ConstantConditions
        javaClass.getModifierList().setModifierProperty(
                PsiModifier.FINAL,
                !(classOrObject instanceof KtClass && KotlinSearchUsagesSupport.SearchUtils.isInheritable((KtClass) classOrObject))
        );

        javaClass.putUserData(ORIGINAL_CLASS, classOrObject);

        return javaClass;
    }

    @Override
    protected JPanel createFindWhatPanel() {
        JPanel findWhatPanel = super.createFindWhatPanel();
        assert findWhatPanel != null;

        Utils.renameCheckbox(
                findWhatPanel,
                JavaBundle.message("find.what.methods.usages.checkbox"),
                KotlinBundle.message("find.declaration.functions.usages.checkbox")
        );
        Utils.renameCheckbox(
                findWhatPanel,
                JavaBundle.message("find.what.fields.usages.checkbox"),
                KotlinBundle.message("find.declaration.properties.usages.checkbox")
        );
        Utils.removeCheckbox(findWhatPanel, JavaBundle.message("find.what.implementing.classes.checkbox"));
        Utils.removeCheckbox(findWhatPanel, JavaBundle.message("find.what.derived.interfaces.checkbox"));
        Utils.removeCheckbox(findWhatPanel, JavaBundle.message("find.what.derived.classes.checkbox"));

        derivedClasses = addCheckboxToPanel(
                KotlinBundle.message("find.declaration.derived.classes.checkbox"),
                getFindUsagesOptions().isDerivedClasses,
                findWhatPanel,
                true
        );
        derivedTraits = addCheckboxToPanel(
                KotlinBundle.message("find.declaration.derived.interfaces.checkbox"),
                getFindUsagesOptions().isDerivedInterfaces,
                findWhatPanel,
                true
        );
        return findWhatPanel;
    }

    @Override
    protected @NotNull KotlinClassFindUsagesOptions getFindUsagesOptions() {
        return (KotlinClassFindUsagesOptions) super.getFindUsagesOptions();
    }

    private @Nullable KtClassOrObject getOriginalClass() {
        PsiElement klass = LightClassUtilsKt.getUnwrapped(getPsiElement());
        if (klass == null) {
            return null;
        }

        return klass instanceof KtClassOrObject
               ? (KtClassOrObject) klass
               : klass.getUserData(ORIGINAL_CLASS);
    }

    @Override
    protected void addUsagesOptions(@NotNull JPanel optionsPanel) {
        KtClassOrObject klass = getOriginalClass();
        boolean isActual = klass != null && PsiUtilsKt.hasActualModifier(klass);
        KotlinClassFindUsagesOptions options = getFindUsagesOptions();
        if (isActual) {
            expectedUsages = addCheckboxToPanel(
                    KotlinBundle.message("find.usages.checkbox.name.expected.classes"),
                    options.getSearchExpected(),
                    optionsPanel,
                    false
            );
        }
        addDefaultOptions(optionsPanel);
    }

    @Override
    public void configureLabelComponent(@NotNull SimpleColoredComponent coloredComponent) {
        KtClassOrObject originalClass = getOriginalClass();
        if (originalClass != null) {
            Utils.configureLabelComponent(coloredComponent, originalClass);
        }
    }

    @Override
    protected void update() {
        super.update();

        if (expectedUsages != null) {
            updateStateOnUsagesStateChange(expectedUsages);
        }

        if (!isOKActionEnabled() && (derivedTraits.isSelected() || derivedClasses.isSelected())) {
            setOKActionEnabled(true);
        }
    }

    @Override
    public void calcFindUsagesOptions(JavaClassFindUsagesOptions options) {
        super.calcFindUsagesOptions(options);

        KotlinClassFindUsagesOptions kotlinOptions = (KotlinClassFindUsagesOptions) options;
        kotlinOptions.isDerivedClasses = derivedClasses.isSelected();
        kotlinOptions.isDerivedInterfaces = derivedTraits.isSelected();
        if (expectedUsages != null) {
            kotlinOptions.setSearchExpected(expectedUsages.isSelected());
        }
    }
}
