import { Injectable, BadRequestException, Logger } from '@nestjs/common';
import { ConfigService } from '@nestjs/config';
import { CosStorageProvider } from './cos-storage.provider';
import { LocalStorageProvider } from './local-storage.provider';
import {
  sanitizeFilename,
  validateFileUpload,
} from '../../common/utils/security.util';

export interface CreateUploadUrlInput {
  filename: string;
  mimeType: string;
  sizeBytes: number;
  objectKey?: string;
}

export interface UploadUrlResult {
  uploadUrl: string;
  objectKey: string;
  bucket: string;
  region: string;
  expiresIn: number;
}

export interface VerifiedUpload {
  size: number;
  etag: string;
  contentType: string;
}

@Injectable()
export class StorageService {
  private readonly logger = new Logger(StorageService.name);
  private readonly driver: 'cos' | 'local';

  constructor(
    private readonly cos: CosStorageProvider,
    private readonly local: LocalStorageProvider,
    private readonly configService: ConfigService,
  ) {
    this.driver = (configService.get<string>('storage.driver', 'cos')) as 'cos' | 'local';
    this.logger.log(`Storage driver: ${this.driver}`);
  }

  getUploadPath(filename: string): string {
    const basePath = this.configService.get<string>(
      'storage.localPath',
      './uploads',
    );
    return `${basePath}/${filename}`;
  }

  generateObjectKey(userId: string, originalFilename: string, uploadId?: string): string {
    const date = new Date();
    const yearMonth = `${date.getFullYear()}${String(date.getMonth() + 1).padStart(2, '0')}`;
    const safeName = sanitizeFilename(originalFilename);
    const env = this.configService.get<string>('app.nodeEnv', 'development');
    const prefix = env === 'production' ? '' : 'dev/';
    return uploadId
      ? `${prefix}${userId}/${yearMonth}/${uploadId}-${safeName}`
      : `${prefix}${userId}/${yearMonth}/${safeName}`;
  }

  async createUploadUrl(
    userId: string,
    input: CreateUploadUrlInput,
    expiresIn = 3600,
  ): Promise<UploadUrlResult> {
    validateFileUpload(input.mimeType, input.sizeBytes);

    const objectKey = input.objectKey ?? this.generateObjectKey(userId, input.filename);
    return this.createUploadUrlForObject(objectKey, expiresIn);
  }

  async createUploadUrlForObject(
    objectKey: string,
    expiresIn = 3600,
  ): Promise<UploadUrlResult> {
    if (this.driver === 'local') {
      const result = await this.local.generateUploadUrl(objectKey, expiresIn);
      return {
        uploadUrl: result.uploadUrl,
        objectKey: result.objectKey,
        bucket: result.bucket,
        region: result.region,
        expiresIn,
      };
    }

    const result = await this.cos.generateUploadUrl(objectKey, expiresIn);
    return {
      uploadUrl: result.uploadUrl,
      objectKey: result.objectKey,
      bucket: result.bucket,
      region: result.region,
      expiresIn,
    };
  }

  async verifyUpload(objectKey: string): Promise<VerifiedUpload> {
    const info = this.driver === 'local'
      ? await this.local.headObject(objectKey)
      : await this.cos.headObject(objectKey);

    if (!info) {
      throw new BadRequestException('文件未被上传到存储服务');
    }
    return info;
  }

  async getDownloadUrl(
    objectKey: string,
    expiresInSeconds?: number,
  ): Promise<string> {
    return this.driver === 'local'
      ? this.local.generateDownloadUrl(objectKey, expiresInSeconds)
      : this.cos.generateDownloadUrl(objectKey, expiresInSeconds);
  }

  async deleteObject(objectKey: string): Promise<void> {
    if (this.driver === 'local') {
      await this.local.deleteObject(objectKey);
    } else {
      await this.cos.deleteObject(objectKey);
    }
  }

  async getObject(objectKey: string): Promise<Buffer | null> {
    return this.driver === 'local'
      ? this.local.getObject(objectKey)
      : this.cos.getObject(objectKey);
  }

  async healthCheck(): Promise<boolean> {
    return this.driver === 'local'
      ? this.local.healthCheck()
      : this.cos.healthCheck();
  }
}
