import { COS_BUCKET, COS_REGION, buildPublicUrl } from '@/constants/Cos';
import { api } from '@/services/api';
import Cos from 'react-native-cos-sdk';

type ServerCosToken = {
  tmpSecretId: string;
  tmpSecretKey: string;
  sessionToken: string;
  startTime?: number;
  expiredTime?: number;
  bucket?: string;
  region?: string;
  prefix?: string;
  cdnDomain?: string;
};

type CosCredential = {
  credentials: {
    tmpSecretId: string;
    tmpSecretKey: string;
    sessionToken: string;
  };
  startTime?: number;
  expiredTime?: number;
  bucket?: string;
  region?: string;
  prefix?: string;
  cdnDomain?: string;
};

type UploadOptions = {
  key: string;
  // React Native COS SDK 推荐使用本地文件路径（file:// 或 content://）
  srcUri?: string;
  // 为兼容旧实现（Web/Blob）。在 RN SDK 下会忽略 body
  body?: any;
  contentType?: string;
  onProgress?: (progress: { percent: number }) => void;
  signal?: AbortSignal;
};

let rnTransferManager: any | null = null;
let rnInitialized = false;


async function fetchCredential(): Promise<CosCredential> {
  // 后端返回 { code, message, data }，api.get 会提取 data
  const data = await api.get<ServerCosToken>('/api/users/cos/upload-token');

  console.log('fetchCredential', data);
  return {
    credentials: {
      tmpSecretId: data.tmpSecretId,
      tmpSecretKey: data.tmpSecretKey,
      sessionToken: data.sessionToken,
    },
    startTime: data.startTime,
    expiredTime: data.expiredTime,
    bucket: data.bucket,
    region: data.region,
    prefix: data.prefix,
    cdnDomain: data.cdnDomain,
  };
}

export async function uploadToCos(options: UploadOptions): Promise<{ key: string; etag?: string; headers?: Record<string, string>; publicUrl?: string }> {
  const { key, srcUri, contentType, onProgress, signal } = options;

  const cred = await fetchCredential();
  const bucket = COS_BUCKET || cred.bucket;
  const region = COS_REGION || cred.region;
  if (!bucket || !region) {
    throw new Error('未配置 COS_BUCKET / COS_REGION，且服务端未返回 bucket/region');
  }

  // 确保对象键以服务端授权的前缀开头（去除通配符，折叠斜杠，避免把 * 拼进 Key）
  const finalKey = ((): string => {
    const rawPrefix = String(cred.prefix || '');
    // 1) 去掉 * 与多余斜杠，再去掉首尾斜杠
    const safePrefix = rawPrefix
      .replace(/\*/g, '')
      .replace(/\/{2,}/g, '/')
      .replace(/^\/+|\/+$/g, '');
    const normalizedKey = key.replace(/^\//, '');
    if (!safePrefix) return normalizedKey;
    if (normalizedKey.startsWith(safePrefix + '/')) return normalizedKey;
    return `${safePrefix}/${normalizedKey}`.replace(/\/{2,}/g, '/');
  })();

  // 初始化 react-native-cos-sdk（一次）
  if (!rnInitialized) {
    await Cos.initWithSessionCredentialCallback(async () => {
      // SDK 会在需要时调用该回调，我们返回当前的临时密钥
      return {
        tmpSecretId: cred.credentials.tmpSecretId,
        tmpSecretKey: cred.credentials.tmpSecretKey,
        sessionToken: cred.credentials.sessionToken,
        startTime: cred.startTime,
        expiredTime: cred.expiredTime,
      } as any;
    });
    const serviceConfig = { region, isDebuggable: true, isHttps: true } as any;
    await Cos.registerDefaultService(serviceConfig);
    const transferConfig = {
      forceSimpleUpload: false,
      enableVerification: true,
      divisionForUpload: 2 * 1024 * 1024,
      sliceSizeForUpload: 1 * 1024 * 1024,
    } as any;
    rnTransferManager = await Cos.registerDefaultTransferManger(serviceConfig, transferConfig);
    rnInitialized = true;
  }

  if (!srcUri || typeof srcUri !== 'string') {
    throw new Error('请提供本地文件路径 srcUri（形如 file:/// 或 content://）');
  }

  const controller = new AbortController();
  if (signal) {
    if (signal.aborted) controller.abort();
    signal.addEventListener('abort', () => controller.abort(), { once: true });
  }

  return await new Promise((resolve, reject) => {
    let cancelled = false;
    let taskRef: any = null;
    (async () => {
      try {
        taskRef = await rnTransferManager.upload(
          bucket,
          finalKey,
          srcUri,
          {
            resultListener: {
              successCallBack: (header: any) => {
                if (cancelled) return;
                const publicUrl = buildPublicUrl(finalKey);
                const etag = header?.ETag || header?.headers?.ETag || header?.headers?.etag;
                resolve({ key: finalKey, etag, headers: header, publicUrl });
              },
              failCallBack: (clientError: any, serviceError: any) => {
                if (cancelled) return;
                console.log('uploadToCos', { clientError, serviceError });
                const err = clientError || serviceError || new Error('COS 上传失败');
                reject(err);
              },
            },
            progressCallback: (complete: number, target: number) => {
              if (onProgress) {
                const percent = target > 0 ? complete / target : 0;
                onProgress({ percent });
              }
            },
            contentType,
          }
        );
      } catch (e) {
        if (!cancelled) reject(e);
      }
    })();

    controller.signal.addEventListener('abort', () => {
      cancelled = true;
      try { taskRef?.cancel?.(); } catch { }
      reject(new DOMException('Aborted', 'AbortError'));
    });
  });
}

export async function uploadWithRetry(options: UploadOptions & { maxRetries?: number; backoffMs?: number }): Promise<{ key: string; etag?: string }> {
  const { maxRetries = 2, backoffMs = 800, ...rest } = options;
  let attempt = 0;
  // 简单指数退避
  while (true) {
    try {
      return await uploadToCos(rest);
    } catch (e: any) {
      if (rest.signal?.aborted) throw e;
      if (attempt >= maxRetries) throw e;
      const wait = backoffMs * Math.pow(2, attempt);
      await new Promise(r => setTimeout(r, wait));
      attempt++;
    }
  }
}


