import { computed, defineComponent, inject, nextTick, onMounted, reactive, ref, watch } from "vue"
import {
	Button,
	Card,
	Checkbox,
	CheckboxGroup,
	Col,
	Divider,
	Dropdown,
	Flex,
	Image,
	Menu,
	MenuItem,
	Modal,
	Row,
	Switch,
	Table,
	Tag,
	Tooltip,
} from "ant-design-vue"
import { cloneDeep, isArray, isBoolean, isEqual, isFunction, isObject, isUndefined } from "lodash-es"
import { DownOutlined, FilePdfOutlined, PictureOutlined, SyncOutlined } from "@ant-design/icons-vue"
import { useCache, useFetch, useProcessStatusSuccess, useSm3 } from "../../hooks"
import { NEWBIE_TABLE } from "../provider/NewbieProvider.jsx"
import NewbieButton from "../button/NewbieButton.jsx"
import NewbieSearch from "../search/NewbieSearch.jsx"

import "./index.less"

/**
 * Newbie 表格组件
 * @version 1.0.0
 */
export default defineComponent({
	name: "NewbieTable",
	props: {
		/**
		 * 表格标题
		 */
		title: { type: String, default: "" },

		/**
		 * 是否使用表单搜索
		 */
		filterable: { type: Boolean, default: true },

		/**
		 *  原生表格属性
		 */
		tableProps: { type: Object, default: () => ({}) },

		/**
		 * 原生表格事件
		 */
		tableEvents: { type: Object, default: () => ({}) },

		/**
		 * 原生 slot
		 */
		tableSlots: { type: Object, default: () => ({}) },

		/**
		 * 是否使用分页，为 Object 时时使用自定义分页
		 */
		pagination: { type: [Boolean, Object], default: true },

		/**
		 * 原生翻页事件
		 */
		pageEvents: { type: Object, default: () => ({}) },

		/**
		 * 是否在搜索条件变化时自动搜索
		 */
		autoQuery: { type: Boolean, default: false },

		/**
		 * 表格数据请求 URL
		 */
		url: { type: String, default: "" },

		/**
		 * 表格请求方式，默认为 GET
		 *
		 * @values get, post
		 */
		method: { type: String, default: "get" },

		/**
		 * 请求数据时额外提交的参数
		 */
		extraData: { type: Object, default: () => ({}) },

		/**
		 * 请求后 Res 的处理方法, 如有 url， 则该方法必须传
		 */
		afterFetched: { type: Function, default: null },

		/**
		 * @typedef {Object} TableColumnConfig
		 * @property {string} title - 列标题
		 * @property {string} dataIndex - 列数据在数据项中对应的 key，支持 a.b.c 的嵌套写法
		 * @property {string} key - Vue 需要的 key，如果已经设置了唯一的 dataIndex，可以忽略这个属性
		 * @property {string} [width] - 列宽度
		 * @property {string} [align] - 对齐方式
		 * @property {boolean} [ellipsis] - 是否自动缩略
		 * @property {boolean} [fixed] - 列是否固定，可选 true(等效于 left) 'left' 'right'
		 * @property {boolean | Object} [filterable] - 是否可过滤，如果是对象，则为过滤配置，为 true 时 `filterable.type = 'input'`
		 * @property {boolean} [isOnlyForQuery] - 是否只用于搜索
		 */

		/**
		 * 表格列定义, [TableColumnConfig ](#tablecolunmconfig-配置)
		 */
		columns: { type: [Array, Function], default: () => [] },

		/**
		 * 选择功能的配置
		 */
		rowSelection: { type: [Boolean, Object], default: () => null },

		/**
		 *  数据值需要指定 key 值，当启用 `rowSelection` 时一定要指定这个 key
		 */
		rowKey: { type: [String, Function], default: "id" },

		/**
		 * 表格数据
		 */
		dataSource: { type: Array, default: () => [] },

		/**
		 * 表格数据, alias of `dataSource`
		 */
		formData: { type: Array, default: () => [] },

		/**
		 * 额外的展开行
		 */
		expandRender: { type: [String, Function], default: null },

		/**
		 * 展开的行
		 */
		expandedRowKeys: { type: Array, default: () => null },

		/**
		 * 是否显示刷新按钮
		 */
		showRefresh: { type: Boolean, default: true },

		/**
		 * 持久化，传入 localStorage 的 key，如果为 true, 将会以 URL Hash 为 key
		 */
		persistence: { type: [Boolean, String], default: false },
	},
	emits: [
		/**
		 * 未传入 `url` 时的手动请求方法
		 */
		"fetch",
	],
	setup(props, { emit, slots, expose }) {
		const searchRef = ref()
		const footerElemRef = ref()

		const tableProvider = inject(NEWBIE_TABLE, () => {})

		const genPersistenceKey = (prefix) => {
			if (!props.persistence) {
				return null
			}
			prefix = prefix || ""
			if (isBoolean(props.persistence)) {
				return `newbieTable_${prefix}` + useSm3(location.href)
			}

			return `newbieTable_${prefix}` + useSm3(location.pathname + "_" + props.persistence)
		}

		let persistencePagination = props.persistence ? useCache(genPersistenceKey()).get({}) : {}

		const state = reactive({
			customColumns: [],
			tableColumns: [],
			filterableColumns: [], // 可过滤的列
			sortableColumns: [], // 可排序的列
			temporary: {}, // 用于存放一些临时数据
			tableLoading: { loading: false }, // 翻页Loading
			customColumnVisible: false,
			columnKeyModal: [],
			pagination: {
				// 翻页数据
				totalSize: 0,
				currentPage: persistencePagination.currentPage || 1,
				pageSize: persistencePagination.pageSize || props.pagination?.pageSize || 10,
			},
			id: "",
			tableSelection: [], // 给外部用
			tableKeySelection: [], // 内部用
			items: [], // 表格内容
			columnKey: {}, // 自定义列
			fetchQueue: [], // 请示队列，以最后那个为准
			searchFormData: {}, // 搜索表单数据
		})

		/**
		 * 持久化翻页与滚动
		 */
		const onPersistence = () => {
			if (!props.persistence) {
				return
			}

			const data = {
				...state.pagination,
			}
			useCache(genPersistenceKey()).set(data)
		}

		const onOpenCustomColumns = () => {
			// 复制一份
			state.columnKeyModal = []
			Object.keys(state.columnKey).forEach((key) => {
				if (state.columnKey[key]) {
					state.columnKeyModal.push(key)
				}
			})
			state.customColumnVisible = true
		}

		const onCustomClearAll = () => {
			state.columnKeyModal = []
		}

		const onCustomSelectAll = () => {
			onCustomClearAll()

			Object.keys(state.columnKey).forEach((key) => {
				state.columnKeyModal.push(key)
			})
		}

		const onSelectionChange = (selectedRowKeys, selectedRows) => {
			state.tableKeySelection = [].concat(selectedRowKeys)
			state.tableSelection = [].concat(selectedRows)
		}

		const onResizeColumn = (w, col) => {
			col.width = w
		}

		const onCustomSubmit = () => {
			Object.keys(state.columnKey).forEach((key) => {
				state.columnKey[key] = state.columnKeyModal.indexOf(key) > -1
			})
			state.customColumnVisible = false
		}

		const getQueryData = () => {
			let params = { ...state.searchFormData, ...props.extraData }
			if (props.pagination) {
				if (state.pagination.pageSize) {
					params[tableProvider.pageSizeKey] = state.pagination.pageSize
				}
				params[tableProvider.pageKey] = state.pagination.currentPage
			}
			return params
		}

		const setQueryData = (fields) => {
			if (searchRef.value) {
				searchRef.value?.setQueryForm(fields)
			}
		}

		const fetchItems = async () => {
			let data = {},
				params = getQueryData()
			const type = new Date().getTime()
			state.fetchQueue.push(type)

			const method = props.method

			if (method === "get") {
				data = { params }
			} else if (method === "post") {
				data = { ...params }
			}

			const res = await useFetch(state.tableLoading)[method](props.url, data)
			useProcessStatusSuccess(res, () => {
				// 根据队列决定是否处理数据
				if (!state.fetchQueue.length) {
					return
				}
				if (state.fetchQueue[state.fetchQueue.length - 1] === type) {
					state.fetchQueue = []
				}
				state.tableKeySelection = []
				state.tableSelection = []
				const fetched = props.afterFetched || tableProvider.afterFetched
				const result = fetched(res)
				if (props.pagination) {
					state.pagination.totalSize = result.totalSize
				}
				state.items = result.items
			})
		}

		/**
		 * 执行获取数据
		 * @param {boolean} refresh 是否刷新
		 * @return {*}
		 */
		const doFetch = async (refresh) => {
			if (refresh === true) {
				state.pagination.currentPage = 1
			}

			if (props.url) {
				await fetchItems()
			} else {
				emit("fetch")
			}

			onPersistence()
		}

		const onSearch = (searchData) => {
			state.searchFormData = searchData
			doFetch(!searchData.persistence)
		}

		const getKey = (item) => item.key || item.dataIndex

		const getMyColumnByRecursion = (columns) => {
			const result = []
			if (columns && columns.length) {
				columns.forEach((it) => {
					const item = cloneDeep(it)
					const key = getKey(item)
					if (key && isUndefined(state.columnKey[key])) {
						state.columnKey[key] = true
					}
					if (!item.children) {
						if ((key && state.columnKey[key]) || !key) {
							result.push({ ...item, resizable: true })
						}
					} else {
						const res = getMyColumnByRecursion([].concat(item.children))
						if (res.length) {
							item.children = res
							if ((key && state.columnKey[key]) || !key) {
								result.push(item)
							}
						}
					}
				})
			}
			return result
		}

		/**
		 * 用于获取自定义列
		 * @param columns
		 * @returns {*[]}
		 */
		const getCustomColumnByRecursion = (columns) => {
			let result = []
			if (columns && columns.length) {
				columns.forEach((column) => {
					const item = cloneDeep(column)
					item.checkKey = getKey(item)
					if (!item.children) {
						result.push(item)
					} else {
						const res = getCustomColumnByRecursion(item.children)
						delete item.children
						result.push(item)
						result = result.concat(res)
					}
				})
			}

			return result
		}

		const totalColumns = computed(() => {
			let columns
			if (isFunction(props.columns)) {
				columns = props.columns()
			} else if (props.columns.length) {
				columns = props.columns
			} else {
				columns = props.tableProps.columns
			}
			return columns
		})

		const prepareFormColumns = () => {
			const originColumns = totalColumns.value.filter((item) => {
				return !item.isOnlyForQuery
			})
			state.customColumns = getCustomColumnByRecursion([].concat(originColumns))
			// 显示列表数组
			state.tableColumns = getMyColumnByRecursion([].concat(originColumns))
		}

		const prepareSearchColumns = () => {
			state.filterableColumns = totalColumns.value
				.filter((item) => {
					return item.filterable || item.isOnlyForQuery
				})
				.map((item) => {
					return {
						title: item.title,
						key: item.dataIndex || item.key,
						options: item.options,
						...item.filterable,
					}
				})

			state.sortableColumns = totalColumns.value
				.filter((item) => item.sortable)
				.map((item) => {
					let column = {
						title: item.title,
						key: item.dataIndex || item.key,
					}

					if (item.sortable === "asc" || item.sortable === "ASC") {
						column.direction = "asc"
					} else if (item.sortable === "desc" || item.sortable === "DESC") {
						column.direction = "desc"
					} else {
						column = { ...column, ...item.sortable }
					}
					return column
				})
		}

		watch(
			() => [totalColumns, state.columnKey],
			(newValues, preValues) => {
				prepareFormColumns()
				//只有 Columns 确实发生变化了才重新渲染 Search,否则会导致 Search 的值丢失
				if (
					!preValues ||
					!isEqual(newValues[0].value.map((item) => getKey(item)).sort(), preValues[0].value.map((item) => getKey(item)).sort())
				) {
					prepareSearchColumns()
				}
			},
			{ immediate: true, deep: true },
		)

		//为了避免出现 Append 在一开始覆盖 Table 内容的情况，在 doFetch 后 Pagination 确定下来再添加兼容样式
		watch(
			() => state.pagination,
			(value) => {
				if (value) {
					footerElemRef.value?.classList.add("pagination-adapt")
				}
			},
			{ deep: true },
		)

		/**
		 * 第一次拿数据
		 */
		const initFetch = () => {
			if (props.url) {
				nextTick(() => fetchItems())
			} else {
				if (props.formData.length) {
					state.items = props.formData
				}

				if (props.dataSource.length) {
					state.items = props.dataSource
				}

				emit("fetch")
			}
		}

		onMounted(() => {
			//1. 因为手动 fetch 需要调用 $ref 的 getQueryForm 等方法，所以需要 mounted 后才执行 fetch
			//2. 当有持久化时需要等待 Search 和 Table 的持久化数据加载完成后再进行数据获取，放在 Search 初始化后中处理
			if (!props.persistence) {
				initFetch()
			}
		})

		/**
		 * 获取当前页的数据
		 * @returns {*[]}
		 */
		const getData = () => {
			return [].concat(state.items)
		}

		/**
		 * 设置数据
		 * @param items
		 */
		const setData = (items) => {
			state.items = items
		}

		/**
		 * 设置翻页数据
		 * @param {int} total
		 * @param {int} currentPage
		 * @param {int} pageSize
		 */
		const setPagination = (total, currentPage, pageSize) => {
			if (!isUndefined(total)) {
				state.pagination.totalSize = total
			}
			if (!isUndefined(currentPage)) {
				state.pagination.currentPage = currentPage
			}
			if (!isUndefined(pageSize)) {
				state.pagination.pageSize = pageSize
			}
		}

		/**
		 * 获取翻页数据
		 * @returns {*}
		 */
		const getPagination = () => state.pagination

		/**
		 * 获取已选择的行数据
		 * @returns {*[]}
		 */
		const getSelection = () => [].concat(state.tableSelection)

		/**
		 * 设置表格 loading 状态
		 * @param {boolean} loading
		 */
		const setTableLoading = (loading) => (state.tableLoading.loading = loading)

		/********** exposes **********/

		expose({
			getData,
			setData,
			setPagination,
			getPagination,
			getSelection,
			getQueryData,
			setQueryData,
			doFetch,
			setTableLoading,
		})

		/********** render **********/

		const titleElem = () => {
			if (slots.title) {
				return <div class={"newbie-table-title-wrapper"}>{slots.title()}</div>
			} else if (props.title) {
				return <div class={"newbie-table-title-wrapper"}>{props.title}</div>
			}
			return null
		}

		const prependElem = () => (slots.prepend ? <div class={"newbie-table-prepend-wrapper"}>{slots.prepend()}</div> : null)

		const filterElem = () =>
			props.filterable
				? [
						<NewbieSearch
							ref={searchRef}
							persistence={props.persistence}
							filterableColumns={state.filterableColumns}
							sortableColumns={state.sortableColumns}
							onSearch={onSearch}
						></NewbieSearch>,
						<Divider></Divider>,
					]
				: null

		const functionalElem = () => (
			<div class={"newbie-table-functional-wrapper"}>
				<div class={"newbie-table-functional"}>{slots.functional ? slots.functional() : null}</div>
				<div class={"newbie-table-functional-default"}>
					{props.filterable ? (
						<Button type={"link"} style={{ marginRight: "5px" }} onClick={onOpenCustomColumns}>
							{() => "自定义列"}
						</Button>
					) : null}
					{props.showRefresh ? (
						<Button class={"newbie-table-refresh"} icon={<SyncOutlined />} onClick={() => doFetch(false)}></Button>
					) : null}
				</div>
			</div>
		)

		const handleTable = () => {
			return {
				bordered: true,
				size: "middle",
				scroll: {
					y: 570,
					x: 1000,
					scrollToFirstRowOnChange: true,
				},
				rowClassName: (_record, index) => (index % 2 === 1 ? "newbie-table-striped" : null),
				expandedRowRender: props.expandRender,
				expandedRowKeys: props.expandedRowKeys,
				rowKey: props.rowKey,
				...props.tableProps,
			}
		}

		const handlePagination = () => {
			let pagination = !props.pagination ? false : isObject(props.pagination) ? cloneDeep(props.pagination) : {}
			if (isObject(pagination)) {
				pagination = {
					size: "small",
					showQuickJumper: true,
					showSizeChanger: true,
					showTotal: (total) => `共${total}条`,
					pageSizeOptions: ["10", "30", "50", "100"],
					total: state.pagination.totalSize,
					pageSize: state.pagination.pageSize,
					current: state.pagination.currentPage,
					"onUpdate:current": (value) => {
						state.pagination.currentPage = value
					},
					"onUpdate:pageSize": (value) => {
						state.pagination.pageSize = value
					},
					onChange: () => {
						doFetch(false)
					},
					showSizeChange: () => {
						doFetch(true)
					},
					...pagination,
				}
			}

			return pagination
		}

		const handleRowSelection = () => {
			let rowSelection = !props.rowSelection ? null : isObject(props.rowSelection) ? cloneDeep(props.rowSelection) : {}

			if (isObject(rowSelection)) {
				rowSelection = {
					selectedRowKeys: state.tableKeySelection,
					preserveSelectedRowKeys: true,
					columnWidth: 40,
					fixed: true,
					onChange: onSelectionChange,
				}
			}

			return rowSelection
		}

		const tableElem = () => {
			return (
				<Table
					rowSelection={handleRowSelection()}
					pagination={handlePagination()}
					loading={state.tableLoading.loading}
					dataSource={state.items}
					columns={state.tableColumns}
					onResizeColumn={onResizeColumn}
					{...handleTable()}
				>
					{{ ...props.tableSlots }}
				</Table>
			)
		}

		const footerElem = () =>
			slots.append || props.pagination ? (
				<div ref={footerElemRef} class={`newbie-table-footer`}>
					<div class={"newbie-table-append-wrapper"}>{slots.append ? slots.append() : null}</div>
					<div class={"newbie-table-pagination-wrapper"}></div>
				</div>
			) : null

		return () => (
			<div class={"newbie-table"}>
				<Card size={"small"} class={"newbie-table-card"}>
					{{
						title: () => titleElem(),
						extra: () => (slots.extra ? slots.extra() : null),
						default: () => [prependElem(), filterElem(), functionalElem(), tableElem(), footerElem()],
					}}
				</Card>
				<Modal v-model:open={state.customColumnVisible} title={"自定义列"} width={"900px"} onOk={onCustomSubmit}>
					{() => [
						<CheckboxGroup v-model:value={state.columnKeyModal} style={{ width: "100%" }}>
							{() => (
								<Row gutter={15}>
									{() =>
										state.customColumns.map((item) => (
											<Col span={6}>
												{() => (
													<Checkbox
														style={{ width: "100%", marginBottom: "5px", overflow: "hidden" }}
														value={item.checkKey}
													>
														{() => item.title}
													</Checkbox>
												)}
											</Col>
										))
									}
								</Row>
							)}
						</CheckboxGroup>,
						<Divider></Divider>,
						<Button onClick={onCustomSelectAll}>{{ default: () => "全选" }}</Button>,
						<Button onClick={onCustomClearAll} style={{ marginLeft: "5px" }}>
							{() => "全不选"}
						</Button>,
					]}
				</Modal>
			</div>
		)
	},
})

/**
 *
 * 生成表格操作按钮
 * @param {array} actions
 *  action :
 *  {
 *      type: 'Button',
 *      name: '点击'
 *      props: {type: 'primary', icon: h(EditOutlined)},
 *      action: function(){},
 *  }
 */
export function useTableActions(actions) {
	if (!actions) return null
	if (!isArray(actions)) {
		actions = [actions]
	}
	const actionElems = actions.map((action) => {
		let props = { ...action.props }

		let { type } = action
		const { name } = action

		let ActionComponent = null

		if (!type || type === "button") {
			ActionComponent = NewbieButton
			props.type = props.type || "text"
		}
		// 专为tag做一些样式处理
		if (type === "a-tag" || type === "tag") {
			ActionComponent = Tag
		}

		if (type === "switch") {
			ActionComponent = Switch
			if (action.name) {
				props.checkedChildren = action.name?.[0] || ""
				props.unCheckedChildren = action.name?.[1] || ""
			}
			props.checked = action.value
			props.disabled = true
		}

		if (action.action) {
			props.onClick = action.action
		}
		if (action.tooltip) {
			const title = action.tooltip
			return (
				<Tooltip title={title} transfer={true}>
					{{
						default: () => <ActionComponent {...props}>{{ default: () => name }}</ActionComponent>,
					}}
				</Tooltip>
			)
		}

		if (action.children?.length) {
			const menuItems = action.children

			return (
				<Dropdown placement={"bottom"}>
					{{
						default: () => (
							<NewbieButton type={"text"} {...props}>
								{{
									default: () => [
										name,
										<DownOutlined
											style={{
												marginLeft: "2px",
												fontSize: "12px",
											}}
										></DownOutlined>,
									],
								}}
							</NewbieButton>
						),
						overlay: () => (
							<Menu>
								{{
									default: () =>
										menuItems.map((item) => (
											<MenuItem>
												{{
													default: () => (
														<NewbieButton
															type={"text"}
															style={{ textAlign: "left" }}
															buttonProps={{ block: true }}
															onClick={item.action}
															{...item.props}
														>
															{{ default: () => item.name }}
														</NewbieButton>
													),
												}}
											</MenuItem>
										)),
								}}
							</Menu>
						),
					}}
				</Dropdown>
			)
		}

		return <ActionComponent {...props}>{{ default: () => name }}</ActionComponent>
	})
	return <div class={"table-actions-wrapper"}>{actionElems}</div>
}

/**
 * 生成表格图片预览
 * @param {string | array} images
 * @param {object} [DefaultIcon]
 * @returns {JSX.Element|unknown[]}
 */
export function useTableImage(images, DefaultIcon) {
	if (!images) {
		return <span style={{ fontSize: "30px" }}>{DefaultIcon ? <DefaultIcon /> : <PictureOutlined />}</span>
	}

	if (!isArray(images)) {
		images = [images]
	}
	return (
		<Flex gap={"small"}>
			{() =>
				images.map((img) => {
					const src = img.thumbUrl || img.url || img
					return <Image src={src} preview={{ src: img.url || img.thumbUrl || img }} width={48} height={48}></Image>
				})
			}
		</Flex>
	)
}

/**
 * 生成附件链接
 * @param {string | array} files
 * @param {object} [options]
 * @returns {JSX.Element|unknown[]}
 */
export function useTableFile(files, options) {
	const { DefaultIcon, placeholder = "暂无附件" } = options || {}
	if (!files) {
		return <span>{placeholder}</span>
	}

	if (!isArray(files)) {
		files = [files]
	}
	return files.map((file, index) => {
		const src = file.url || file
		const name = file.name || (files.length > 1 ? `查看附件${index + 1}` : "查看附件")
		return (
			<a href={src} style={{ display: "block" }} target="_blank">
				<span style={{ fontSize: "14px" }}>
					{DefaultIcon ? <DefaultIcon /> : <FilePdfOutlined />} {name}
				</span>
			</a>
		)
	})
}
