'use strict';
import * as path from 'path';
import { commands, Range, TextEditor, Uri, window } from 'vscode';
import { GlyphChars } from '../constants';
import { Container } from '../container';
import { GitBranch, GitLog, GitTag, GitUri } from '../git/gitService';
import { Logger } from '../logger';
import { Messages } from '../messages';
import {
    ChooseFromBranchesAndTagsQuickPickItem,
    CommandQuickPickItem,
    FileHistoryQuickPick,
    ShowCommitsInResultsQuickPickItem
} from '../quickpicks';
import { Iterables, Strings } from '../system';
import { ActiveEditorCachedCommand, CommandContext, Commands, getCommandUri } from './common';
import { ShowQuickCommitFileDetailsCommandArgs } from './showQuickCommitFileDetails';

export interface ShowQuickFileHistoryCommandArgs {
    branchOrTag?: GitBranch | GitTag;
    log?: GitLog;
    maxCount?: number;
    range?: Range;
    showInResults?: boolean;

    goBackCommand?: CommandQuickPickItem;
    nextPageCommand?: CommandQuickPickItem;
}

export class ShowQuickFileHistoryCommand extends ActiveEditorCachedCommand {
    constructor() {
        super([Commands.ShowFileHistoryInResults, Commands.ShowQuickFileHistory]);
    }

    protected async preExecute(context: CommandContext, args: ShowQuickFileHistoryCommandArgs = {}): Promise<any> {
        if (context.command === Commands.ShowFileHistoryInResults) {
            args = { ...args };
            args.showInResults = true;
        }

        return this.execute(context.editor, context.uri, args);
    }

    async execute(editor?: TextEditor, uri?: Uri, args: ShowQuickFileHistoryCommandArgs = {}) {
        uri = getCommandUri(uri, editor);
        if (uri == null) return commands.executeCommand(Commands.ShowQuickCurrentBranchHistory);

        const gitUri = await GitUri.fromUri(uri);

        args = { ...args };

        const placeHolder = `${gitUri.getFormattedPath({
            suffix: args.branchOrTag ? ` (${args.branchOrTag.name})` : undefined
        })}${gitUri.sha ? ` ${Strings.pad(GlyphChars.Dot, 1, 1)} ${gitUri.shortSha}` : ''}`;

        let progressCancellation;
        if (!args.showInResults) {
            progressCancellation = FileHistoryQuickPick.showProgress(placeHolder);
        }

        try {
            if (args.log === undefined) {
                args.log = await Container.git.getLogForFile(gitUri.repoPath, gitUri.fsPath, {
                    maxCount: args.maxCount,
                    range: args.range,
                    ref: (args.branchOrTag && args.branchOrTag.ref) || gitUri.sha
                });
                if (args.log === undefined) {
                    if (args.branchOrTag) {
                        return window.showWarningMessage(`The file could not be found in ${args.branchOrTag.name}`);
                    }
                    return Messages.showFileNotUnderSourceControlWarningMessage('Unable to show file history');
                }
            }

            if (progressCancellation !== undefined && progressCancellation.token.isCancellationRequested) {
                return undefined;
            }

            if (args.showInResults) {
                void (await Container.resultsView.addSearchResults(gitUri.repoPath!, args.log, {
                    label: placeHolder,
                    resultsType: { singular: 'commit', plural: 'commits' }
                }));

                return undefined;
            }

            let previousPageCommand: CommandQuickPickItem | undefined = undefined;

            if (args.log.truncated) {
                const npc = new CommandQuickPickItem(
                    {
                        label: `$(arrow-right) Show Next Commits`,
                        description: `${Strings.pad(GlyphChars.Dash, 2, 3)} shows ${args.log.maxCount} newer commits`
                    },
                    Commands.ShowQuickFileHistory,
                    [gitUri, { ...args, log: undefined } as ShowQuickFileHistoryCommandArgs]
                );

                const last = Iterables.last(args.log.commits.values());
                if (last != null) {
                    previousPageCommand = new CommandQuickPickItem(
                        {
                            label: `$(arrow-left) Show Previous Commits`,
                            description: `${Strings.pad(GlyphChars.Dash, 2, 3)} shows ${
                                args.log.maxCount
                            } older commits`
                        },
                        Commands.ShowQuickFileHistory,
                        [
                            new GitUri(uri, last),
                            { ...args, log: undefined, nextPageCommand: npc } as ShowQuickFileHistoryCommandArgs
                        ]
                    );
                }
            }

            // Create a command to get back to where we are right now
            const currentCommand = new CommandQuickPickItem(
                {
                    label: `go back ${GlyphChars.ArrowBack}`,
                    description: `${Strings.pad(GlyphChars.Dash, 2, 3)} to history of ${
                        GlyphChars.Space
                    }$(file-text) ${path.basename(gitUri.fsPath)}${
                        args.branchOrTag
                            ? ` from ${GlyphChars.Space}${
                                  args.branchOrTag instanceof GitTag ? '$(tag)' : '$(git-branch)'
                              } ${args.branchOrTag.name}`
                            : gitUri.sha
                                ? ` from ${GlyphChars.Space}$(git-commit) ${gitUri.shortSha}`
                                : ''
                    }`
                },
                Commands.ShowQuickFileHistory,
                [uri, args]
            );

            const pick = await FileHistoryQuickPick.show(args.log, gitUri, placeHolder, {
                progressCancellation: progressCancellation,
                currentCommand: currentCommand,
                goBackCommand: args.goBackCommand,
                nextPageCommand: args.nextPageCommand,
                previousPageCommand: previousPageCommand,
                showAllCommand:
                    args.log !== undefined && args.log.truncated
                        ? new CommandQuickPickItem(
                              {
                                  label: `$(sync) Show All Commits`,
                                  description: `${Strings.pad(GlyphChars.Dash, 2, 3)} this may take a while`
                              },
                              Commands.ShowQuickFileHistory,
                              [uri, { ...args, log: undefined, maxCount: 0 }]
                          )
                        : undefined,
                showInResultsCommand:
                    args.log !== undefined
                        ? new ShowCommitsInResultsQuickPickItem(args.log, {
                              label: placeHolder,
                              resultsType: { singular: 'commit', plural: 'commits' }
                          })
                        : undefined
            });
            if (pick === undefined) return undefined;

            if (pick instanceof ChooseFromBranchesAndTagsQuickPickItem) {
                const branchOrTag = await pick.execute();
                if (branchOrTag === undefined) return undefined;
                if (branchOrTag instanceof CommandQuickPickItem) return branchOrTag.execute();

                return commands.executeCommand(Commands.ShowQuickFileHistory, gitUri, {
                    ...args,
                    log: undefined,
                    branchOrTag: branchOrTag.branchOrTag,
                    goBackCommand: currentCommand
                } as ShowQuickFileHistoryCommandArgs);
            }
            else {
                if (pick instanceof CommandQuickPickItem) return pick.execute();
            }

            return commands.executeCommand(Commands.ShowQuickCommitFileDetails, pick.commit.toGitUri(), {
                commit: pick.commit,
                fileLog: args.log,
                sha: pick.commit.sha,
                goBackCommand: currentCommand
            } as ShowQuickCommitFileDetailsCommandArgs);
        }
        catch (ex) {
            Logger.error(ex, 'ShowQuickFileHistoryCommand');
            return Messages.showGenericErrorMessage('Unable to show file history');
        }
        finally {
            progressCancellation && progressCancellation.cancel();
        }
    }
}
