import mqtt, { type MqttClient, type IClientOptions } from 'mqtt'

export type MqttMessageHandler = (topic: string, payload: any, raw: Uint8Array) => void

export interface MqttConnectOptions {
    /** 覆盖 localStorage 中的 MQTTURL */
    url?: string
    username?: string
    password?: string
    clientId?: string
    qos?: 0 | 1 | 2
    connectTimeout?: number
}

const DEFAULT_OPTIONS: IClientOptions = {
    protocolId: 'MQTT',
    qos: 2,
    clean: true,
    connectTimeout: 30 * 1000,
    username: 't_user',
    password: 'njcnpqs'
}

/** 解析项目内常见的 `{key:value,key2:value2}` MQTT 字符串消息 */
export function parseMqttPayload(str: string) {
    const content = String(str).replace(/^{|}$/g, '')
    const result: Record<string, string | number> = {}
    const regex = /([^,:]+):([^,]+)(?=,|$)/g
    let match
    while ((match = regex.exec(content)) !== null) {
        const key = match[1].trim()
        let value = match[2].trim()
        // 去掉包裹引号 systemCrc:"3477" / version:"V1"
        if (
            (value.startsWith('"') && value.endsWith('"')) ||
            (value.startsWith("'") && value.endsWith("'"))
        ) {
            value = value.slice(1, -1)
        }
        result[key] = isNaN(Number(value)) || value === '' ? value : Number(value)
    }
    return result
}

/** 解码 mqtt 二进制消息为对象（兼容 JSON 字符串 / 自定义 key:value 串） */
export function decodeMqttMessage(message: Uint8Array) {
    const text = new TextDecoder().decode(message)
    let parsed: any = text
    try {
        parsed = JSON.parse(text)
    } catch {
        // 非 JSON，按原文字符串处理
    }
    // JSON.parse 后仍可能是内层字符串
    if (typeof parsed === 'string') {
        try {
            const inner = JSON.parse(parsed)
            parsed = typeof inner === 'string' ? parseMqttPayload(inner) : inner
        } catch {
            parsed = parseMqttPayload(parsed)
        }
    }
    return parsed
}

/** 根据 nowStep / allStep 计算进度百分比 */
export function calcMqttProgress(data: { nowStep?: number | string; allStep?: number | string }) {
    const allStep = Number(data?.allStep) || 0
    const nowStep = Number(data?.nowStep) || 0
    if (!allStep) return 0
    let percentage = parseInt(String((nowStep / allStep) * 100), 10)
    if (percentage > 100) percentage = 100
    if (percentage < 0) percentage = 0
    return percentage
}

export default class MqttService {
    private client: MqttClient | null = null
    private messageHandler: MqttMessageHandler | null = null
    private subscribedTopics = new Set<string>()

    get connected() {
        return !!this.client?.connected
    }

    get instance() {
        return this.client
    }

    /** 连接 MQTT，默认读取 localStorage.MQTTURL */
    connect(options: MqttConnectOptions = {}) {
        const url = options.url || window.localStorage.getItem('MQTTURL')
        if (!url) {
            throw new Error('未配置 MQTT 地址')
        }

        if (this.client?.connected) {
            return this.client
        }

        // 已有未连上的实例则先关掉
        this.disconnect()

        const clientOptions: IClientOptions = {
            ...DEFAULT_OPTIONS,
            clientId: options.clientId || `mqttjs${Math.random()}`,
            username: options.username ?? DEFAULT_OPTIONS.username,
            password: options.password ?? DEFAULT_OPTIONS.password,
            connectTimeout: options.connectTimeout ?? DEFAULT_OPTIONS.connectTimeout,
            qos: options.qos ?? DEFAULT_OPTIONS.qos
        }

        this.client = mqtt.connect(url, clientOptions)

        this.client.on('message', (topic, message) => {
            if (!this.messageHandler) return
            const payload = decodeMqttMessage(message)
            this.messageHandler(topic, payload, message)
        })

        this.client.on('error', error => {
            console.log('mqtt连接失败...', error)
            this.disconnect()
        })

        this.client.on('close', () => {
            console.log('mqtt客户端已断开连接.....')
        })

        return this.client
    }

    /** 连接成功后订阅；若尚未连接则等 connect 事件 */
    subscribe(topic: string | string[], qos: 0 | 1 | 2 = 2) {
        const topics = Array.isArray(topic) ? topic : [topic]
        topics.forEach(t => this.subscribedTopics.add(t))

        const doSubscribe = () => {
            if (!this.client) return
            topics.forEach(t => {
                this.client!.subscribe(t, { qos })
                console.log('mqtt已订阅:', t)
            })
        }

        if (this.client?.connected) {
            doSubscribe()
            return
        }

        this.client?.once('connect', () => {
            console.log('mqtt客户端已连接....')
            doSubscribe()
        })
    }

    /** 设置消息回调（覆盖式） */
    onMessage(handler: MqttMessageHandler) {
        this.messageHandler = handler
    }

    /** 取消订阅 */
    unsubscribe(topic?: string | string[]) {
        if (!this.client) return
        const topics = topic
            ? Array.isArray(topic)
                ? topic
                : [topic]
            : [...this.subscribedTopics]
        topics.forEach(t => {
            this.client?.unsubscribe(t)
            this.subscribedTopics.delete(t)
        })
    }

    /** 断开并清理 */
    disconnect() {
        if (this.client) {
            try {
                this.client.end(true)
            } catch (e) {}
            this.client = null
        }
        this.subscribedTopics.clear()
        this.messageHandler = null
    }
}

/** 快捷创建：连接并订阅指定 topic */
export function createMqttClient(
    topic: string | string[],
    onMessage: MqttMessageHandler,
    options?: MqttConnectOptions
) {
    const service = new MqttService()
    service.connect(options)
    service.onMessage(onMessage)
    service.subscribe(topic)
    return service
}
