import { watch, isRef, onBeforeUnmount, type Ref } from 'vue'
import { useRouter } from 'vue-router'
import { ElMessageBox } from 'element-plus'

/**
 * 统一"未保存离开守卫"（CMA表单保存交互规范 P0）
 *
 * 用法（页面 setup 中）：
 *   const isDirty = ref(false)
 *   const { markDirty, markClean } = useFormGuard(isDirty)
 *   表单数据变化时 markDirty()；保存成功后 markClean()
 *
 * 行为：
 *   1. 路由切换前检查：从 meta.editable 页面离开且 isDirty 时弹确认框
 *   2. 浏览器关闭/刷新前检查：beforeunload 原生提示
 * 组件卸载时自动注销守卫与事件监听，避免全局钩子累积。
 */
export function useFormGuard(isDirty: Ref<boolean>) {
  const router = useRouter()

  // 路由切换前检查（组件卸载时自动注销）
  const removeGuard = router.beforeEach((to, from, next) => {
    // 同页 query/hash 变化不算离开（如筛选条件更新URL），不触发守卫
    if (to.path === from.path) {
      next()
      return
    }
    if (isDirty.value && from.meta.editable) {
      ElMessageBox.confirm('有未保存的修改，确定离开吗？', '提示', {
        confirmButtonText: '离开',
        cancelButtonText: '继续编辑',
        type: 'warning',
      }).then(() => {
        isDirty.value = false
        next()
      }).catch(() => {
        next(false)
      })
    } else {
      next()
    }
  })

  // 浏览器关闭/刷新前检查
  const onBeforeUnload = (e: BeforeUnloadEvent) => {
    if (isDirty.value) {
      e.preventDefault()
      e.returnValue = ''
    }
  }
  window.addEventListener('beforeunload', onBeforeUnload)

  onBeforeUnmount(() => {
    removeGuard()
    window.removeEventListener('beforeunload', onBeforeUnload)
  })

  return {
    markDirty: () => { isDirty.value = true },
    markClean: () => { isDirty.value = false },
  }
}

/**
 * 弹窗表单脏追踪：弹窗打开时记录表单快照，内容变化后触发 onDirty，关闭时重置。
 * 适用于"弹窗编辑"类页面（KPIList/OrgManage/UserManage/ExpenseManage/TaxCompliance/CashPlan/AlertList 等）。
 * form 可为 ref 或 reactive 对象。
 *
 * 用法：
 *   trackDialogForm(showForm, form, markDirty)
 */
export function trackDialogForm(open: Ref<boolean>, form: any, onDirty: () => void) {
  let snap = ''
  const get = () => JSON.stringify(isRef(form) ? form.value : form)
  watch(open, (v) => { if (!v) snap = '' })
  watch(get, (v) => {
    if (!open.value) return
    if (!snap) snap = v
    else if (v !== snap) onDirty()
  })
}
