import { UIInternal } from './ui-internal';
import { Container } from 'aurelia-framework';
import { I18N } from 'aurelia-i18n';
import { ColDef } from 'ag-grid-community';
import { FieldType } from '../model/common-model';
import moment from 'moment';

export class CsvExporterOptions {
    public fieldSeparator: string = ';';
    public filename: string = 'Export';
    public quoteStrings: string = '"';
    public showLabels: boolean = true;
    public headerConfig: string[] | ColDef[] = [];

    public get headers() : string[] {
        return this.headerConfig.map((header: string | ColDef) => 
            isString(header) ? header : header.headerName
        ).filter(h => !!h);
    }

    constructor(options?: Partial<CsvExporterOptions>) {
        Object.assign(this, options);
    }
}

export class CsvExporter<T> {
    private options: CsvExporterOptions;
    private _i18n: I18N;

    constructor(colDefs: ColDef[]);
    constructor(options: CsvExporterOptions);
    constructor(colDefs: ColDef[], options: CsvExporterOptions);
    constructor(colDefsOrOptions: ColDef[] | CsvExporterOptions , options?: CsvExporterOptions) {
        let finalOptions : CsvExporterOptions = null;
        if(colDefsOrOptions instanceof CsvExporterOptions) {
            finalOptions = colDefsOrOptions;
        } else {
            finalOptions = options || new CsvExporterOptions();
            finalOptions.headerConfig = colDefsOrOptions.filter(ColDef => ColDef.field != 'isSelected');
        }
        this.options = finalOptions;
    }

    private get i18n() : I18N {
        if(this._i18n == null) {
            this._i18n = Container.instance.get(I18N);
        }
        return this._i18n;
    }

    public export(data: T[]) {        
        const EOL = "\r\n";
        let csv = "\ufeff"
            + this.options.headers.join(this.options.fieldSeparator) + EOL
            + data.map(entry => this.exportRow(entry)).join(EOL);
        if(csv == '') {
            return;
        }
        
        let fileExtension = '.csv';
        let blob = new Blob([csv], { "type": "text/csv;charset=utf8;" });
        let filename = this.options.filename.replace(/ /g, "_") + fileExtension;
        if ((navigator as any).msSaveBlob) {
            (navigator as any).msSaveBlob(blob, filename);
        } else {
            let link = document.createElement("a");
            link.href = URL.createObjectURL(blob);
            link.setAttribute('visibility', 'hidden');
            link.download = filename;
            document.body.appendChild(link);
            link.click();
            document.body.removeChild(link);
        }
    }

    private exportRow(entry: T) : string {
        let row : string[] = [];
        if(false == isString(this.options.headerConfig[0])) {
            // Use ColDef to parse values
            row = (this.options.headerConfig as ColDef[]).map((colDef: ColDef) : string => {
                if(colDef.valueGetter) {
                    return isString(colDef.valueGetter)
                        ? UIInternal.getValue(entry, colDef.valueGetter)
                        : colDef.valueGetter({
                            colDef,
                            data: entry,
                            getValue: (field: string) => UIInternal.getValue(entry, field),
                            node: null,
                            column: null,
                            api: null,
                            columnApi: null,
                            context: undefined
                        });
                }
                let value : any = UIInternal.getValue(entry, colDef.field);

                if(value == null) {
                    return value;
                }
                if (colDef.type == FieldType.Date) {
                    return moment(value).format(colDef.floatingFilterComponentParams?.dateFormat || 'YYYY-MM-DD');
                } else if (colDef.type == FieldType.DateTime) {
                    return moment(value).format(colDef.floatingFilterComponentParams?.dateFormat || 'YYYY-MM-DD HH:mm:ss');
                } else if (colDef.type == FieldType.Boolean) {
                    if (value) {
                        return this.i18n.tr("general.yes", { ns: "common" });
                    } else {
                        return this.i18n.tr("general.no", { ns: "common" });
                    }
                } else if (colDef.type == FieldType.Number) {
                    return (value as Number).toLocaleString('fr-BE', { useGrouping: false });
                }
                return value;
            });
        } else {
            row = this.options.headers.reduce((row: string[], header: string) => {
                row.push(entry[header] || "");
                return row;
            }, []);
        }
        // Quotes and join
        return row.map((value: string) => {
            value = (value || "").replace(/"/g, '""');
            if (this.options.quoteStrings || value.indexOf(',') > -1 || value.indexOf('\n') > -1 || value.indexOf('\r') > -1) {
                value = this.options.quoteStrings + value + this.options.quoteStrings;
            }
            return value;
        }).join(this.options.fieldSeparator);
    }
}