package aceboot::http

import aceboot::web.*
import aceboot::router.pathregex.*
import stdx.net.http.*
import std.collection.*

/**
 * WebSocket 端点注册表与升级分发(ace-http 侧,不依赖 ace-websocket;后者经 registerWsEndpoint 反向推送)。
 * driver:拿到已升级的 WebSocket + 路径参数后自行收发(通常 onOpen + 帧循环 + onClose)。
 */
public type WsDriver = (WebSocket, HashMap<String, String>) -> Unit

class WsEndpointEntry {
    let path: String
    let driver: WsDriver
    let matcher: CompiledMatcher
    init(path: String, driver: WsDriver) {
        this.path = path
        this.driver = driver
        this.matcher = CompiledMatcher(path, MatchOptions())
    }
}

let _wsEndpoints = ArrayList<WsEndpointEntry>()

/** 注册 WebSocket 端点(路径支持 :param;由 ace-websocket 的 @WsController 推送)。 */
public func registerWsEndpoint(path: String, driver: WsDriver): Unit {
    _wsEndpoints.add(WsEndpointEntry(path, driver))
}

public func wsEndpointCount(): Int64 {
    _wsEndpoints.size
}

// 是否 WebSocket 升级请求:
// - HTTP/1.1:GET + `Upgrade: websocket` 头(RFC 6455)。
// - HTTP/2:extended CONNECT(RFC 8441,:method=CONNECT + :protocol=websocket,无 Upgrade 头);
//   :protocol 伪头由 stdx 的 upgradeFromServer 校验,这里按 CONNECT 分流即可
//   (非 ws 的 CONNECT 不会命中已注册端点,自然落回洋葱)。
func isWebSocketUpgrade(req: HttpRequest): Bool {
    match (req.version) {
        case HTTP2_0 => req.method == "CONNECT"
        case _ => match (req.headers.getFirst("upgrade")) {
            case Some(v) => toLowerAscii(v).contains("websocket")
            case None => false
        }
    }
}

// 匹配 ws 端点(复用 ace-router path-to-regexp),命中返回 driver + 路径参数。
func matchWsEndpoint(path: String): ?(WsDriver, HashMap<String, String>) {
    for (e in _wsEndpoints) {
        let (ok, result) = e.matcher.matchDetailed(path)
        if (ok) {
            let params = HashMap<String, String>()
            for (p in result.paramsText) {
                params.add(p[0], p[1])
            }
            return (e.driver, params)
        }
    }
    return None
}

// 升级并驱动 ws 会话;返回 true 表示已处理(dispatch 早退,不进洋葱)。
func handleWsUpgrade(httpCtx: HttpContext, path: String): Bool {
    match (matchWsEndpoint(path)) {
        case Some((driver, params)) =>
            let ws = WebSocket.upgradeFromServer(httpCtx)
            try {
                driver(ws, params)
            } catch (e: Exception) {
                ()
            } finally {
                ws.closeConn()
            }
            true
        case None => false
    }
}

/** 内置 echo 端点(测试/探活用):文本/二进制原样回发。 */
public func registerEchoWs(path: String): Unit {
    registerWsEndpoint(path, {ws: WebSocket, _: HashMap<String, String> =>
        while (true) {
            let f = ws.read()
            match (f.frameType) {
                case TextWebFrame => ws.write(TextWebFrame, f.payload)
                case BinaryWebFrame => ws.write(BinaryWebFrame, f.payload)
                case PingWebFrame => ws.writePongFrame(f.payload)
                case CloseWebFrame =>
                    ws.writeCloseFrame(status: 1000u16)
                    break
                case _ => ()
            }
        }
    })
}