/**
 * 对象存储相关 API
 */

import { BaseService } from '../BaseService'
import type { BaseResponse } from '@/types/api'

const inferFileNameFromKey = (fileKey: string) => {
  return fileKey.split('/').pop() || 'download'
}

const triggerBrowserDownload = (downloadUrl: string, fileName: string) => {
  const link = document.createElement('a')
  link.href = downloadUrl
  link.download = fileName
  link.target = '_blank'
  link.rel = 'noopener noreferrer'
  document.body.appendChild(link)
  link.click()
  document.body.removeChild(link)
}

/**
 * 文件用途枚举
 */
export type FilePurpose = 'iot_import' | 'device_import' | 'export' | 'attachment'

/**
 * 获取上传 URL 请求参数
 */
export interface GetUploadUrlRequest {
  /** 文件名（如：cards.csv） */
  file_name: string
  /** 文件 MIME 类型（如：text/csv），留空则自动推断 */
  content_type?: string
  /** 文件用途 (iot_import:ICCID导入, export:数据导出, attachment:附件) */
  purpose: FilePurpose
}

/**
 * 获取上传 URL 响应
 */
export interface GetUploadUrlResponse {
  /** 预签名上传 URL，使用 PUT 方法上传文件 */
  upload_url: string
  /** 文件路径标识，上传成功后用于调用业务接口 */
  file_key: string
  /** URL 有效期（秒） */
  expires_in: number
}

/**
 * 批量获取文件下载预签名 URL 请求
 */
export interface BatchDownloadUrlsRequest {
  /** 文件路径标识列表，最多 50 个 */
  file_keys: string[]
}

/**
 * 批量获取文件下载预签名 URL 响应
 */
export interface BatchDownloadUrlsResponse {
  /** 文件下载地址映射，key 为 file_key */
  urls: Record<string, string>
  /** URL 有效期（秒） */
  expires_in: number
}

export class StorageService extends BaseService {
  /**
   * 获取文件上传预签名 URL
   *
   * ## 完整上传流程
   * 1. 调用本接口获取预签名 URL 和 file_key
   * 2. 使用预签名 URL 上传文件（发起 PUT 请求直接上传到对象存储）
   * 3. 使用 file_key 调用相关业务接口
   *
   * @param data 请求参数
   */
  static getUploadUrl(data: GetUploadUrlRequest): Promise<BaseResponse<GetUploadUrlResponse>> {
    return this.post<BaseResponse<GetUploadUrlResponse>>('/api/admin/storage/upload-url', data)
  }

  /**
   * 使用预签名 URL 上传文件到对象存储
   *
   * 注意事项：
   * - 预签名 URL 有效期 15 分钟，请及时使用
   * - 上传时 Content-Type 需与请求时一致
   * - file_key 在上传成功后永久有效
   * - 开发环境通过代理上传，生产环境直接上传
   *
   * @param uploadUrl 预签名 URL（由对象存储生成）
   * @param file 文件
   * @param contentType 文件类型（需与 getUploadUrl 请求时保持一致）
   */
  static async uploadFile(uploadUrl: string, file: File, contentType?: string): Promise<void> {
    try {
      // 开发环境使用代理解决 CORS 问题
      let finalUrl = uploadUrl
      if (import.meta.env.DEV) {
        // 将对象存储域名替换为代理路径
        finalUrl = uploadUrl.replace(/^https?:\/\/obs-helf\.cucloud\.cn/, '/obs-proxy')
      }

      const headers: Record<string, string> = {}

      // 只有在明确指定 contentType 时才设置，否则让浏览器自动处理
      if (contentType) {
        headers['Content-Type'] = contentType
      }

      const response = await fetch(finalUrl, {
        method: 'PUT',
        body: file,
        headers
      })

      if (!response.ok) {
        const errorText = await response.text().catch(() => response.statusText)
        throw new Error(`上传文件失败 (${response.status}): ${errorText}`)
      }
    } catch (error: any) {
      // 增强错误信息
      if (error.name === 'TypeError' && error.message.includes('Failed to fetch')) {
        throw new Error(
          'CORS 错误: 无法上传文件到对象存储。' +
            '这通常是因为对象存储服务器未正确配置 CORS 策略。' +
            '请联系后端开发人员检查对象存储的 CORS 配置。'
        )
      }
      throw error
    }
  }

  /**
   * 批量获取文件下载预签名 URL
   * 用于获取私有文件的临时下载地址
   * @param data 请求参数
   */
  static batchDownloadUrls(
    data: BatchDownloadUrlsRequest
  ): Promise<BaseResponse<BatchDownloadUrlsResponse>> {
    return this.post<BaseResponse<BatchDownloadUrlsResponse>>(
      '/api/admin/storage/batch-download-urls',
      data
    )
  }

  /**
   * 获取单文件下载 URL
   * @param fileKey 文件路径标识
   */
  static async getDownloadUrl(fileKey: string): Promise<string> {
    const normalizedFileKey = fileKey.trim()
    const res = await this.batchDownloadUrls({
      file_keys: [normalizedFileKey]
    })

    if (res.code !== 0 || !res.data?.urls?.[normalizedFileKey]) {
      throw new Error(res.msg || '获取下载地址失败')
    }

    return res.data.urls[normalizedFileKey]
  }

  /**
   * 按文件标识直接触发浏览器下载
   * @param fileKey 文件路径标识
   * @param fileName 可选文件名
   */
  static async downloadFileByKey(fileKey: string, fileName?: string): Promise<void> {
    const normalizedFileKey = fileKey.trim()
    if (!normalizedFileKey) {
      throw new Error('文件标识不能为空')
    }

    const downloadUrl = await this.getDownloadUrl(normalizedFileKey)
    triggerBrowserDownload(downloadUrl, fileName || inferFileNameFromKey(normalizedFileKey))
  }
}
