// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license. See License.txt in the project root.

package com.microsoft.alm.plugin.idea.tfvc.core.tfs.operations;

import com.google.common.collect.ImmutableList;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.openapi.vfs.newvfs.persistent.PersistentFS;
import com.intellij.psi.PsiDirectory;
import com.intellij.psi.PsiElement;
import com.intellij.psi.PsiFile;
import com.intellij.psi.PsiMethod;
import com.intellij.refactoring.listeners.RefactoringElementListener;
import com.intellij.refactoring.rename.RenameUtil;
import com.intellij.usageView.UsageInfo;
import com.intellij.util.IncorrectOperationException;
import com.microsoft.alm.helpers.Path;
import com.microsoft.alm.plugin.context.ServerContext;
import com.microsoft.alm.plugin.external.models.PendingChange;
import com.microsoft.alm.plugin.external.models.ServerStatusType;
import com.microsoft.alm.plugin.external.utils.CommandUtils;
import com.microsoft.alm.plugin.idea.IdeaAbstractTest;
import com.microsoft.alm.plugin.idea.tfvc.core.TFSVcs;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import org.powermock.api.mockito.PowerMockito;
import org.powermock.core.classloader.annotations.PrepareForTest;
import org.powermock.modules.junit4.PowerMockRunner;

import java.util.Collections;
import java.util.List;

import static org.mockito.Matchers.any;
import static org.mockito.Matchers.anyBoolean;
import static org.mockito.Matchers.eq;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import static org.powermock.api.mockito.PowerMockito.verifyStatic;

@RunWith(PowerMockRunner.class)
@PrepareForTest({CommandUtils.class, TFSVcs.class, PersistentFS.class, RenameUtil.class})
public class RenameFileDirectoryTest extends IdeaAbstractTest {
    private UsageInfo[] usageInfos = new UsageInfo[1];
    private String NEW_FILE_NAME = "newName.txt";
    private String NEW_DIRECTORY_NAME = "newDirectory";
    private String PARENT_PATH = "/path/to/the";
    private String CURRENT_FILE_PATH = Path.combine(PARENT_PATH, "file.txt");
    private String NEW_FILE_PATH = Path.combine(PARENT_PATH, NEW_FILE_NAME);

    @Mock
    private PsiFile mockPsiFile;

    @Mock
    private PsiDirectory mockPsiDirectory;

    @Mock
    private VirtualFile mockVirtualFile;

    @Mock
    private VirtualFile mockVirtualParent;

    @Mock
    private RefactoringElementListener mockListener;

    @Mock
    private TFSVcs mockTFSVcs;

    @Mock
    private Project mockProject;

    @Mock
    private ServerContext mockServerContext;

    @Mock
    private PersistentFS mockPersistentFS;

    @Mock
    private PendingChange mockPendingChange;

    @Before
    public void setUp() {
        MockitoAnnotations.initMocks(this);
        PowerMockito.mockStatic(CommandUtils.class, TFSVcs.class, PersistentFS.class, RenameUtil.class);

        when(mockPsiFile.getVirtualFile()).thenReturn(mockVirtualFile);
        when(mockPsiFile.getProject()).thenReturn(mockProject);
        when(mockPsiDirectory.getVirtualFile()).thenReturn(mockVirtualFile);
        when(mockPsiDirectory.getProject()).thenReturn(mockProject);
        when(mockVirtualParent.getPath()).thenReturn(PARENT_PATH);
        when(mockVirtualFile.getParent()).thenReturn(mockVirtualParent);

        when(mockTFSVcs.getServerContext(anyBoolean())).thenReturn(mockServerContext);
        when(TFSVcs.getInstance(mockProject)).thenReturn(mockTFSVcs);
        when(PersistentFS.getInstance()).thenReturn(mockPersistentFS);
    }

    @Test(expected = IncorrectOperationException.class)
    public void testExecute_BadElement() {
        PsiMethod mockmethod = mock(PsiMethod.class);
        RenameFileDirectory.execute(mockmethod, NEW_FILE_NAME, usageInfos, mockListener);
    }

    @Test
    public void testExecute_RenameFileNoChanges() {
        when(mockVirtualFile.getPath()).thenReturn(CURRENT_FILE_PATH);
        when(CommandUtils.getStatusForFiles(any(Project.class), eq(mockServerContext), eq(ImmutableList.of(CURRENT_FILE_PATH))))
                .thenReturn(Collections.EMPTY_LIST);

        RenameFileDirectory.execute(mockPsiFile, NEW_FILE_NAME, usageInfos, mockListener);

        verifyStatic(times(1));
        CommandUtils.renameFile(eq(mockServerContext), eq(CURRENT_FILE_PATH), eq(NEW_FILE_PATH));
        PersistentFS.getInstance().processEvents(any(List.class));
        verify(mockListener).elementRenamed(mockPsiFile);

        verifyStatic(never());
        RenameUtil.doRenameGenericNamedElement(any(PsiElement.class), any(String.class), any(UsageInfo[].class), any(RefactoringElementListener.class));
    }

    @Test
    public void testExecute_RenameDirectoryNoChanges() {
        String dirName = Path.combine("/path/to/the", "directory");
        when(mockVirtualFile.getPath()).thenReturn(dirName);
        when(CommandUtils.getStatusForFiles(any(Project.class), eq(mockServerContext), eq(ImmutableList.of(dirName))))
                .thenReturn(Collections.EMPTY_LIST);

        RenameFileDirectory.execute(mockPsiFile, NEW_DIRECTORY_NAME, usageInfos, mockListener);

        verifyStatic(times(1));
        CommandUtils.renameFile(eq(mockServerContext), eq(dirName), eq(Path.combine("/path/to/the", "newDirectory")));
        PersistentFS.getInstance().processEvents(any(List.class));
        verify(mockListener).elementRenamed(mockPsiFile);

        verifyStatic(never());
        RenameUtil.doRenameGenericNamedElement(any(PsiElement.class), any(String.class), any(UsageInfo[].class), any(RefactoringElementListener.class));
    }

    @Test
    public void testExecute_RenameFileEditChanges() {
        when(mockPendingChange.getChangeTypes()).thenReturn(ImmutableList.of(ServerStatusType.EDIT));
        when(mockVirtualFile.getPath()).thenReturn(CURRENT_FILE_PATH);
        when(CommandUtils.getStatusForFiles(any(Project.class), eq(mockServerContext), eq(ImmutableList.of(CURRENT_FILE_PATH))))
                .thenReturn(ImmutableList.of(mockPendingChange));

        RenameFileDirectory.execute(mockPsiFile, NEW_FILE_NAME, usageInfos, mockListener);

        verifyStatic(times(1));
        CommandUtils.renameFile(eq(mockServerContext), eq(CURRENT_FILE_PATH), eq(NEW_FILE_PATH));
        PersistentFS.getInstance().processEvents(any(List.class));
        verify(mockListener).elementRenamed(mockPsiFile);

        verifyStatic(never());
        RenameUtil.doRenameGenericNamedElement(any(PsiElement.class), any(String.class), any(UsageInfo[].class), any(RefactoringElementListener.class));
    }

    @Test
    public void testExecute_RenameFileEditRenameChanges() {
        when(mockPendingChange.getChangeTypes()).thenReturn(ImmutableList.of(ServerStatusType.EDIT, ServerStatusType.RENAME));
        when(mockVirtualFile.getPath()).thenReturn(CURRENT_FILE_PATH);
        when(CommandUtils.getStatusForFiles(any(Project.class), eq(mockServerContext), eq(ImmutableList.of(CURRENT_FILE_PATH))))
                .thenReturn(ImmutableList.of(mockPendingChange));

        RenameFileDirectory.execute(mockPsiFile, NEW_FILE_NAME, usageInfos, mockListener);

        verifyStatic(times(1));
        CommandUtils.renameFile(eq(mockServerContext), eq(CURRENT_FILE_PATH), eq(NEW_FILE_PATH));
        PersistentFS.getInstance().processEvents(any(List.class));
        verify(mockListener).elementRenamed(mockPsiFile);

        verifyStatic(never());
        RenameUtil.doRenameGenericNamedElement(any(PsiElement.class), any(String.class), any(UsageInfo[].class), any(RefactoringElementListener.class));
    }

    @Test
    public void testExecute_RenameFileUnversionedChange() {
        when(mockPendingChange.getChangeTypes()).thenReturn(ImmutableList.of(ServerStatusType.ADD));
        when(mockVirtualFile.getPath()).thenReturn(CURRENT_FILE_PATH);
        when(CommandUtils.getStatusForFiles(any(Project.class), eq(mockServerContext), eq(ImmutableList.of(CURRENT_FILE_PATH))))
                .thenReturn(ImmutableList.of(mockPendingChange));

        RenameFileDirectory.execute(mockPsiFile, NEW_FILE_NAME, usageInfos, mockListener);

        verifyStatic(times(1));
        RenameUtil.doRenameGenericNamedElement(mockPsiFile, NEW_FILE_NAME, usageInfos, mockListener);
        verify(mockListener).elementRenamed(mockPsiFile);

        verifyStatic(never());
        CommandUtils.renameFile(any(ServerContext.class), any(String.class), any(String.class));
        PersistentFS.getInstance().processEvents(any(List.class));
    }
}