import type { App, InjectionKey, Plugin } from 'vue'
import type { ComponentProps } from 'vue-component-type-helpers'
import type {
	ModalComponentProps,
	ModalFormComponentProps,
	ModalFormOptions,
	ModalOptions,
	ModalType,
	ConfirmModalUserOptions,
	ModalConfirmOptions
} from './modalTypes'
import { createApp, defineComponent, h, inject, reactive } from 'vue'
import { Modal, ModalConfirm, ModalForm } from '../components'

export interface ModalApi {
	showModal: <T extends { [key: string]: any }>(
		options: ModalOptions,
		slots?: { [key: string]: any }
	) => ModalComponentProps<T> | undefined
	showModalForm: <T extends { [key: string]: any }>(
		options: ModalFormOptions<T>,
		slots?: { [key: string]: any }
	) => ModalFormComponentProps<T> | undefined
	hideModal: (index?: number) => void
	confirmModal: (options: ConfirmModalUserOptions) => Promise<boolean>
	hideAllModals: () => void
}

export const ModalSymbol: InjectionKey<ModalApi> = Symbol('modal')

// Global singleton instance as fallback (created on first plugin install)
let globalModalApi: ModalApi | null = null

export function useModal(): ModalApi {
	const modalApi = inject(ModalSymbol, null)
	if (!modalApi) {
		// Fallback to global singleton if injection fails
		if (globalModalApi) {
			return globalModalApi
		}
		throw new Error('Modal API not provided. Make sure ModalPlugin is installed via app.use(ModalPlugin)')
	}
	return modalApi
}

export const ModalPlugin: Plugin = {
	install: (app: App) => {
		const BASE_Z_INDEX = 1000
		const modalStack = reactive<ModalComponentProps<object>[]>([])

		const hideModal = (index?: number) => {
			if (index === undefined || index >= modalStack.length - 1) {
				// Close top modal
				modalStack.splice(modalStack.length - 1, 1)
			} else {
				modalStack.splice(index, 1)
			}
		}

		const hideAllModals = () => {
			modalStack.splice(0, modalStack.length)
		}

		const confirmModal = (options: ConfirmModalUserOptions): Promise<boolean> => {
			return new Promise((resolve) => {
				const confirmOptions = typeof options === 'string' ? { title: '', message: options } : options
				modalStack.push({
					modalOptions: reactive({ ...confirmOptions, resolve }),
					modalType: 'confirmModal',
					componentSlots: {},
				})
			})
		}

		const showModal = <T extends { [key: string]: any }>(
			modalType: ModalType,
			options: ModalOptions | ModalFormOptions<T>,
			slots: { [key: string]: any } = {}
		): ModalComponentProps<T> | ModalFormComponentProps<T> | undefined => {
			// Make options reactive so updates propagate
			const reactiveOptions = reactive(options) as ModalOptions | ModalFormOptions<T>

			// Ensure visible is set for modals
			if (!('visible' in reactiveOptions)) {
				reactiveOptions.visible = true
			}

			const modalComponent = {
				modalOptions: reactiveOptions,
				modalType,
				componentSlots: slots,
			}
			modalStack.push(modalComponent)

			if (modalType === 'modalForm') {
				return modalComponent as ModalFormComponentProps<T>
			}
			return modalComponent
		}

		const api: ModalApi = {
			showModal: (options: ModalOptions, slots?: { [key: string]: any }) => showModal('modal', options, slots),

			showModalForm: <T extends { [key: string]: any }>(
				options: ModalFormOptions<T>,
				slots?: { [key: string]: any }
			) => showModal<T>('modalForm', options, slots) as ModalFormComponentProps<T>,

			confirmModal: (options: ConfirmModalUserOptions) => confirmModal(options),

			hideModal,

			hideAllModals,
		}

		// Set global singleton on first install
		if (!globalModalApi) {
			globalModalApi = api
		}

		app.provide(ModalSymbol, api)

		// Modal container component
		const ModalContainerComponent = defineComponent({
			name: 'ModalContainer',
			setup() {
				// Handle ESC key - only close top modal
				const handleEscape = (e: KeyboardEvent) => {
					if (e.key === 'Escape' && modalStack.length > 0) {
						const topModal = modalStack[modalStack.length - 1]
						// Check if top modal is dismissable
						const modalOptions = topModal.modalOptions as ModalOptions
						if (modalOptions.dismissable !== false) {
							hideModal()
						}
					}
				}

				return {
					modalStack,
					handleEscape,
				}
			},
			mounted() {
				document.addEventListener('keydown', this.handleEscape)
			},
			unmounted() {
				document.removeEventListener('keydown', this.handleEscape)
			},
			render() {
				return this.modalStack.map((modal, index) => {
					// Calculate z-index based on stack position
					const zIndex = BASE_Z_INDEX + index * 10

					if (!modal || !modal.modalOptions) {
						console.error('[BageLink Modal] Invalid modal in stack:', modal)
						return null
					}

					const props = {
						...modal.modalOptions,
						'visible': true,
						'zIndex': zIndex,
						'onUpdate:visible': () => { hideModal(index) },
					}

					switch (modal.modalType) {
						case 'modalForm': {
							const formOptions = modal.modalOptions as ModalFormOptions<any>
							if (!formOptions.schema) {
								console.error('[BageLink Modal] ModalForm requires a schema', modal.modalOptions)
								return null
							}
							return h(ModalForm, props as ComponentProps<typeof ModalForm>, modal.componentSlots)
						}
						case 'confirmModal':
							return h(ModalConfirm, props as ModalConfirmOptions, {})
						default:
							return h(Modal, props, modal.componentSlots)
					}
				})
			},
		})

		// Register the component so users can manually add it if needed
		app.component('ModalContainer', ModalContainerComponent)

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

					// Create a separate app instance for modals to avoid context issues
					const modalApp = createApp(ModalContainerComponent)

					// Share the same context/plugins
					modalApp._context = app._context

					// Mount it
					modalApp.mount(container)
				}
			}

			// Try to mount immediately if DOM is ready
			if (document.body) {
				mountModalContainer()
			}

			// Also use mixin as fallback for cases where plugin is installed before DOM ready
			app.mixin({
				mounted() {
					// Only run once on the root component
					if (this.$root === this) {
						mountModalContainer()
					}
				}
			})
		}
	},
}
