'use strict';
import { DateStyle } from '../../configuration';
import { GlyphChars } from '../../constants';
import { Container } from '../../container';
import { Strings } from '../../system';
import { GitCommit, GitCommitType } from '../models/commit';
import { GitLogCommit } from '../models/models';
import { Formatter, IFormatOptions } from './formatter';

const emojiMap: { [key: string]: string } = require('../../../emoji/emojis.json');
const emojiRegex = /:([-+_a-z0-9]+):/g;

export interface ICommitFormatOptions extends IFormatOptions {
    dateStyle?: DateStyle;
    truncateMessageAtNewLine?: boolean;

    tokenOptions?: {
        ago?: Strings.ITokenOptions;
        agoOrDate?: Strings.ITokenOptions;
        author?: Strings.ITokenOptions;
        authorAgo?: Strings.ITokenOptions;
        authorAgoOrDate?: Strings.ITokenOptions;
        changes?: Strings.ITokenOptions;
        changesShort?: Strings.ITokenOptions;
        date?: Strings.ITokenOptions;
        id?: Strings.ITokenOptions;
        message?: Strings.ITokenOptions;
    };
}

export class CommitFormatter extends Formatter<GitCommit, ICommitFormatOptions> {
    private get _ago() {
        return this._item.fromNow();
    }

    private get _date() {
        return this._item.formatDate(this._options.dateFormat!);
    }

    private get _agoOrDate() {
        const dateStyle =
            this._options.dateStyle !== undefined ? this._options.dateStyle : Container.config.defaultDateStyle;
        return dateStyle === DateStyle.Absolute ? this._date : this._ago;
    }

    get ago() {
        return this._padOrTruncate(this._ago, this._options.tokenOptions!.ago);
    }

    get agoOrDate() {
        return this._padOrTruncate(this._agoOrDate, this._options.tokenOptions!.agoOrDate);
    }

    get author() {
        const author = this._item.author;
        return this._padOrTruncate(author, this._options.tokenOptions!.author);
    }

    get authorAgo() {
        const authorAgo = `${this._item.author}, ${this._ago}`;
        return this._padOrTruncate(authorAgo, this._options.tokenOptions!.authorAgo);
    }

    get authorAgoOrDate() {
        const authorAgo = `${this._item.author}, ${this._agoOrDate}`;
        return this._padOrTruncate(authorAgo, this._options.tokenOptions!.authorAgoOrDate);
    }

    get changes() {
        if (!(this._item instanceof GitLogCommit) || this._item.type === GitCommitType.File) {
            return this._padOrTruncate('', this._options.tokenOptions!.changes);
        }

        return this._padOrTruncate(this._item.getFormattedDiffStatus(), this._options.tokenOptions!.changes);
    }

    get changesShort() {
        if (!(this._item instanceof GitLogCommit) || this._item.type === GitCommitType.File) {
            return this._padOrTruncate('', this._options.tokenOptions!.changesShort);
        }

        return this._padOrTruncate(
            this._item.getFormattedDiffStatus({ compact: true, separator: '' }),
            this._options.tokenOptions!.changesShort
        );
    }

    get date() {
        return this._padOrTruncate(this._date, this._options.tokenOptions!.date);
    }

    get id() {
        return this._padOrTruncate(this._item.shortSha || '', this._options.tokenOptions!.id);
    }

    get message() {
        let message: string;
        if (this._item.isStagedUncommitted) {
            message = 'Staged changes';
        }
        else if (this._item.isUncommitted) {
            message = 'Uncommitted changes';
        }
        else {
            if (this._options.truncateMessageAtNewLine) {
                const index = this._item.message.indexOf('\n');
                message =
                    index === -1
                        ? this._item.message
                        : `${this._item.message.substring(0, index)}${GlyphChars.Space}${GlyphChars.Ellipsis}`;
            }
            else {
                message = this._item.message;
            }

            message = message.replace(emojiRegex, (s, code) => emojiMap[code] || s);
        }

        return this._padOrTruncate(message, this._options.tokenOptions!.message);
    }

    get sha() {
        return this.id;
    }

    static fromTemplate(template: string, commit: GitCommit, dateFormat: string | null): string;
    static fromTemplate(template: string, commit: GitCommit, options?: ICommitFormatOptions): string;
    static fromTemplate(
        template: string,
        commit: GitCommit,
        dateFormatOrOptions?: string | null | ICommitFormatOptions
    ): string;
    static fromTemplate(
        template: string,
        commit: GitCommit,
        dateFormatOrOptions?: string | null | ICommitFormatOptions
    ): string {
        return super.fromTemplateCore(this, template, commit, dateFormatOrOptions);
    }
}
