import Foundation

// 控制中心控件用的原生剪贴板客户端：用控件专用设备会话（WidgetSessionStore）直连
// /api/clipboard 读写，access 过期（401）即调 /api/auth/refresh 自刷并写回。控件在独立进程
// 运行、不开主 app，故不能复用引擎桥——这里走纯原生 REST（PLAN §I4）。独立会话使自刷不与
// 引擎抢 refresh 轮换。
enum ClipboardClient
{
    enum ClientError: Error { case noSession, http(Int) }

    // 上传本机剪贴板文本到云剪贴板（PUT /api/clipboard）。
    static func upload(_ text: String) async throws
    {
        let body = try JSONSerialization.data(withJSONObject: [
            "content": text,
            "content_type": "text/plain",
            "origin_ts": Int(Date().timeIntervalSince1970 * 1000),
        ])
        _ = try await authed(method: "PUT", path: "/api/clipboard", body: body)
    }

    // 拉取云剪贴板文本（GET /api/clipboard）。
    static func pull() async throws -> String
    {
        let data = try await authed(method: "GET", path: "/api/clipboard", body: nil)
        let obj = try? JSONSerialization.jsonObject(with: data) as? [String: Any]
        return (obj?["content"] as? String) ?? ""
    }

    // 带鉴权 + 401 自刷重试一次的请求。
    private static func authed(method: String, path: String, body: Data?) async throws -> Data
    {
        guard var session = WidgetSessionStore.load() else { throw ClientError.noSession }
        do
        {
            return try await send(method: method, path: path, body: body, token: session.accessToken)
        }
        catch ClientError.http(401)
        {
            session = try await refresh(session)
            WidgetSessionStore.save(session)
            return try await send(method: method, path: path, body: body, token: session.accessToken)
        }
    }

    private static func send(method: String, path: String, body: Data?, token: String) async throws -> Data
    {
        var req = URLRequest(url: URL(string: CDropAPI.base + path)!)
        req.httpMethod = method
        req.setValue("Bearer \(token)", forHTTPHeaderField: "Authorization")
        if let body
        {
            req.httpBody = body
            req.setValue("application/json", forHTTPHeaderField: "Content-Type")
        }
        let (data, resp) = try await URLSession.shared.data(for: req)
        let code = (resp as? HTTPURLResponse)?.statusCode ?? 0
        // 200 与 409（PUT 时云端有更新的 LWW 冲突）都算成功；其余抛对应码（401 触发自刷）。
        guard code == 200 || code == 409 else { throw ClientError.http(code) }
        return data
    }

    private static func refresh(_ session: WidgetSession) async throws -> WidgetSession
    {
        let body = try JSONSerialization.data(withJSONObject: [ "refresh_token": session.refreshToken ])
        var req = URLRequest(url: URL(string: CDropAPI.base + "/api/auth/refresh")!)
        req.httpMethod = "POST"
        req.httpBody = body
        req.setValue("application/json", forHTTPHeaderField: "Content-Type")
        let (data, resp) = try await URLSession.shared.data(for: req)
        let code = (resp as? HTTPURLResponse)?.statusCode ?? 0
        guard code == 200,
              let obj = try? JSONSerialization.jsonObject(with: data) as? [String: Any],
              let access = obj["access_token"] as? String
        else
        {
            // 刷新被拒（refresh 过期 / 吊销）→ 清掉控件会话，待主 app 下次启动重铸。
            // CAS-before-clear：仅当磁盘上的 refresh 仍是本次所用那条时才清——否则说明另一个并发
            // 控件 intent 已轮换出新会话，别把它刚写入的新令牌误删（审查 L3）。
            if WidgetSessionStore.load()?.refreshToken == session.refreshToken
            {
                WidgetSessionStore.clear()
            }
            throw ClientError.http(401)
        }
        let newRefresh = (obj["refresh_token"] as? String) ?? session.refreshToken
        return WidgetSession(accessToken: access, refreshToken: newRefresh, userId: session.userId)
    }
}
