// Copyright 2000-2024 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
package com.intellij.psi.util;

import com.intellij.ide.fileTemplates.FileTemplate;
import com.intellij.ide.fileTemplates.FileTemplateManager;
import com.intellij.ide.fileTemplates.JavaCreateFromTemplateHandler;
import com.intellij.ide.highlighter.JavaFileType;
import com.intellij.ide.util.PackageUtil;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.module.Module;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.psi.*;
import com.intellij.psi.codeStyle.JavaCodeStyleManager;
import com.intellij.util.IncorrectOperationException;
import com.intellij.util.text.StringTokenizer;
import org.jetbrains.annotations.NonNls;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;

import java.io.IOException;
import java.util.Properties;

/**
 * author: lesya
 */
public final class CreateClassUtil {
  private static final Logger LOG = Logger.getInstance(CreateClassUtil.class);

  public static final @NonNls String DEFAULT_CLASS_TEMPLATE = "#DEFAULT_CLASS_TEMPLATE";
  private static final @NonNls String DO_NOT_CREATE_CLASS_TEMPLATE = "#DO_NOT_CREATE_CLASS_TEMPLATE";
  private static final @NonNls String CLASS_NAME_PROPERTY = "Class_Name";
  private static final @NonNls String INTERFACE_NAME_PROPERTY = "Interface_Name";

  private CreateClassUtil() {}

  private static @Nullable PsiClass createClassFromTemplate(final @NotNull Properties attributes, final @Nullable String templateName,
                                                            final @NotNull PsiDirectory directoryRoot,
                                                            final @NotNull String className) throws IncorrectOperationException {
    if (templateName == null) return null;
    if (templateName.equals(DO_NOT_CREATE_CLASS_TEMPLATE)) return null;

    final Project project = directoryRoot.getProject();
    try {
      final PsiDirectory directory = createParentDirectories(directoryRoot, className);
      final PsiFile psiFile = directory.findFile(className + "." + JavaFileType.INSTANCE.getDefaultExtension());
      if (psiFile != null) {
        psiFile.delete();
      }

      final String rawClassName = extractClassName(className);
      final PsiFile existing = directory.findFile(rawClassName + ".java");
      if (existing instanceof PsiJavaFile) {
        final PsiClass[] classes = ((PsiJavaFile)existing).getClasses();
        if (classes.length > 0) {
          return classes[0];
        }
        return null;
      }

      final PsiClass aClass;
      if (templateName.equals(DEFAULT_CLASS_TEMPLATE)) {
        aClass = JavaDirectoryService.getInstance().createClass(directory, rawClassName);
      }
      else {
        final FileTemplateManager fileTemplateManager = FileTemplateManager.getInstance(project);
        FileTemplate fileTemplate = fileTemplateManager.getJ2eeTemplate(templateName);
        final String text = fileTemplate.getText(attributes);
        aClass = JavaCreateFromTemplateHandler.createClassOrInterface(project, directory, text, true, fileTemplate.getExtension());
      }
      return (PsiClass)JavaCodeStyleManager.getInstance(project).shortenClassReferences(aClass);
    }
    catch (IOException e) {
      throw new IncorrectOperationException(e);
    }
  }

  private static @NotNull PsiDirectory createParentDirectories(@NotNull PsiDirectory directoryRoot, @NotNull String className) throws IncorrectOperationException {
    final PsiPackage currentPackage = JavaDirectoryService.getInstance().getPackage(directoryRoot);
    final String packagePrefix = currentPackage == null? null : currentPackage.getQualifiedName() + ".";
    final String packageName = extractPackage(packagePrefix != null && className.startsWith(packagePrefix)?
                                              className.substring(packagePrefix.length()) : className);
    final StringTokenizer tokenizer = new StringTokenizer(packageName, ".");
    while (tokenizer.hasMoreTokens()) {
      String packagePart = tokenizer.nextToken();
      PsiDirectory subdirectory = directoryRoot.findSubdirectory(packagePart);
      if (subdirectory == null) {
        directoryRoot.checkCreateSubdirectory(packagePart);
        subdirectory = directoryRoot.createSubdirectory(packagePart);
      }
      directoryRoot = subdirectory;
    }
    return directoryRoot;
  }

  public static String extractClassName(String fqName) {
    return StringUtil.getShortName(fqName);
  }

  public static String extractPackage(String fqName) {
    int i = fqName.lastIndexOf('.');
    return i == -1 ? "" : fqName.substring(0, i);
  }

  public static String makeFQName(String aPackage, String className) {
    String fq = aPackage;
    if (!"".equals(aPackage)) {
      fq += ".";
    }
    fq += className;
    return fq;
  }

  public static @Nullable PsiClass createClassNamed(String newClassName, String templateName, @NotNull PsiDirectory directory) throws IncorrectOperationException {
    return createClassNamed(newClassName, FileTemplateManager.getInstance(directory.getProject()).getDefaultProperties(), templateName, directory);
  }

  public static @Nullable PsiClass createClassWithDefaultProperties(String newClassName, Properties classProperties, String templateName,
                                                                    @NotNull PsiDirectory directory)
    throws IncorrectOperationException {
    Properties defaultProperties = FileTemplateManager.getInstance(directory.getProject()).getDefaultProperties();
    Properties properties = new Properties(defaultProperties);
    properties.putAll(classProperties);

    return createClassNamed(newClassName, properties, templateName, directory);
  }

  private static @Nullable PsiClass createClassNamed(@Nullable String newClassName,
                                                     @NotNull Properties properties,
                                                     String templateName,
                                                     @NotNull PsiDirectory directory) throws IncorrectOperationException {
    if (newClassName == null) {
      return null;
    }
    final String className = extractClassName(newClassName);
    properties.setProperty(CLASS_NAME_PROPERTY, className);
    properties.setProperty(INTERFACE_NAME_PROPERTY, className);

    return createClassFromTemplate(properties, templateName, directory, newClassName);
  }

  public static @Nullable PsiClass createClassFromCustomTemplate(@Nullable PsiDirectory classDirectory,
                                                                 final @Nullable Module module,
                                                                 final String className,
                                                                 final String templateName) {
    if (classDirectory == null && module != null) {
      try {
        classDirectory = PackageUtil.findOrCreateDirectoryForPackage(module, "", null, false);
      }
      catch (IncorrectOperationException e) {
        return null;
      }
    }
    if (classDirectory == null) {
      return null;
    }
    try {
      final Properties properties =                                  FileTemplateManager.getInstance(classDirectory.getProject()).getDefaultProperties();
      return createClassNamed(className, new Properties(properties), templateName, classDirectory);
    }
    catch (IncorrectOperationException e) {
      LOG.error(e);
      return null;
    }
  }
}
