import { createCustomOSSUploader } from '@bty/uploader'
import type { STSTokenResponse } from '@/package/uploader/src/index'
import { AESUtils } from '@/package/utils/crypto'
import { tryParseToObject } from '@/package/utils/parse'
import { isSupportedFileType, getMimeByExtension } from './file-types'
import { HTTPClient } from '@/http'

const MAX_FILE_SIZE = 100 * 1024 * 1024 // 100MB

export interface BotAttachment {
  url: string
  fileName: string
  mimeType?: string
  size?: number
}

interface UploadResult {
  fileId: string
  fileName: string
}

// Reuse a single HTTPClient for Nova API calls
const novaClient = new HTTPClient({
  baseURL: process.env.NOVA_BASE_URL,
  headers: {
    'Content-Type': 'application/json',
    'Tenant-Id': process.env.NOVA_TENANT_ID!,
    'Authorization': process.env.NOVA_ACCESS_KEY!,
  },
})

async function getServerSTSToken(): Promise<STSTokenResponse> {
  const env = process.env.NODE_ENV
  const res = await novaClient.get<string | { data?: string }>('/v1/oss/upload_sts')
  
  const dataToDecrypt = typeof res === 'string'
    ? res
    : res?.data

  if (typeof dataToDecrypt !== 'string') {
    throw new Error('Invalid encrypted STS payload from Nova API')
  }

  const decryptedData = new AESUtils(env).decryptAES_CBC(dataToDecrypt)
  return tryParseToObject(decryptedData) as STSTokenResponse
}

async function downloadFile(
  url: string,
  headers?: Record<string, string>,
): Promise<{ buffer: Buffer; size: number }> {
  const response = await fetch(url, { headers })
  if (!response.ok) {
    throw new Error(`Download failed: ${response.status} ${response.statusText}`)
  }
  const arrayBuffer = await response.arrayBuffer()
  const buffer = Buffer.from(arrayBuffer)
  return { buffer, size: buffer.length }
}

async function uploadSingleAttachment(
  attachment: BotAttachment,
  headers?: Record<string, string>,
): Promise<UploadResult | null> {
  // 1. Validate file type
  if (!isSupportedFileType(attachment.fileName)) {
    console.warn(`[File Uploader] Unsupported file type: ${attachment.fileName}`)
    return null
  }

  // 2. Pre-check size if available
  if (attachment.size && attachment.size > MAX_FILE_SIZE) {
    console.warn(`[File Uploader] File too large (${attachment.size} bytes): ${attachment.fileName}`)
    return null
  }

  // 3. Download file
  const { buffer, size } = await downloadFile(attachment.url, headers)

  if (size > MAX_FILE_SIZE) {
    console.warn(`[File Uploader] Downloaded file too large (${size} bytes): ${attachment.fileName}`)
    return null
  }

  // 4. Determine MIME type
  const mimeType = attachment.mimeType || getMimeByExtension(attachment.fileName) || 'application/octet-stream'

  // 5. Construct OSS path (same pattern as frontend useFileUploader)
  const uuid = crypto.randomUUID()
  const timestamp = Date.now()
  const filePath = `super_agent/user_upload_file/${uuid}/_${timestamp}_${attachment.fileName}`

  // 6. Upload to OSS
  const ossUploader = createCustomOSSUploader(getServerSTSToken)
  
  // Note: We use `upload` (which maps to `put` in ali-oss) instead of `multipartUpload` 
  // and pass the raw `Buffer` directly. In Node.js, ali-oss natively supports Buffer 
  // for `put`, whereas `multipartUpload` expects a file path or a browser File object 
  // (which causes "FileReader is not defined" when polyfilled).
  await ossUploader.upload({
    filePath,
    file: buffer as unknown as File,
    options: {
      headers: {
        'Content-Type': mimeType,
        'Content-Disposition': 'inline',
      },
    },
  })

  // 7. Create file record via Nova API
  const lastDotIndex = attachment.fileName.lastIndexOf('.')
  const splitName = lastDotIndex !== -1
    ? [attachment.fileName.substring(0, lastDotIndex), attachment.fileName.substring(lastDotIndex + 1)]
    : [attachment.fileName]

  const safeName = `${splitName[0]}-${Math.random().toString(36).substring(2, 5)}${splitName.length > 1 ? `.${splitName[1]}` : ''}`

  const res = await novaClient.post<{ file_upload_record_id: string }>(
    '/v1/super_agent/file_upload_record/create',
    {
      file_url: filePath,
      file_type: mimeType,
      file_name: safeName,
      file_byte_size: size,
      conversation_id: uuid,
    },
  )

  console.log(`[File Uploader] Uploaded: ${safeName} → ${res.file_upload_record_id}`)

  return {
    fileId: res.file_upload_record_id,
    fileName: safeName,
  }
}

/**
 * Upload multiple bot attachments to Nova OSS and create file records.
 * Returns array of upload_file_ids (only successful uploads).
 * Never throws — individual failures are logged and skipped.
 */
export async function uploadBotAttachments(
  attachments: BotAttachment[],
  headers?: Record<string, string>,
): Promise<string[]> {
  if (attachments.length === 0) return []

  const results = await Promise.allSettled(
    attachments.map(att => uploadSingleAttachment(att, headers)),
  )

  const fileIds: string[] = []
  for (const result of results) {
    if (result.status === 'fulfilled' && result.value) {
      fileIds.push(result.value.fileId)
    } else if (result.status === 'rejected') {
      console.error(`[File Uploader] Attachment upload failed:`, result.reason)
    }
  }

  return fileIds
}
