import axios from 'axios'
import OSS from 'ali-oss'
import { signRequest } from '@/utils/hmac'
import { store } from '@/utils/store'
import config from '@/utils/config'
import bridge from '@/utils/bridge'

const api = axios.create({
  baseURL: config.apiBaseUrl ? config.apiBaseUrl + '/api/v1' : '/api/v1',
  timeout: 120000
})

// 双模式认证拦截器：
// 1. 有 sessionToken → Bearer Token（企业生产环境，通过 auth/session 换取）
// 2. 有 appSecret → HMAC 签名（开发调试 / 直连后端模式）
api.interceptors.request.use(config => {
  if (store.sessionToken) {
    config.headers['Authorization'] = 'Bearer ' + store.sessionToken
  } else if (store.orgId && store.appSecret) {
    const queryParams = {}
    if (config.params) {
      Object.entries(config.params).forEach(([k, v]) => {
        if (v != null) queryParams[k] = String(v)
      })
    }
    const headers = signRequest(store.orgId, store.appSecret, queryParams)
    Object.assign(config.headers, headers)
  }
  return config
})

// ─── Token 刷新状态管理（双模式）───
let isRefreshing = false
let pendingRequests = []

function handleTokenExpired_standalone() {
  if (isRefreshing) return
  isRefreshing = true
  store.saveRecoveryState()
  store.clearSession()
  const redirectUrl = store.authRedirectUrl
  if (redirectUrl) {
    const returnPath = encodeURIComponent(window.location.pathname || '/')
    window.location.href = redirectUrl + (redirectUrl.includes('?') ? '&' : '?')
      + 'returnPath=' + returnPath + '&orgId=' + encodeURIComponent(store.orgId)
  } else {
    window.location.href = import.meta.env.BASE_URL || '/' // 与 Vite base（/ai-web/）一致
  }
  setTimeout(() => { isRefreshing = false }, 3000)
}

function handleTokenExpired_iframe(failedConfig) {
  if (!isRefreshing) {
    isRefreshing = true
    bridge.requestTokenRefresh()
      .then(({ token, orgId, phone }) => {
        store.setSession(orgId || store.orgId, token)
        if (phone) store.setPhone(phone)
        isRefreshing = false
        pendingRequests.forEach(cb => cb(token))
        pendingRequests = []
      })
      .catch(() => {
        isRefreshing = false
        pendingRequests.forEach(cb => cb(null))
        pendingRequests = []
      })
  }
  return new Promise((resolve, reject) => {
    // 队列上限防止内存泄漏
    if (pendingRequests.length >= 20) {
      reject(new Error('TOO_MANY_PENDING_REQUESTS'))
      return
    }
    pendingRequests.push(newToken => {
      if (newToken) {
        // 防止无限重试：标记已重试，最多重试1次
        if (failedConfig.__retried) {
          reject(new Error('TOKEN_REFRESH_FAILED'))
          return
        }
        failedConfig.__retried = true
        failedConfig.headers['Authorization'] = 'Bearer ' + newToken
        // 清除可能残留的HMAC头
        delete failedConfig.headers['X-App-Key']
        delete failedConfig.headers['X-Timestamp']
        delete failedConfig.headers['X-Nonce']
        delete failedConfig.headers['X-Signature']
        resolve(api(failedConfig))
      } else {
        reject(new Error('TOKEN_REFRESH_FAILED'))
      }
    })
  })
}

api.interceptors.response.use(
  res => {
    const d = res.data
    if (d?.code !== 0 && d?.code !== 200) {
      if (config.isTokenMode && (d?.code === 20010 || d?.code === 20009)) {
        if (bridge.isEmbedded) {
          return handleTokenExpired_iframe(res.config)
        }
        handleTokenExpired_standalone()
        return Promise.reject(new Error('TOKEN_EXPIRED'))
      }
      return Promise.reject(new Error(d?.msg || '请求失败'))
    }
    return d
  },
  err => {
    if (config.isTokenMode && err.response?.status === 401) {
      if (bridge.isEmbedded) {
        return handleTokenExpired_iframe(err.config)
      }
      handleTokenExpired_standalone()
      return Promise.reject(new Error('TOKEN_EXPIRED'))
    }
    return Promise.reject(err)
  }
)

// ─── 图片上传 ───
export function uploadImage(file) {
  const form = new FormData()
  form.append('file', file)
  return api.post('/creation/upload', form, {
    headers: { 'Content-Type': 'multipart/form-data' },
    timeout: 30000
  })
}

// ─── A6 角色提取（JSON方式，传 imageUrl） ───
export function extractCharacters(imageUrl, { saveOriginal, title } = {}) {
  const body = {
    orgId: store.orgId,
    phone: store.phone,
    imageUrl
  }
  if (saveOriginal) body.saveOriginal = true
  if (title) body.title = title
  return api.post('/creation/extract-original', body, { timeout: 120000 })
}

// ─── 批量更新配音URL（录音直传OSS后调用） ───
export function batchUpdateAudio(workId, pages) {
  return api.post('/update/batch-audio', {
    orgId: store.orgId,
    phone: store.phone,
    workId,
    pages
  })
}

// ─── A3 图片故事创作 ───
export function createStory({ imageUrl, storyHint, style, title, author, heroCharId, extractId, refAdaptMode = 'enhanced' }) {
  const body = {
    orgId: store.orgId,
    phone: store.phone,
    imageUrl,
    storyHint,
    style,
    title,
    refAdaptMode,
    enableVoice: false
  }
  if (author) body.author = author
  if (heroCharId) body.heroCharId = heroCharId
  if (extractId) body.extractId = extractId
  return api.post('/creation/image-story', body)
}

// ─── B2 查询作品详情 ───
export function getWorkDetail(workId) {
  return api.get(`/query/work/${workId}`, {
    params: { orgId: store.orgId, phone: store.phone }
  })
}

// ─── B1 额度校验 ───
export function checkQuota() {
  return api.post('/query/validate', {
    orgId: store.orgId,
    phone: store.phone,
    apiType: 'A3'
  })
}

// ─── C1 编辑绘本信息（推进状态 COMPLETED→CATALOGED） ───
export function updateWork(workId, data) {
  return api.put(`/update/work/${workId}`, data)
}

// ─── C2 完成配音（推进状态 CATALOGED→DUBBED，允许空 pages） ───
export function finishDubbing(workId) {
  return api.post('/update/batch-audio', {
    orgId: store.orgId,
    phone: store.phone,
    workId,
    pages: []
  })
}

// ─── A20 AI配音 ───
export function voicePage(data) {
  return api.post('/creation/voice', {
    orgId: store.orgId,
    phone: store.phone,
    ...data
  }, { timeout: 120000 })
}

// ─── STS 临时凭证 ───
export function getStsToken() {
  return api.post('/oss/sts-token', {
    orgId: store.orgId,
    phone: store.phone
  })
}

// ─── OSS 直传（STS 模式） ───
let _ossClient = null
let _stsData = null

async function getOssClient() {
  // 复用未过期的 client
  if (_ossClient && _stsData) {
    const expireTime = new Date(_stsData.expiration).getTime()
    if (Date.now() < expireTime - 5 * 60 * 1000) {
      return { client: _ossClient, prefix: _stsData.uploadPrefix }
    }
  }
  const res = await getStsToken()
  _stsData = res.data
  _ossClient = new OSS({
    region: _stsData.region,
    accessKeyId: _stsData.accessKeyId,
    accessKeySecret: _stsData.accessKeySecret,
    stsToken: _stsData.securityToken,
    bucket: _stsData.bucket,
    endpoint: _stsData.endpoint,
    refreshSTSToken: async () => {
      const r = await getStsToken()
      _stsData = r.data
      return {
        accessKeyId: _stsData.accessKeyId,
        accessKeySecret: _stsData.accessKeySecret,
        stsToken: _stsData.securityToken
      }
    },
    refreshSTSTokenInterval: 300000
  })
  return { client: _ossClient, prefix: _stsData.uploadPrefix }
}

/**
 * STS 直传文件到 OSS
 * @param {File|Blob} file - 要上传的文件
 * @param {Object} [opts] - 选项
 * @param {string} [opts.type='img'] - 文件类型前缀: img=图片, aud=音频
 * @param {Function} [opts.onProgress] - 进度回调 (0-100)
 * @param {string} [opts.ext] - 强制指定扩展名（Blob 无 name 时使用）
 * @returns {Promise<string>} 文件的完整 OSS URL
 */
export async function ossUpload(file, opts = {}) {
  const { type = 'img', onProgress, ext: forceExt } = opts
  const { client, prefix } = await getOssClient()
  const ext = forceExt || (file.name ? file.name.split('.').pop() : 'bin').toLowerCase()
  const date = new Date().toISOString().slice(0, 10)
  const rand = Math.random().toString(36).slice(2, 10)
  const key = `${prefix}${date}/${type}_${Date.now()}_${rand}.${ext}`
  await client.put(key, file, {
    headers: { 'x-oss-object-acl': 'public-read' },
    progress: (p) => { if (onProgress) onProgress(Math.round(p * 100)) }
  })
  // 返回完整 URL
  if (_stsData.cdnDomain) {
    return `${_stsData.cdnDomain}/${key}`
  }
  return `https://${_stsData.bucket}.${_stsData.endpoint.replace('https://', '')}/${key}`
}

/**
 * STS 列举用户目录下的文件
 * @returns {Promise<Array>} 文件列表 [{name, size, lastModified, url}]
 */
export async function ossListFiles() {
  const { client, prefix } = await getOssClient()
  const result = await client.list({ prefix, 'max-keys': 100 })
  return (result.objects || []).map(obj => ({
    name: obj.name.replace(prefix, ''),
    size: obj.size,
    lastModified: obj.lastModified,
    url: obj.url
  }))
}

export async function getOrgConfig(orgId) {
  const baseUrl = config.apiBaseUrl || ''
  const res = await axios.get(baseUrl + '/api/v1/query/org-config', { params: { orgId } })
  return res.data
}

export default api
