import {
  API_BASE_URL,
  type ApiResponse,
  CLIENT_ID,
  CLIENT_SECRET,
} from "@/lib/api"
import { clearSession, getUserToken } from "./token"

type CallMode = "public" | "device" | "user"

const DEFAULT_TIMEOUT = 15_000

function captureHeaders(response: Response): Record<string, string> {
  const headers: Record<string, string> = {}
  response.headers.forEach((value, key) => {
    headers[key.toLowerCase()] = value
  })
  return headers
}

async function request<R = undefined>(
  endpoint: string,
  init: {
    method: "GET" | "POST"
    body?: string
    auth?: string
    timeout?: number
  },
): Promise<ApiResponse<R>> {
  const controller = new AbortController()
  const timer = setTimeout(
    () => controller.abort(),
    init.timeout ?? DEFAULT_TIMEOUT,
  )

  try {
    const headers: HeadersInit = {}
    if (init.body !== undefined) {
      headers["Content-Type"] = "application/json"
    }
    if (init.auth) headers.Authorization = init.auth

    const response = await fetch(`${API_BASE_URL}${endpoint}`, {
      method: init.method,
      headers,
      body: init.body,
      signal: controller.signal,
    })

    const responseHeaders = captureHeaders(response)
    const contentType = response.headers.get("Content-Type") ?? "text/plain"

    if (contentType.includes("text/plain")) {
      const text = await response.text()
      if (!response.ok) {
        return {
          success: false,
          status: response.status,
          message: text || "请求失败",
        }
      }
      return { success: true, data: undefined as R, headers: responseHeaders }
    }

    if (contentType.includes("application/json")) {
      const json = (await response.json()) as R & {
        message?: string
        error_description?: string
        Code?: number
        Message?: string
        Data?: unknown
        data?: unknown
      }
      if (!response.ok) {
        return {
          success: false,
          status: response.status,
          message: json.message || json.error_description || "请求失败",
        }
      }

      if (typeof json.Code === "number") {
        if (json.Code === 10000) {
          return {
            success: true,
            data: (json.Data ?? json.data ?? undefined) as R,
            headers: responseHeaders,
          }
        }
        return {
          success: false,
          status: response.status,
          message: json.Message || "请求失败",
        }
      }

      return { success: true, data: json, headers: responseHeaders }
    }

    throw new Error(`无法解析响应数据: ${contentType}`)
  } catch (e) {
    if (controller.signal.aborted) {
      return {
        success: false,
        status: 408,
        message: "请求超时，请稍后重试",
      }
    }
    console.error("后端请求异常:", e)
    return {
      success: false,
      status: 500,
      message: (e as Error).message || "网络错误",
    }
  } finally {
    clearTimeout(timer)
  }
}

let deviceToken: string | null = null
let deviceTokenExpire = 0
let deviceTokenPromise: Promise<string | null> | null = null

async function acquireDeviceToken(): Promise<string | null> {
  if (deviceToken && Date.now() < deviceTokenExpire) return deviceToken
  if (!deviceTokenPromise) {
    const basic = btoa(`${CLIENT_ID}:${CLIENT_SECRET}`)
    deviceTokenPromise = request<{
      access_token: string
      expires_in: number
    }>("/api/auth/token", {
      method: "POST",
      body: JSON.stringify({ grant_type: "client_credentials" }),
      auth: `Basic ${basic}`,
    })
      .then(resp => {
        if (!resp.success) {
          deviceToken = null
          return null
        }
        deviceToken = resp.data.access_token
        deviceTokenExpire = Date.now() + (resp.data.expires_in - 60) * 1000
        return resp.data.access_token
      })
      .finally(() => {
        deviceTokenPromise = null
      })
  }
  return deviceTokenPromise
}

const inflight = new Map<string, Promise<ApiResponse<unknown>>>()

function dedupGet<R>(
  endpoint: string,
  run: () => Promise<ApiResponse<R>>,
): Promise<ApiResponse<R>> {
  const existing = inflight.get(endpoint)
  if (existing) return existing as Promise<ApiResponse<R>>
  const promise = run().finally(() => inflight.delete(endpoint))
  inflight.set(endpoint, promise)
  return promise
}

async function call<R = undefined>(
  mode: CallMode,
  endpoint: string,
  data?: unknown,
  method: "GET" | "POST" = "POST",
): Promise<ApiResponse<R>> {
  const body = data === undefined ? undefined : JSON.stringify(data)

  const run = async (): Promise<ApiResponse<R>> => {
    const maxAttempts = mode === "device" ? 2 : 1

    for (let attempt = 1; attempt <= maxAttempts; attempt += 1) {
      let auth: string | undefined
      if (mode === "user") {
        const token = getUserToken()
        if (!token) {
          return {
            success: false,
            status: 401,
            message: "未登录或会话已过期",
          }
        }
        auth = `Bearer ${token}`
      } else if (mode === "device") {
        if (attempt > 1) {
          // 上次请求 401，强制作废旧 token 再重新获取
          deviceToken = null
          deviceTokenExpire = 0
        }
        const token = await acquireDeviceToken()
        if (!token) {
          return {
            success: false,
            status: 401,
            message: "设备凭证获取失败",
          }
        }
        auth = `Bearer ${token}`
      }

      const resp = await request<R>(endpoint, { method, body, auth })

      if (!resp.success && resp.status === 401) {
        if (mode === "user") {
          clearSession()
          return resp
        }
        if (mode === "device" && attempt < maxAttempts) {
          continue
        }
      }
      return resp
    }

    return {
      success: false,
      status: 401,
      message: "请求未授权",
    }
  }

  if (method === "GET" && data === undefined) {
    return dedupGet<R>(endpoint, run)
  }
  return run()
}

async function callPublic<R = undefined>(
  endpoint: string,
  data?: unknown,
  method: "GET" | "POST" = "POST",
): Promise<ApiResponse<R>> {
  return call<R>("public", endpoint, data, method)
}

async function callByDevice<R = undefined>(
  endpoint: string,
  data?: unknown,
  method: "GET" | "POST" = "POST",
): Promise<ApiResponse<R>> {
  return call<R>("device", endpoint, data, method)
}

async function callByUser<R = undefined>(
  endpoint: string,
  data?: unknown,
  method: "GET" | "POST" = "POST",
): Promise<ApiResponse<R>> {
  return call<R>("user", endpoint, data, method)
}

export { callByDevice, callByUser, callPublic }
