import { nextTick } from 'vue';
import type { ExcelStyle, ExcelRow, ExcelFont, ExcelAlignment, ExcelBorder, ExcelBorders, ColDef } from '@ag-grid-community/core';

type ExcelStyle_ = Omit<ExcelStyle, 'id'>;

type ExportStyle = ExcelStyle_ | (() => ExcelStyle_);
interface ExportConfigCellContent {
	value: string | null;
	type?: 'string' | 'number';
	merge?: number;
	style?: ExportStyle;
}
interface ExportConfigCell {
	[key: `cell${number}`]: ExportConfigCellContent;
	style?: ExportStyle;
}
interface ExportConfigLine {
	[key: `line${number}`]: ExportConfigCell;
	style?: ExportStyle;
}
interface ExportConfig {
	top: ExportConfigLine;
	bottom: ExportConfigLine;
}
type ExportTopConfig = ExportConfig['top'];
type ExportBottomConfig = ExportConfig['bottom'];
interface ExportColEvent {
	export: {
		setCellStyle: (style: ExcelStyle_) => void;
		setCellStyleID: (id: string) => void;
		setHeaderStyle: (style: ExcelStyle_) => void;
		setHeaderStyleID: (id: string) => void;
	};
}
type ExportCol = ColDef & ExportColEvent;
export class AgGridCustomExportManager {
	#erFormHelper: any = null;
	#orginStylesTop: ExcelStyle[] = [];
	#orginStylesBottom: ExcelStyle[] = [];
	#orginStylesCustom: ExcelStyle[] = [];
	#soruce: { [key: string]: any }[] = [];
	#fileName = '';
	#gridId = '';
	#exportConfig = {
		sheetName: 'Sheet1',
		columnKeys: [],
		prependContent: {},
		appendContent: {},
	};
	constructor(erFormHelper: any, grid: string, fileName?: string) {
		this.#erFormHelper = erFormHelper;
		this.#gridId = grid;
		fileName && (this.#fileName = fileName);
	}
	#api() {
		const api = this.#erFormHelper?.getGridApi(this.#gridId) ?? null;
		return api;
	}
	#formatParams(type: 'top' | 'bottom', params: ExportConfigLine) {
		let res = [] as any[];
		let styles = deepClone(params.style ?? {});
		Object.entries(params).map(([line, lineCfg]: [string, ExportConfigCell]) => {
			// pos 是从1开始的
			const pos_line = Number.parseInt(line.split('line')[1]);
			// 处理cell
			let cells: any[] = [];
			Object.entries(lineCfg).map(([cell, cellCfg]: [string, ExportConfigCellContent]) => {
				const pos_cell = Number.parseInt(cell.split('cell')[1]);
				// 设置单元格内容数据格式
				const cellContent: { [key: string]: any } = {};
				// 单元格值
				const valType = cellCfg.type === 'number' ? 'Number' : 'String';
				cellContent['data'] = {
					type: valType,
					value: valType === 'String' ? String(cellCfg.value) : Number(cellCfg.value),
				};
				// 设置单元格样式
				if (cellCfg.style) {
					const id = `-auto-style-${type === 'top' ? 'top' : 'bottom'}-cell${pos_cell}`;
					if (typeof cellCfg.style === 'object') {
						styles = deepMerge(styles, cellCfg.style);
					}
					if (typeof cellCfg.style === 'function') {
						styles = deepMerge(styles, cellCfg.style());
					}
					if (type === 'top') {
						this.#orginStylesTop.push({ ...styles, id });
					} else {
						this.#orginStylesBottom.push({ ...styles, id });
					}
					cellContent['styleId'] = id;
				}
				// 横向合并单元格
				if (cellCfg.merge) {
					cellContent['mergeAcross'] = cellCfg.merge - 1;
				}
				cells[pos_cell - 1] = cellContent;
			});
			// 填充undefined
			cells = Array.from(cells).map((v) => (!v ? { cells: [] } : v));
			res[pos_line - 1] = { cells };
		});
		// 填充undefined
		res = Array.from(res).map((v) => (!v ? { cells: [] } : v));
		return res;
	}
	addLineTop(params: ExportTopConfig) {
		this.#exportConfig.prependContent = this.#formatParams('top', params);
	}
	addLineBottom(params: ExportBottomConfig) {
		this.#exportConfig.appendContent = this.#formatParams('bottom', params);
	}
	addStyles(...styles: ExcelStyle[]) {
		const res = { status: 0, msg: '' };
		const ids = this.#orginStylesTop
			.concat(this.#orginStylesBottom)
			.concat(this.#orginStylesCustom)
			.map((style) => style.id);
		styles.map((style) => {
			if (ids.includes(style.id)) {
				res.status--;
				res.msg += `ID[${style.id}]已存在;`;
			} else {
				this.#orginStylesCustom.push(style);
			}
		});
		return res;
	}
	#addCol(col: ColDef) {
		const col_ = col as any;
		col_['export'] = {
			setCellStyleID: (id: string) => {
				col.cellClass = id;
			},
			setHeaderStyleID: (id: string) => {
				col.headerClass = id;
			},
			setHeaderStyle: (style: ExcelStyle) => {
				const id = `-auto-style-head-${col.field}`;
				this.addStyles({ ...style, id });
				col.headerClass = id;
			},
			setCellStyle: (style: ExcelStyle) => {
				const id = `-auto-style-cell-${col.field}`;
				this.addStyles({ ...style, id });
				col.cellClass = id;
			},
		};
		return col_ as ExportCol;
	}
	setColumnStyle(callback: (col: ExportCol) => void) {
		this.#erFormHelper.setGridColumnOptions('exportGrid', (col: ColDef) => {
			callback.call(window, this.#addCol(col));
		});
	}
	createStyles(styles: ExcelStyle_) {
		return styles;
	}
	getStyles() {
		return this.#orginStylesTop.concat(this.#orginStylesBottom).concat(this.#orginStylesCustom);
	}
	setSoruce(source: { [key: string]: any }[]) {
		const api = this.#api();
		if (api) {
			this.#soruce = source;
			return true;
		}
		return false;
	}
	clearSource() {
		const api = this.#api();
		if (api) {
			this.#soruce = [];
			return true;
		}
		return false;
	}
	setFileName(fileName: string) {
		this.#fileName = fileName;
	}

	export(sheetName = 'Sheet1'): Promise<boolean> {
		return new Promise((resolve, reject) => {
			nextTick(async () => {
				const api = this.#api();
				// 清空原本数据源
				api.setRowData([]);
				api.setRowData(this.#soruce);
				this.#exportConfig.sheetName = sheetName;
				this.#exportConfig.columnKeys = this.#erFormHelper.getGridColumnFields(this.#gridId, 'Array');
				const exportData = api?.getSheetDataForExcel(this.#exportConfig);
				if (exportData) {
					api.exportMultipleSheetsAsExcel({
						data: [exportData],
						fileName: `${this.#fileName ?? `export_${new Date().getTime()}`}.xlsx`,
					});
					resolve(true);
				}
				reject(false);
			});
		});
	}
}

export type { ExcelStyle_, ExcelFont, ExcelAlignment, ExcelBorder, ExcelBorders };

function deepMerge(obj1: { [key: string]: any }, obj2?: { [key: string]: any }) {
	if (obj2) {
		const result = deepClone(obj1);
		for (const key in obj2) {
			const value = obj2[key];
			const isNorObj = typeof obj2[key] === 'object' && obj2[key] !== null;
			const isArr = Array.isArray(value);
			const isData = value instanceof Date;
			if (value && isNorObj && !isArr && !isData) {
				result[key] = deepMerge(result[key] || {}, obj2[key]);
			} else {
				result[key] = value;
			}
		}
		return result;
	} else {
		return deepClone(obj1);
	}
}

function deepClone(obj: { [key: string]: any }, hash = new WeakMap()) {
	if (obj === null) return null;
	if (obj instanceof Date) return new Date(obj);
	if (obj instanceof RegExp) return new RegExp(obj);
	if (typeof obj !== 'object') return obj;
	if (hash.has(obj)) return hash.get(obj);
	const cloneObj: { [key: string]: any } = Array.isArray(obj) ? [] : {};
	hash.set(obj, cloneObj);
	for (const key in obj) {
		if (obj.hasOwnProperty(key)) {
			cloneObj[key] = deepClone(obj[key], hash);
		}
	}
	return cloneObj;
}