import { useState, useCallback, useRef } from 'react'
import { S3Client, PutObjectCommand } from '@aws-sdk/client-s3'
import { getSignedUrl } from '@aws-sdk/s3-request-presigner'
import { authService } from '@/services/common'
import { BizTypeEnum } from '@/services/common/types'
import { useS3TokenCache } from './useS3TokenCache'
import { ApiError } from '@/types/api'
import { toast } from 'sonner'

interface UploadProgress {
  loaded: number
  total: number
  percentage: number
}

interface UseS3UploadOptions {
  bizType?: BizTypeEnum
  needCheck?: boolean
  onSuccess?: (url: string) => void
  onError?: (error: string) => void
  onProgress?: (progress: UploadProgress) => void
  maxRetries?: number // 最大重试次数
  retryDelay?: number // 重试延迟（毫秒）
  // 注意：token缓存现在由业务类型自动决定 - 只有SOUND_PATH使用缓存
}

interface UseS3UploadReturn {
  uploading: boolean
  progress: UploadProgress
  error: string
  retryCount: number
  uploadFile: (file: File) => Promise<string | undefined>
  cancelUpload: () => void
  reset: () => void
}

export function useS3Upload(options: UseS3UploadOptions = {}): UseS3UploadReturn {
  const [uploading, setUploading] = useState(false)
  const [progress, setProgress] = useState<UploadProgress>({ loaded: 0, total: 0, percentage: 0 })
  const [error, setError] = useState<string>('')
  const [retryCount, setRetryCount] = useState(0)

  // 用于取消上传的引用
  const xhrRef = useRef<XMLHttpRequest | null>(null)
  const abortControllerRef = useRef<AbortController | null>(null)

  const { maxRetries = 1, retryDelay = 1000, needCheck = false } = options

  // 使用token缓存hook
  const { getToken } = useS3TokenCache()

  // 使用 XMLHttpRequest 上传以支持进度追踪
  const uploadWithProgress = useCallback(
    async (presignedUrl: string, file: File, contentType: string): Promise<void> => {
      return new Promise((resolve, reject) => {
        const xhr = new XMLHttpRequest()
        xhrRef.current = xhr

        // 监听上传进度
        xhr.upload.addEventListener('progress', (event) => {
          if (event.lengthComputable) {
            const progressData: UploadProgress = {
              loaded: event.loaded,
              total: event.total,
              percentage: Math.round((event.loaded / event.total) * 100),
            }
            setProgress(progressData)
            options.onProgress?.(progressData)
          }
        })

        // 监听上传完成
        xhr.addEventListener('load', () => {
          if (xhr.status >= 200 && xhr.status < 300) {
            resolve()
          } else {
            reject(new Error(`上传失败: ${xhr.status} ${xhr.statusText}`))
          }
        })

        // 监听上传错误
        xhr.addEventListener('error', () => {
          reject(new Error('网络错误，上传失败'))
        })

        // 监听上传中止
        xhr.addEventListener('abort', () => {
          reject(new Error('上传被中止'))
        })

        // 开始上传
        xhr.open('PUT', presignedUrl)
        xhr.setRequestHeader('Content-Type', contentType)
        xhr.send(file)
      })
    },
    [options]
  )

  // 音频格式验证
  const isAudioFile = (file: File): boolean => {
    const audioExtensions = ['mp3', 'wav', 'ogg', 'webm', 'aac', 'm4a', 'flac']
    const suffix = file.name.split('.').pop()?.toLowerCase() || ''
    return audioExtensions.includes(suffix) || file.type.startsWith('audio/')
  }

  // 重试逻辑
  const sleep = (ms: number) => new Promise((resolve) => setTimeout(resolve, ms))

  const uploadFileWithRetry = useCallback(
    async (file: File, retries = 0): Promise<string> => {
      try {
        // 获取文件后缀，确保安全处理
        let suffix = ''
        const nameParts = file.name.split('.')
        const originalNamePartsLength = nameParts.length

        console.log('分割前的文件信息:', {
          fileName: file.name,
          fileType: file.type,
          nameParts: [...nameParts], // 复制数组避免被修改
          originalNamePartsLength,
        })

        // 只有当文件名包含点号且有多个部分时才提取后缀
        if (nameParts.length > 1) {
          suffix = nameParts.pop()?.toLowerCase() || ''
        }

        console.log('提取后缀后:', {
          extractedSuffix: suffix,
          remainingNameParts: nameParts,
        })

        // 如果没有后缀但是有MIME类型，尝试从MIME类型推断后缀
        if (!suffix && file.type) {
          const mimeToExtension: { [key: string]: string } = {
            'audio/mpeg': 'mp3',
            'audio/mp3': 'mp3',
            'audio/wav': 'wav',
            'audio/wave': 'wav',
            'audio/x-wav': 'wav',
            'audio/ogg': 'ogg',
            'audio/webm': 'webm',
            'audio/aac': 'aac',
            'audio/x-aac': 'aac',
            'audio/mp4': 'm4a',
            'audio/x-m4a': 'm4a',
            'image/jpeg': 'jpg',
            'image/jpg': 'jpg',
            'image/png': 'png',
            'image/gif': 'gif',
            'image/webp': 'webp',
          }
          suffix = mimeToExtension[file.type] || ''
          console.log('从MIME类型推断的后缀:', suffix)
        }

        console.log('最终使用的后缀:', suffix)

        // 获取STS token - 只有SOUND_PATH使用缓存，其他都重新获取
        const bizType = options.bizType || BizTypeEnum.Role
        const isSoundPath = bizType === BizTypeEnum.SOUND_PATH

        console.log('业务类型:', bizType, '是否为SOUND_PATH:', isSoundPath)

        const stsResponse = isSoundPath
          ? await getToken(bizType) // SOUND_PATH使用缓存
          : await authService.fetchS3StsToken({
              // 其他类型直接获取token
              bizTypeEnum: bizType,
              suffix,
            })

        console.log('STS响应 (来源:', isSoundPath ? 'SOUND_PATH缓存' : '直接API', '):', {
          bizType,
          bucket: stsResponse.bucket,
          region: stsResponse.region,
          path: stsResponse.path,
        })

        // 创建 S3 客户端
        const s3Client = new S3Client({
          region: stsResponse.region,
          credentials: {
            accessKeyId: stsResponse.accessKeyId,
            secretAccessKey: stsResponse.accessKeySecret,
            sessionToken: stsResponse.securityToken,
          },
        })

        // 根据业务类型决定使用哪种路径策略
        let key: string

        if (isSoundPath) {
          // SOUND_PATH: 使用自定义路径（从API返回的path中提取目录 + 自定义文件名）
          const pathParts = stsResponse.path.split('/')
          const directoryPath = pathParts.slice(0, -1).join('/') + '/'

          // 生成客户端控制的文件名
          const timestamp = Date.now()
          const randomString = Math.random().toString(36).substring(2, 15)
          const clientFileName = suffix
            ? `${timestamp}_${randomString}.${suffix}`
            : `${timestamp}_${randomString}`

          key = directoryPath + clientFileName

          console.log('SOUND_PATH - API返回的原始path:', stsResponse.path)
          console.log('SOUND_PATH - 提取的目录路径:', directoryPath)
          console.log('SOUND_PATH - 客户端生成的文件名:', clientFileName)
          console.log('SOUND_PATH - 最终的S3 key:', key)
        } else {
          // 其他类型: 直接使用API返回的path
          key = stsResponse.path

          console.log('其他类型 - 直接使用API返回的path:', key)
        }

        // 创建预签名URL用于上传
        const putObjectCommand = new PutObjectCommand({
          Bucket: stsResponse.bucket,
          Key: key,
          ContentType: file.type,
        })

        const presignedUrl = await getSignedUrl(s3Client, putObjectCommand, {
          expiresIn: 3600, // 1小时过期
        })

        // 使用 XMLHttpRequest 上传文件（支持进度追踪）
        await uploadWithProgress(presignedUrl, file, file.type)

        const domainSpecialMap = {
          [BizTypeEnum.IM]: process.env.NEXT_PUBLIC_S3_IM_URI,
          [BizTypeEnum.SOUND]: process.env.NEXT_PUBLIC_S3_SOUND_URI || 'https://snd.crushlevel.ai',
          [BizTypeEnum.Role]: process.env.NEXT_PUBLIC_S3_URI,
          [BizTypeEnum.Album]: process.env.NEXT_PUBLIC_S3_URI,
          [BizTypeEnum.HeadImage]: process.env.NEXT_PUBLIC_S3_URI,
          [BizTypeEnum.SOUND_PATH]:
            process.env.NEXT_PUBLIC_S3_SOUND_URI || 'https://snd.crushlevel.ai',
        }

        const domain =
          domainSpecialMap[options.bizType as BizTypeEnum] || process.env.NEXT_PUBLIC_S3_URI

        // 构建文件访问URL
        const fileUrl = `${domain}/${key}`

        // 鉴定图片是否合规
        if (needCheck) {
          await authService.fetchImageCheck({
            bizTypeEnum: options.bizType || BizTypeEnum.Role,
            fileFullPath: key,
          })
        }
        return fileUrl
      } catch (err) {
        // 如果是用户主动取消，不重试
        if (err instanceof Error && err.message.includes('中止')) {
          throw err
        }

        if (err instanceof ApiError && err.errorCode === '10019999') {
          toast.error(err.errorMsg)
          throw err
        }

        // 重试逻辑
        if (retries < maxRetries) {
          setRetryCount(retries + 1)
          console.log(`上传失败，${retryDelay}ms 后进行第 ${retries + 1} 次重试...`)
          await sleep(retryDelay)
          return uploadFileWithRetry(file, retries + 1)
        }

        throw err
      }
    },
    [options, uploadWithProgress, maxRetries, retryDelay, getToken, needCheck]
  )

  const uploadFile = useCallback(
    async (file: File) => {
      if (!file) return

      // 如果是音频文件且业务类型不是SOUND，给出友好提示
      if (isAudioFile(file) && options.bizType !== BizTypeEnum.SOUND) {
        console.warn('检测到音频文件，建议使用 BizTypeEnum.SOUND 业务类型')
      }

      setUploading(true)
      setError('')
      setRetryCount(0)
      setProgress({ loaded: 0, total: file.size, percentage: 0 })

      try {
        const fileUrl = await uploadFileWithRetry(file)

        setUploading(false)
        setProgress({ loaded: file.size, total: file.size, percentage: 100 })
        options.onSuccess?.(fileUrl)

        return fileUrl
      } catch (err) {
        setUploading(false)
        const errorMsg = err instanceof Error ? err.message : '上传失败'
        setError(errorMsg)
        options.onError?.(errorMsg)
        throw err
      }
    },
    [options, uploadFileWithRetry]
  )

  // 取消上传
  const cancelUpload = useCallback(() => {
    if (xhrRef.current) {
      xhrRef.current.abort()
      xhrRef.current = null
    }

    if (abortControllerRef.current) {
      abortControllerRef.current.abort()
      abortControllerRef.current = null
    }

    setUploading(false)
    setError('上传已取消')
  }, [])

  const reset = useCallback(() => {
    setUploading(false)
    setProgress({ loaded: 0, total: 0, percentage: 0 })
    setError('')
    setRetryCount(0)
  }, [])

  return {
    uploading,
    progress,
    error,
    retryCount,
    uploadFile,
    cancelUpload,
    reset,
  }
}

// 导出类型以供其他组件使用
export type { UploadProgress, UseS3UploadOptions, UseS3UploadReturn }

/*
使用示例：

```tsx
import { useS3Upload } from '@/hooks/useS3Upload'
import { BizTypeEnum } from '@/services/common/types'

function MyUploadComponent() {
  const { 
    uploading, 
    progress, 
    error, 
    retryCount, 
    uploadFile, 
    cancelUpload, 
    reset 
  } = useS3Upload({
    bizType: BizTypeEnum.Role, // 根据业务类型自动决定是否使用缓存
    maxRetries: 3,
    retryDelay: 2000,
    onProgress: (progress) => {
      console.log('上传进度:', progress.percentage + '%')
    },
    onSuccess: (url) => {
      console.log('上传成功:', url)
    },
    onError: (error) => {
      console.error('上传失败:', error)
    }
  })

  const handleFileSelect = async (event: React.ChangeEvent<HTMLInputElement>) => {
    const file = event.target.files?.[0]
    if (file) {
      try {
        const url = await uploadFile(file)
        console.log('文件上传成功，URL:', url)
      } catch (error) {
        console.error('上传失败:', error)
      }
    }
  }

  return (
    <div>
      <input type="file" onChange={handleFileSelect} disabled={uploading} />
      
      {uploading && (
        <div>
          <div>上传进度: {progress.percentage}%</div>
          <div>已上传: {progress.loaded} / {progress.total} 字节</div>
          {retryCount > 0 && <div>重试次数: {retryCount}</div>}
          <button onClick={cancelUpload}>取消上传</button>
        </div>
      )}
      
      {error && (
        <div>
          <div style={{ color: 'red' }}>错误: {error}</div>
          <button onClick={reset}>重置</button>
        </div>
      )}
    </div>
  )
}
```

功能特性：
1. ✅ 真实的 AWS S3 上传（使用 STS token）
2. ✅ 上传进度追踪
3. ✅ 自动重试机制（可配置次数和延迟）
4. ✅ 取消上传功能
5. ✅ 错误处理和状态管理
6. ✅ TypeScript 类型支持
7. ✅ 回调函数支持（成功、失败、进度）
8. ✅ 智能路径处理：SOUND_PATH使用自定义路径，其他类型使用API路径
9. ✅ 智能缓存策略：只有SOUND_PATH使用token缓存，其他类型每次重新获取
10. ✅ 预签名 URL 安全上传
11. ✅ 音频文件格式自动识别和处理
12. ✅ MIME类型到文件扩展名的自动映射

配置选项：
- bizType: 业务类型（默认 BizTypeEnum.Role）
  * SOUND_PATH: 使用token缓存 + 自定义文件路径
  * 其他类型: 每次重新获取token + 使用API返回路径
- maxRetries: 最大重试次数（默认 1）
- retryDelay: 重试延迟毫秒数（默认 1000）
- onSuccess: 上传成功回调
- onError: 上传失败回调
- onProgress: 上传进度回调
*/
