import type { App, InjectionKey, Plugin } from 'vue'
import type {
	DialogApi,
	DialogFormOptions,
	DialogFormResult,
	DialogConfirmOptions,
	DialogOpenOptions,
	DialogRef,
	DialogAction
} from './dialogTypes'
import { createApp, defineComponent, h, inject, reactive } from 'vue'
import Dialog from './Dialog.vue'
import DialogConfirm from './DialogConfirm.vue'
import DialogForm from './DialogForm.vue'

export const DialogSymbol: InjectionKey<DialogApi> = Symbol('dialog')

// Global singleton instance as fallback
let globalDialogApi: DialogApi | null = null

export function useDialog(): DialogApi {
	const dialogApi = inject(DialogSymbol, null)
	if (!dialogApi) {
		if (globalDialogApi) {
			return globalDialogApi
		}
		throw new Error('Dialog API not provided. Make sure DialogPlugin is installed via app.use(DialogPlugin)')
	}
	return dialogApi
}

interface DialogStackItem {
	id: number
	type: 'form' | 'confirm' | 'open'
	options: any
	resolve: (value: any) => void
	open: boolean
}

export const DialogPlugin: Plugin = {
	install: (app: App) => {
		let dialogId = 0
		const dialogStack = reactive<DialogStackItem[]>([])

		const api: DialogApi = {
			form: <T extends Record<string, any>>(options: DialogFormOptions<T>): Promise<DialogFormResult<T>> => {
				return new Promise((resolve) => {
					const id = ++dialogId
					dialogStack.push({
						id,
						type: 'form',
						options,
						resolve,
						open: true
					})
				})
			},

			confirm: (options: string | DialogConfirmOptions): Promise<boolean> => {
				return new Promise((resolve) => {
					const id = ++dialogId
					const normalizedOptions: DialogConfirmOptions = typeof options === 'string'
						? { message: options }
						: options

					dialogStack.push({
						id,
						type: 'confirm',
						options: normalizedOptions,
						resolve,
						open: true
					})
				})
			},

			open: (options: DialogOpenOptions): DialogRef => {
				const id = ++dialogId
				const item: DialogStackItem = {
					id,
					type: 'open',
					options,
					resolve: () => {},
					open: true
				}
				dialogStack.push(item)

				return {
					close: () => {
						const idx = dialogStack.findIndex(d => d.id === id)
						if (idx !== -1) {
							dialogStack[idx].open = false
							// Remove after animation
							setTimeout(() => {
								const removeIdx = dialogStack.findIndex(d => d.id === id)
								if (removeIdx !== -1) {
									dialogStack.splice(removeIdx, 1)
								}
							}, 200)
						}
					}
				}
			}
		}

		// Set global singleton
		if (!globalDialogApi) {
			globalDialogApi = api
		}

		app.provide(DialogSymbol, api)

		// Dialog container component
		const DialogContainerComponent = defineComponent({
			name: 'DialogContainer',
			setup() {
				function handleFormResolve(item: DialogStackItem, result: { action: DialogAction, data: any }) {
					item.resolve(result)
					item.open = false
					// Remove after animation
					setTimeout(() => {
						const idx = dialogStack.findIndex(d => d.id === item.id)
						if (idx !== -1) {
							dialogStack.splice(idx, 1)
						}
					}, 200)
				}

				function handleConfirmResolve(item: DialogStackItem, confirmed: boolean) {
					item.resolve(confirmed)
					item.open = false
					setTimeout(() => {
						const idx = dialogStack.findIndex(d => d.id === item.id)
						if (idx !== -1) {
							dialogStack.splice(idx, 1)
						}
					}, 200)
				}

				function handleOpenUpdate(item: DialogStackItem, open: boolean) {
					if (!open) {
						item.open = false
						setTimeout(() => {
							const idx = dialogStack.findIndex(d => d.id === item.id)
							if (idx !== -1) {
								dialogStack.splice(idx, 1)
							}
						}, 200)
					}
				}

				return {
					dialogStack,
					handleFormResolve,
					handleConfirmResolve,
					handleOpenUpdate
				}
			},
			render() {
				return this.dialogStack.map((item) => {
					if (item.type === 'form') {
						return h(DialogForm, {
							'key': item.id,
							'open': item.open,
							...item.options,
							'onUpdate:open': (open: boolean) => {
								if (!open) {
									this.handleFormResolve(item, { action: 'cancel', data: item.options.modelValue || {} })
								}
							},
							'onResolve': (result: { action: DialogAction, data: any }) => {
								this.handleFormResolve(item, result)
							}
						})
					}

					if (item.type === 'confirm') {
						return h(DialogConfirm, {
							'key': item.id,
							'open': item.open,
							...item.options,
							'onUpdate:open': (open: boolean) => {
								if (!open) {
									this.handleConfirmResolve(item, false)
								}
							},
							'onResolve': (confirmed: boolean) => {
								this.handleConfirmResolve(item, confirmed)
							}
						})
					}

					if (item.type === 'open') {
						const opts = item.options as DialogOpenOptions
						return h(Dialog, {
							'key': item.id,
							'open': item.open,
							'title': opts.title,
							'width': opts.width,
							'position': opts.position,
							'dismissable': opts.dismissable,
							'class': opts.class,
							'onUpdate:open': (open: boolean) => { this.handleOpenUpdate(item, open) }
						}, {
							default: () => h(
								typeof opts.component === 'string' ? opts.component : opts.component,
								opts.props || {}
							)
						})
					}

					return null
				})
			}
		})

		// Register component
		app.component('DialogContainer', DialogContainerComponent)

		// Auto-mount to body
		if (typeof document !== 'undefined' && typeof window !== 'undefined') {
			const mountDialogContainer = () => {
				const existingContainer = document.getElementById('bagelink-dialog-root')
				if (!existingContainer) {
					const container = document.createElement('div')
					container.id = 'bagelink-dialog-root'
					document.body.appendChild(container)

					const dialogApp = createApp(DialogContainerComponent)
					dialogApp._context = app._context
					dialogApp.mount(container)
				}
			}

			if (document.body) {
				mountDialogContainer()
			}

			app.mixin({
				mounted() {
					if (this.$root === this) {
						mountDialogContainer()
					}
				}
			})
		}
	}
}
