package com.sjhy.plugin.tool;

import com.intellij.codeInsight.actions.AbstractLayoutCodeProcessor;
import com.intellij.codeInsight.actions.OptimizeImportsProcessor;
import com.intellij.codeInsight.actions.ReformatCodeProcessor;
import com.intellij.openapi.command.WriteCommandAction;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.editor.Document;
import com.intellij.openapi.fileEditor.FileDocumentManager;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.ui.MessageDialogBuilder;
import com.intellij.openapi.ui.Messages;
import com.intellij.openapi.util.Computable;
import com.intellij.openapi.vfs.LocalFileSystem;
import com.intellij.openapi.vfs.VfsUtil;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.psi.PsiDirectory;
import com.intellij.psi.PsiDocumentManager;
import com.intellij.psi.PsiFile;
import com.intellij.psi.PsiManager;
import com.intellij.testFramework.LightVirtualFile;
import com.intellij.util.ExceptionUtil;
import com.sjhy.plugin.constants.MsgValue;
import com.sjhy.plugin.entity.SaveFile;

import java.io.File;
import java.io.IOException;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.util.Collections;
import java.util.List;

/**
 * 文件工具类
 *
 * @author makejava
 * @version 1.0.0
 * @since 2018/07/17 13:10
 */
public class FileUtils {
    private static final Logger LOG = Logger.getInstance(FileUtils.class);
    private static volatile FileUtils fileUtils;

    /**
     * 单例模式
     */
    public static FileUtils getInstance() {
        if (fileUtils == null) {
            synchronized (FileUtils.class) {
                if (fileUtils == null) {
                    fileUtils = new FileUtils();
                }
            }
        }
        return fileUtils;
    }

    private FileUtils() {
    }

    /**
     * 读取文件内容(文本文件)
     *
     * @param project 项目对象
     * @param file    文件对象
     * @return 文件内容
     */
    public String read(Project project, File file) {
        PsiManager psiManager = PsiManager.getInstance(project);
        VirtualFile virtualFile = LocalFileSystem.getInstance().findFileByIoFile(file);
        if (virtualFile == null) {
            return null;
        }
        PsiFile psiFile = psiManager.findFile(virtualFile);
        if (psiFile == null) {
            return null;
        }
        return psiFile.getText();
    }

    /**
     * 写入文件
     *
     * @param saveFile 需要保存的文件对象
     */
    public void write(SaveFile saveFile) {
        // 校验目录是否存在
        PsiManager psiManager = PsiManager.getInstance(saveFile.getProject());
        PsiDirectory psiDirectory;
        VirtualFile directory = LocalFileSystem.getInstance().findFileByPath(saveFile.getPath());
        if (directory == null) {
            // 尝试创建目录
            if (saveFile.isOperateTitle() && !MessageDialogBuilder.yesNo(MsgValue.TITLE_INFO, "Directory " + saveFile.getPath() + " Not Found, Confirm Create?").isYes()) {
                return;
            }
            psiDirectory = WriteCommandAction.runWriteCommandAction(saveFile.getProject(), (Computable<PsiDirectory>) () -> {
                try {
                    VirtualFile dir = VfsUtil.createDirectoryIfMissing(saveFile.getPath());
                    LOG.assertTrue(dir != null);
                    // 重载文件,防止发生IndexNotReadyException异常
                    FileDocumentManager.getInstance().reloadFiles(dir);
                    return psiManager.findDirectory(dir);
                } catch (IOException e) {
                    LOG.error("path " + saveFile.getPath() + " error");
                    ExceptionUtil.rethrow(e);
                    return null;
                }
            });
        } else {
            psiDirectory = psiManager.findDirectory(directory);
        }
        if (psiDirectory == null) {
            return;
        }
        // 保存或替换文件
        PsiFile oldFile = psiDirectory.findFile(saveFile.getFile().getName());
        PsiDocumentManager psiDocumentManager = PsiDocumentManager.getInstance(saveFile.getProject());
        FileDocumentManager fileDocumentManager = FileDocumentManager.getInstance();
        if (saveFile.isOperateTitle() && oldFile != null) {
            MessageDialogBuilder.YesNoCancel yesNoCancel = MessageDialogBuilder.yesNoCancel(MsgValue.TITLE_INFO, "File " + saveFile.getFile().getName() + " Exists, Select Operate Mode?");
            yesNoCancel.yesText("Cover");
            yesNoCancel.noText("Compare");
            yesNoCancel.cancelText("Cancel");
            int result = yesNoCancel.show();
            switch (result) {
                case Messages.YES:
                    break;
                case Messages.NO:
                    // 对比代码时也格式化代码
                    if (saveFile.isReformat()) {
                        // 保留旧文件内容,用新文件覆盖旧文件执行格式化,然后再还原旧文件内容
                        String oldText = oldFile.getText();
                        WriteCommandAction.runWriteCommandAction(saveFile.getProject(), () -> psiDocumentManager.getDocument(oldFile).setText(saveFile.getFile().getText()));
                        // 提交所有改动,并非VCS中的提交文件
                        PsiDocumentManager.getInstance(saveFile.getProject()).commitAllDocuments();
                        reformatFile(saveFile.getProject(), Collections.singletonList(oldFile));
                        // 提交所有改动,并非VCS中的提交文件
                        PsiDocumentManager.getInstance(saveFile.getProject()).commitAllDocuments();
                        String newText = oldFile.getText();
                        WriteCommandAction.runWriteCommandAction(saveFile.getProject(), () -> psiDocumentManager.getDocument(oldFile).setText(oldText));
                        // 提交所有改动,并非VCS中的提交文件
                        PsiDocumentManager.getInstance(saveFile.getProject()).commitAllDocuments();
                        saveFile.setVirtualFile(new LightVirtualFile(saveFile.getFile().getName(), saveFile.getFile().getFileType(), newText));
                    }
                    CompareFileUtils.showCompareWindow(saveFile.getProject(), fileDocumentManager.getFile(psiDocumentManager.getDocument(oldFile)), saveFile.getVirtualFile());
                    return;
                case Messages.CANCEL:
                default:
                    return;
            }
        }
        PsiDirectory finalPsiDirectory = psiDirectory;
        PsiFile finalFile = WriteCommandAction.runWriteCommandAction(saveFile.getProject(), (Computable<PsiFile>) () -> {
            if (oldFile == null) {
                // 提交所有改动,并非VCS中的提交文件
                PsiDocumentManager.getInstance(saveFile.getProject()).commitAllDocuments();
                return (PsiFile) finalPsiDirectory.add(saveFile.getFile());
            } else {
                // 对旧文件进行替换操作
                Document document = psiDocumentManager.getDocument(oldFile);
                LOG.assertTrue(document != null);
                document.setText(saveFile.getFile().getText());
                return oldFile;
            }
        });
        // 判断是否需要进行代码格式化操作
        if (saveFile.isReformat()) {
            reformatFile(saveFile.getProject(), Collections.singletonList(finalFile));
        }
        // 提交所有改动,并非VCS中的提交文件
        PsiDocumentManager.getInstance(saveFile.getProject()).commitAllDocuments();
    }

    /**
     * 执行格式化
     *
     * @param project     项目对象
     * @param psiFileList 文件列表
     */
    @SuppressWarnings("unchecked")
    private void reformatFile(Project project, List<PsiFile> psiFileList) {
        if (CollectionUtil.isEmpty(psiFileList)) {
            return;
        }
        // 提交所有改动,并非VCS中的提交文件
        PsiDocumentManager.getInstance(project).commitAllDocuments();
        // 尝试对文件进行格式化处理
        AbstractLayoutCodeProcessor processor = new ReformatCodeProcessor(project, psiFileList.toArray(new PsiFile[0]), null, false);
        // 优化导入
        processor = new OptimizeImportsProcessor(processor);
        // 重新编排代码(会将代码中的属性与方法的顺序进行重新调整)
//            processor = new RearrangeCodeProcessor(processor);

        // 清理代码,进行旧版本兼容,旧版本的IDEA尚未提供该处理器
        try {
            Class<AbstractLayoutCodeProcessor> codeCleanupCodeProcessorCls = (Class<AbstractLayoutCodeProcessor>) Class.forName("com.intellij.codeInsight.actions.CodeCleanupCodeProcessor");
            Constructor<AbstractLayoutCodeProcessor> constructor = codeCleanupCodeProcessorCls.getConstructor(AbstractLayoutCodeProcessor.class);
            processor = constructor.newInstance(processor);
        } catch (ClassNotFoundException ignored) {
            // 类不存在直接忽略
        } catch (NoSuchMethodException | IllegalAccessException | InstantiationException | InvocationTargetException e) {
            // 抛出未知异常
            ExceptionUtil.rethrow(e);
        }
        // 执行处理
        processor.run();
    }
}