/**
 * OfferPie 跨标签页通信桥接组件
 * 用于浏览器插件与网站页面之间的纯本地通信（不依赖任何后端接口）
 *
 * 【使用说明】
 * 1. 引入并创建实例：
 *    import { createChannelBridge } from './channelBridge'
 *    const bridge = createChannelBridge({ domain: 'offerpai.com', isPlugin: true })
 *
 * 2. 发送数据（单次，同时写缓存 + 广播）：
 *    bridge.put<MyDataType>('offerpai.com', 'resumeStatus', { ... })
 *
 * 3. 高频发送（节流写缓存，每次都广播）：
 *    bridge.emit<MyDataType>('offerpai.com', 'cursorPosition', { ... })
 *
 * 4. 主动获取一次数据（先广播问对方，60ms超时转IndexedDB）：
 *    const result = await bridge.get<MyDataType>('offerpai.com', 'resumeStatus')
 *
 * 5. 持续监听数据变化（初始读一次缓存，后续实时接收 put/emit）：
 *    const unwatch = bridge.watch<MyDataType>('offerpai.com', 'resumeStatus', (data) => { ... })
 *    // 取消监听
 *    unwatch()
 *
 * 【跨域模式 useCsl】
 * 在 put/emit/get/watch 最后一个参数传 true，数据存储走 chrome.storage.local（通过 Background 中转）
 * 适用于网页与插件在不同域名标签页之间的通信场景
 * 示例：bridge.put<MyData>('offerpai.com', 'someData', { ... }, true)
 * 注意：useCsl 模式依赖插件 Background Service Worker 在线
 *
 * 6. 销毁实例（页面卸载时调用）：
 *    bridge.destroy()
 *
 * 【参数说明】
 * - domain: 频道域名标识，不同网站项目用不同域名隔离
 * - isPlugin: 是否为插件环境，默认 false（非插件时会启用 Web Lock 保活）
 *
 * 【数据类型要求】
 * - 传递的数据必须有 TypeScript 类型定义（泛型约束 extends object）
 * - 组件自动为每条数据添加 createTime 和 updateTime 字段
 *
 * ============================================================
 * 【通信数据名称注册表】
 * 在此区域记录所有通信数据的名称、类型和用途说明
 * 新增数据时在此补充，删除时移除对应行
 * ------------------------------------------------------------
 * | 数据名(name)                  | 中文名称       | 说明                              |
 * | ----------------------------- | -------------- | --------------------------------- |
 * | offerpieBrowserPlugUsage      | 插件使用状态   | 标识插件正在运行 { usage: string, version: string } |
 * | offerpaiDeliveryLinkList      | 投递链接列表   | 记录要投递职位的来源链接,目前用于解决投递网站的重定向 { linkList: string[] } |
 * ============================================================
 */

// ============ 类型定义 ============

/** 组件自动附加的时间戳字段 */
export interface BridgeTimestamp {
    /** 数据首次创建时间（格式：yyyy-MM-dd HH:mm:ss） */
    createTime: string
    /** 数据最近更新时间（格式：yyyy-MM-dd HH:mm:ss） */
    updateTime: string
}

/** 带时间戳的完整数据包装 */
export type BridgeData<T extends object> = T & BridgeTimestamp

/** IndexedDB 存储记录结构 */
interface DBRecord {
    /** 数据名称（唯一索引） */
    name: string
    /** JSON 数据本体 */
    data: object
    /** 创建时间 */
    createTime: string
    /** 更新时间 */
    updateTime: string
}

/** BroadcastChannel 消息结构 */
interface ChannelMessage {
    /** 消息类型 */
    type: 'put' | 'emit' | 'get-request' | 'get-response' | 'csl-put-request' | 'csl-put-response' | 'csl-get-request' | 'csl-get-response'
    /** 数据名称 */
    name: string
    /** 请求唯一ID（用于 get 请求-响应匹配） */
    id?: string
    /** 数据本体（put/emit/get-response 携带） */
    data?: object
    /** 创建时间 */
    createTime?: string
    /** 更新时间 */
    updateTime?: string
    /** 目标域名（csl 中转消息使用） */
    domain?: string
}

/** watch 回调函数类型 */
type WatchCallback<T extends object> = (data: BridgeData<T> | null) => void

/** 创建实例的配置参数 */
export interface ChannelBridgeOptions {
    /** 默认域名（可在方法调用时覆盖） */
    domain: string
    /** 是否为插件环境，默认 false。非插件时启用 Web Lock 保活 */
    isPlugin?: boolean
}

// ============ 常量 ============

/** IndexedDB 数据库名称（含固定雪花ID保证唯一性） */
const DB_NAME = 'offerpie_comm_7394028156183472'
/** get 方法 BroadcastChannel 超时时间（毫秒） */
const GET_TIMEOUT_MS = 60
/** emit 方法节流写库间隔（毫秒） */
const EMIT_THROTTLE_MS = 500
/** chrome.storage.local 存储总 key */
const CSL_STORAGE_KEY = 'offerpie_comm_7394028156183472'
/** Background 消息类型前缀（用于 useCsl 模式） */
const CSL_MSG_PREFIX = 'OFFERPIE_CSL'

// ============ 工具函数 ============

/** 获取当前时间的 localDateTime 格式字符串：yyyy-MM-dd HH:mm:ss */
function nowLocalDateTime(): string {
    const d = new Date()
    const pad = (n: number) => String(n).padStart(2, '0')
    return `${d.getFullYear()}-${pad(d.getMonth() + 1)}-${pad(d.getDate())} ${pad(d.getHours())}:${pad(d.getMinutes())}:${pad(d.getSeconds())}`
}

/** 生成简单唯一ID（用于 get 请求匹配） */
function uid(): string {
    return Date.now().toString(36) + Math.random().toString(36).slice(2, 8)
}

// ============ chrome.storage.local 操作（useCsl 模式） ============

/** 检测 chrome.runtime.sendMessage 是否可用（插件环境） */
function isChromeRuntimeAvailable(): boolean {
    return typeof chrome !== 'undefined' && !!chrome.runtime && !!chrome.runtime.sendMessage
}

/**
 * 通过 Background 写入 chrome.storage.local
 * 插件环境直接发 chrome.runtime.sendMessage
 * 网页环境自动走 BroadcastChannel 中转给同域 Content Script 代理
 */
function cslPut(domain: string, name: string, data: object, createTime: string, updateTime: string): Promise<void> {
    return new Promise((resolve, reject) => {
        if (isChromeRuntimeAvailable()) {
            // 插件环境：直接发给 Background
            chrome.runtime.sendMessage(
                { type: `${CSL_MSG_PREFIX}_PUT`, domain, name, data, createTime, updateTime },
                (response) => {
                    if (chrome.runtime.lastError) {
                        reject(new Error(chrome.runtime.lastError.message))
                    } else {
                        resolve()
                    }
                }
            )
        } else {
            // 网页环境：通过 BroadcastChannel 请求同域 Content Script 代理
            const requestId = uid()
            const ch = _getCslProxyChannel(domain)
            const handler = (event: MessageEvent<ChannelMessage>) => {
                if (event.data.type === 'csl-put-response' && event.data.id === requestId) {
                    resolve()
                }
            }
            ch.addEventListener('message', handler)
            ch.postMessage({ type: 'csl-put-request', id: requestId, name, data, createTime, updateTime, domain } as ChannelMessage)
            // 超时兜底
            setTimeout(() => {
                ch.removeEventListener('message', handler)
                resolve() // 即使超时也不阻塞
            }, 3000)
        }
    })
}

/**
 * 通过 Background 从 chrome.storage.local 读取数据
 * 插件环境直接发 chrome.runtime.sendMessage
 * 网页环境自动走 BroadcastChannel 中转给同域 Content Script 代理
 */
function cslGet(domain: string, name: string): Promise<DBRecord | null> {
    return new Promise((resolve, reject) => {
        if (isChromeRuntimeAvailable()) {
            // 插件环境：直接发给 Background
            chrome.runtime.sendMessage(
                { type: `${CSL_MSG_PREFIX}_GET`, domain, name },
                (response) => {
                    if (chrome.runtime.lastError) {
                        reject(new Error(chrome.runtime.lastError.message))
                    } else {
                        resolve(response?.record || null)
                    }
                }
            )
        } else {
            // 网页环境：通过 BroadcastChannel 请求同域 Content Script 代理
            const requestId = uid()
            const ch = _getCslProxyChannel(domain)
            const handler = (event: MessageEvent<ChannelMessage>) => {
                if (event.data.type === 'csl-get-response' && event.data.id === requestId) {
                    ch.removeEventListener('message', handler)
                    const record = event.data.data && event.data.createTime && event.data.updateTime
                        ? { name: event.data.name, data: event.data.data, createTime: event.data.createTime, updateTime: event.data.updateTime }
                        : null
                    resolve(record)
                }
            }
            ch.addEventListener('message', handler)
            ch.postMessage({ type: 'csl-get-request', id: requestId, name, domain } as ChannelMessage)
            // 超时兜底
            setTimeout(() => {
                ch.removeEventListener('message', handler)
                resolve(null)
            }, 3000)
        }
    })
}

/** CSL 代理频道缓存 */
const _cslProxyChannels = new Map<string, BroadcastChannel>()

/** 获取 CSL 代理用的 BroadcastChannel（与主频道分开，避免消息混淆） */
function _getCslProxyChannel(domain: string): BroadcastChannel {
    if (_cslProxyChannels.has(domain)) return _cslProxyChannels.get(domain)!
    const ch = new BroadcastChannel(`offerpie-bridge-csl-${domain}`)
    _cslProxyChannels.set(domain, ch)
    return ch
}

// ============ IndexedDB 操作 ============

/** 数据库连接缓存（避免重复打开和版本冲突） */
const _dbCache = new Map<string, Promise<IDBDatabase>>()

/** 打开/创建 IndexedDB 数据库，按 domain 动态创建 ObjectStore，带连接缓存防并发冲突 */
function openDB(storeName: string): Promise<IDBDatabase> {
    // 如果已有进行中或已完成的连接，直接复用
    if (_dbCache.has(storeName)) {
        return _dbCache.get(storeName)!.then(db => {
            // 检查连接是否还有效（可能被 close 了）
            try {
                // 尝试创建事务验证连接有效性
                if (db.objectStoreNames.contains(storeName)) {
                    return db
                }
            } catch {
                // 连接已关闭，清除缓存重新打开
                _dbCache.delete(storeName)
            }
            return openDB(storeName)
        })
    }

    const promise = new Promise<IDBDatabase>((resolve, reject) => {
        const request = indexedDB.open(DB_NAME)
        request.onupgradeneeded = () => {
            const db = request.result
            if (!db.objectStoreNames.contains(storeName)) {
                const store = db.createObjectStore(storeName, { keyPath: 'name' })
                store.createIndex('name', 'name', { unique: true })
            }
        }
        request.onsuccess = () => {
            const db = request.result
            if (!db.objectStoreNames.contains(storeName)) {
                // store 不存在，需要升版本。关闭当前连接再重开
                db.close()
                _dbCache.delete(storeName)
                const version = db.version + 1
                const req2 = indexedDB.open(DB_NAME, version)
                req2.onupgradeneeded = () => {
                    const db2 = req2.result
                    if (!db2.objectStoreNames.contains(storeName)) {
                        const store = db2.createObjectStore(storeName, { keyPath: 'name' })
                        store.createIndex('name', 'name', { unique: true })
                    }
                }
                req2.onsuccess = () => resolve(req2.result)
                req2.onerror = () => { _dbCache.delete(storeName); reject(req2.error) }
                req2.onblocked = () => {
                    // 其他标签页占用数据库导致升级被阻塞，超时后放弃
                    console.warn('[ChannelBridge] IndexedDB upgrade blocked, retrying...')
                    _dbCache.delete(storeName)
                    reject(new Error('IndexedDB upgrade blocked'))
                }
            } else {
                resolve(db)
            }
        }
        request.onerror = () => { _dbCache.delete(storeName); reject(request.error) }
        request.onblocked = () => {
            console.warn('[ChannelBridge] IndexedDB open blocked')
            _dbCache.delete(storeName)
            reject(new Error('IndexedDB open blocked'))
        }
    })

    _dbCache.set(storeName, promise)
    return promise
}

/** 写入或更新一条记录到 IndexedDB */
async function dbPut(storeName: string, record: DBRecord): Promise<void> {
    try {
        const db = await openDB(storeName)
        return new Promise((resolve, reject) => {
            const tx = db.transaction(storeName, 'readwrite')
            const store = tx.objectStore(storeName)
            store.put(record)
            tx.oncomplete = () => resolve()
            tx.onerror = () => reject(tx.error)
        })
    } catch (err) {
        console.warn('[ChannelBridge] dbPut failed:', err)
    }
}

/** 从 IndexedDB 读取一条记录 */
async function dbGet(storeName: string, name: string): Promise<DBRecord | null> {
    try {
        const db = await openDB(storeName)
        return new Promise((resolve, reject) => {
            const tx = db.transaction(storeName, 'readonly')
            const store = tx.objectStore(storeName)
            const req = store.get(name)
            req.onsuccess = () => resolve(req.result || null)
            req.onerror = () => reject(req.error)
        })
    } catch (err) {
        console.warn('[ChannelBridge] dbGet failed:', err)
        return null
    }
}

// ============ 核心：创建通信桥接实例 ============

export function createChannelBridge(options: ChannelBridgeOptions) {
    const { domain, isPlugin = false } = options

    /** BroadcastChannel 实例缓存（按 domain 隔离频道） */
    const channels = new Map<string, BroadcastChannel>()

    /** watch 订阅回调注册表：key = `${domain}::${name}` */
    const watchers = new Map<string, Set<WatchCallback<any>>>()

    /** emit 节流定时器：key = `${domain}::${name}` */
    const emitTimers = new Map<string, ReturnType<typeof setTimeout>>()

    /** emit 节流暂存最新数据：key = `${domain}::${name}` */
    const emitPending = new Map<string, DBRecord>()

    /** get 请求等待队列：key = requestId */
    const getResolvers = new Map<string, (data: BridgeData<any> | null) => void>()

    // --- Web Lock 保活（非插件环境） ---
    if (!isPlugin && typeof navigator !== 'undefined' && navigator.locks) {
        navigator.locks.request('offerpie-keep-alive', () => new Promise(() => {}))
    }

    /** 获取或创建指定 domain 的 BroadcastChannel */
    function getChannel(channelDomain: string): BroadcastChannel {
        if (channels.has(channelDomain)) return channels.get(channelDomain)!
        const ch = new BroadcastChannel(`offerpie-bridge-${channelDomain}`)
        ch.onmessage = (event: MessageEvent<ChannelMessage>) => handleMessage(channelDomain, event.data)
        channels.set(channelDomain, ch)
        return ch
    }

    /** 处理收到的 BroadcastChannel 消息 */
    async function handleMessage(channelDomain: string, msg: ChannelMessage) {
        const watchKey = `${channelDomain}::${msg.name}`

        if (msg.type === 'put' || msg.type === 'emit') {
            // 收到对方的数据推送，触发本地 watch 回调
            const wrapped = msg.data && msg.createTime && msg.updateTime
                ? { ...msg.data, createTime: msg.createTime, updateTime: msg.updateTime } as BridgeData<any>
                : null
            const callbacks = watchers.get(watchKey)
            if (callbacks) {
                callbacks.forEach(cb => cb(wrapped))
            }
        }

        if (msg.type === 'get-request' && msg.id) {
            // 收到对方的 get 请求，从本地 IndexedDB 读取数据并回复
            const record = await dbGet(channelDomain, msg.name)
            const ch = getChannel(channelDomain)
            const response: ChannelMessage = {
                type: 'get-response',
                name: msg.name,
                id: msg.id,
                data: record?.data,
                createTime: record?.createTime,
                updateTime: record?.updateTime,
            }
            ch.postMessage(response)
        }

        if (msg.type === 'get-response' && msg.id) {
            // 收到对方对 get 请求的回复
            const resolver = getResolvers.get(msg.id)
            if (resolver) {
                getResolvers.delete(msg.id)
                if (msg.data && msg.createTime && msg.updateTime) {
                    resolver({ ...msg.data, createTime: msg.createTime, updateTime: msg.updateTime } as BridgeData<any>)
                } else {
                    resolver(null)
                }
            }
        }
    }

    // 初始化默认频道监听
    getChannel(domain)

    // --- CSL 代理监听（插件环境下，监听网页端的 CSL 中转请求） ---
    if (isPlugin && isChromeRuntimeAvailable()) {
        const cslProxyCh = new BroadcastChannel(`offerpie-bridge-csl-${domain}`)
        cslProxyCh.onmessage = async (event: MessageEvent<ChannelMessage>) => {
            const msg = event.data

            if (msg.type === 'csl-put-request' && msg.id) {
                // 代理写入 chrome.storage.local
                const targetDomain = msg.domain || domain
                await new Promise<void>((resolve) => {
                    chrome.runtime.sendMessage(
                        { type: `${CSL_MSG_PREFIX}_PUT`, domain: targetDomain, name: msg.name, data: msg.data, createTime: msg.createTime, updateTime: msg.updateTime },
                        () => resolve()
                    )
                })
                cslProxyCh.postMessage({ type: 'csl-put-response', id: msg.id, name: msg.name } as ChannelMessage)
            }

            if (msg.type === 'csl-get-request' && msg.id) {
                // 代理读取 chrome.storage.local
                const targetDomain = msg.domain || domain
                const response = await new Promise<any>((resolve) => {
                    chrome.runtime.sendMessage(
                        { type: `${CSL_MSG_PREFIX}_GET`, domain: targetDomain, name: msg.name },
                        (res) => resolve(res)
                    )
                })
                const record = response?.record
                cslProxyCh.postMessage({
                    type: 'csl-get-response',
                    id: msg.id,
                    name: msg.name,
                    data: record?.data,
                    createTime: record?.createTime,
                    updateTime: record?.updateTime,
                } as ChannelMessage)
            }
        }
    }

    // --- 公开方法 ---

    /**
     * put - 单次发送数据（写入 IndexedDB + BroadcastChannel 广播）
     * @param targetDomain 目标域名频道
     * @param name 数据名称
     * @param data 数据本体（需有 TS 类型定义）
     * @param useCsl 是否使用 chrome.storage.local 跨域模式（通过 Background 中转）
     */
    async function put<T extends object>(targetDomain: string, name: string, data: T, useCsl?: boolean): Promise<void> {
        const now = nowLocalDateTime()

        if (useCsl) {
            // 跨域模式：通过 Background 读写 chrome.storage.local
            const existing = await cslGet(targetDomain, name).catch(() => null)
            const createTime = existing?.createTime || now
            const updateTime = now
            await cslPut(targetDomain, name, data, createTime, updateTime)
            // 同时广播（同域标签页也能收到）
            const ch = getChannel(targetDomain)
            const msg: ChannelMessage = { type: 'put', name, data, createTime, updateTime }
            ch.postMessage(msg)
            return
        }

        // 默认模式：IndexedDB
        const existing = await dbGet(targetDomain, name)
        const createTime = existing?.createTime || now
        const updateTime = now

        const record: DBRecord = { name, data, createTime, updateTime }
        // 写入 IndexedDB
        await dbPut(targetDomain, record)
        // BroadcastChannel 广播
        const ch = getChannel(targetDomain)
        const msg: ChannelMessage = { type: 'put', name, data, createTime, updateTime }
        ch.postMessage(msg)
    }

    /**
     * emit - 高频发送数据（每次都广播，节流 500ms 写一次 IndexedDB）
     * @param targetDomain 目标域名频道
     * @param name 数据名称
     * @param data 数据本体（需有 TS 类型定义）
     * @param useCsl 是否使用 chrome.storage.local 跨域模式（通过 Background 中转）
     */
    async function emit<T extends object>(targetDomain: string, name: string, data: T, useCsl?: boolean): Promise<void> {
        const now = nowLocalDateTime()
        const key = `${targetDomain}::${name}`

        if (useCsl) {
            // 跨域模式：每次广播，节流写 chrome.storage.local
            let createTime: string
            const pending = emitPending.get(key)
            if (pending) {
                createTime = pending.createTime
            } else {
                const existing = await cslGet(targetDomain, name).catch(() => null)
                createTime = existing?.createTime || now
            }
            const updateTime = now
            const record: DBRecord = { name, data, createTime, updateTime }
            emitPending.set(key, record)

            // BroadcastChannel 每次都广播
            const ch = getChannel(targetDomain)
            const msg: ChannelMessage = { type: 'emit', name, data, createTime, updateTime }
            ch.postMessage(msg)

            // 节流写 chrome.storage.local
            if (!emitTimers.has(key)) {
                emitTimers.set(key, setTimeout(async () => {
                    emitTimers.delete(key)
                    const latestRecord = emitPending.get(key)
                    if (latestRecord) {
                        emitPending.delete(key)
                        await cslPut(targetDomain, latestRecord.name, latestRecord.data, latestRecord.createTime, latestRecord.updateTime).catch(() => {})
                    }
                }, EMIT_THROTTLE_MS))
            }
            return
        }

        // 默认模式：IndexedDB
        // 先尝试获取 createTime（从暂存或内存缓存）
        let createTime: string
        const pending = emitPending.get(key)
        if (pending) {
            createTime = pending.createTime
        } else {
            // 只在首次查库获取 createTime，失败就用当前时间（不阻塞后续 emit）
            let existing: DBRecord | null = null
            try {
                existing = await dbGet(targetDomain, name)
            } catch {}
            createTime = existing?.createTime || now
        }
        const updateTime = now
        const record: DBRecord = { name, data, createTime, updateTime }

        // 暂存最新数据（不再 delete，保证后续 emit 总能命中缓存不走 dbGet）
        emitPending.set(key, record)

        // BroadcastChannel 每次都广播（保证 watch 实时性）
        const ch = getChannel(targetDomain)
        const msg: ChannelMessage = { type: 'emit', name, data, createTime, updateTime }
        ch.postMessage(msg)

        // 节流写库
        if (!emitTimers.has(key)) {
            emitTimers.set(key, setTimeout(async () => {
                emitTimers.delete(key)
                const latestRecord = emitPending.get(key)
                if (latestRecord) {
                    // 注意：不再 delete emitPending，让下次 emit 继续命中缓存
                    await dbPut(targetDomain, latestRecord)
                }
            }, EMIT_THROTTLE_MS))
        }
    }

    /**
     * get - 主动获取一次数据（先 BroadcastChannel 请求，60ms 超时转 IndexedDB）
     * @param targetDomain 目标域名频道
     * @param name 数据名称
     * @param useCsl 是否使用 chrome.storage.local 跨域模式（通过 Background 中转）
     * @returns 数据本体（含 createTime/updateTime），无数据返回 null
     */
    async function get<T extends object>(targetDomain: string, name: string, useCsl?: boolean): Promise<BridgeData<T> | null> {
        if (useCsl) {
            // 跨域模式：先广播请求，超时读 chrome.storage.local
            const ch = getChannel(targetDomain)
            const requestId = uid()
            const msg: ChannelMessage = { type: 'get-request', name, id: requestId }
            ch.postMessage(msg)

            const result = await new Promise<BridgeData<T> | null>((resolve) => {
                getResolvers.set(requestId, resolve as any)
                setTimeout(() => {
                    if (getResolvers.has(requestId)) {
                        getResolvers.delete(requestId)
                        resolve(null)
                    }
                }, GET_TIMEOUT_MS)
            })

            if (result) return result

            // 超时降级读 chrome.storage.local
            const record = await cslGet(targetDomain, name).catch(() => null)
            if (record) {
                return { ...record.data, createTime: record.createTime, updateTime: record.updateTime } as BridgeData<T>
            }
            return null
        }

        // 默认模式：IndexedDB
        const ch = getChannel(targetDomain)
        const requestId = uid()

        // 发起广播请求
        const msg: ChannelMessage = { type: 'get-request', name, id: requestId }
        ch.postMessage(msg)

        // 等待响应或超时
        const result = await new Promise<BridgeData<T> | null>((resolve) => {
            getResolvers.set(requestId, resolve as any)
            setTimeout(() => {
                if (getResolvers.has(requestId)) {
                    getResolvers.delete(requestId)
                    resolve(null) // 超时，标记为未收到响应
                }
            }, GET_TIMEOUT_MS)
        })

        // 如果广播拿到了数据直接返回
        if (result) return result

        // 超时降级读 IndexedDB
        const record = await dbGet(targetDomain, name)
        if (record) {
            return { ...record.data, createTime: record.createTime, updateTime: record.updateTime } as BridgeData<T>
        }
        return null
    }

    /**
     * watch - 持续监听数据变化（初始读一次缓存，后续实时接收 put/emit 推送）
     * @param targetDomain 目标域名频道
     * @param name 数据名称
     * @param callback 数据变化回调，参数为最新数据或 null
     * @param useCsl 是否使用 chrome.storage.local 跨域模式（通过 Background 中转）
     * @returns 取消监听的函数
     */
    function watch<T extends object>(targetDomain: string, name: string, callback: WatchCallback<T>, useCsl?: boolean): () => void {
        const key = `${targetDomain}::${name}`
        // 确保频道已初始化
        getChannel(targetDomain)

        // 注册回调（BroadcastChannel 广播也能触发，不管 useCsl 与否）
        if (!watchers.has(key)) watchers.set(key, new Set())
        watchers.get(key)!.add(callback)

        if (useCsl) {
            // 跨域模式：初始读 chrome.storage.local
            cslGet(targetDomain, name).then((record) => {
                if (record) {
                    callback({ ...record.data, createTime: record.createTime, updateTime: record.updateTime } as BridgeData<T>)
                } else {
                    callback(null)
                }
            }).catch(() => callback(null))

            // 监听 chrome.storage.onChanged 事件
            const storageListener = (changes: { [key: string]: chrome.storage.StorageChange }, areaName: string) => {
                if (areaName !== 'local' || !changes[CSL_STORAGE_KEY]) return
                const newValue = changes[CSL_STORAGE_KEY].newValue as Array<{ domain: string; value: DBRecord }> | undefined
                if (!newValue) return
                const item = newValue.find(i => i.domain === targetDomain && i.value?.name === name)
                if (item && item.value) {
                    callback({ ...item.value.data, createTime: item.value.createTime, updateTime: item.value.updateTime } as BridgeData<T>)
                }
            }

            if (typeof chrome !== 'undefined' && chrome.storage && chrome.storage.onChanged) {
                chrome.storage.onChanged.addListener(storageListener)
            }

            // 返回取消监听函数
            return () => {
                const set = watchers.get(key)
                if (set) {
                    set.delete(callback)
                    if (set.size === 0) watchers.delete(key)
                }
                if (typeof chrome !== 'undefined' && chrome.storage && chrome.storage.onChanged) {
                    chrome.storage.onChanged.removeListener(storageListener)
                }
            }
        }

        // 默认模式：初始读取 IndexedDB
        dbGet(targetDomain, name).then((record) => {
            if (record) {
                callback({ ...record.data, createTime: record.createTime, updateTime: record.updateTime } as BridgeData<T>)
            } else {
                callback(null)
            }
        })

        // 返回取消监听函数
        return () => {
            const set = watchers.get(key)
            if (set) {
                set.delete(callback)
                if (set.size === 0) watchers.delete(key)
            }
        }
    }

    /**
     * destroy - 销毁实例，关闭所有频道，清除定时器
     */
    function destroy(): void {
        channels.forEach(ch => ch.close())
        channels.clear()
        watchers.clear()
        emitTimers.forEach(timer => clearTimeout(timer))
        emitTimers.clear()
        emitPending.clear()
        getResolvers.clear()
    }

    return { put, emit, get, watch, destroy }
}
