import { Injectable, Logger } from '@nestjs/common';
import { ConfigService } from '@nestjs/config';
import COS from 'cos-nodejs-sdk-v5';

export interface CosUploadUrlResult {
  uploadUrl: string;
  bucket: string;
  region: string;
  objectKey: string;
  expireSeconds: number;
}

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

@Injectable()
export class CosStorageProvider {
  private readonly logger = new Logger(CosStorageProvider.name);
  private readonly cos: COS;
  private readonly bucket: string;
  private readonly region: string;

  constructor(private readonly configService: ConfigService) {
    const secretId = this.configService.get<string>('storage.cos.secretId', '');
    const secretKey = this.configService.get<string>('storage.cos.secretKey', '');
    const domain = this.configService.get<string>('storage.cos.domain', '');
    this.bucket = this.configService.get<string>('storage.cos.bucket', '');
    this.region = this.configService.get<string>('storage.cos.region', '');

    this.cos = new COS({
      SecretId: secretId,
      SecretKey: secretKey,
      ...(domain ? { Domain: domain } : {}),
      Protocol: 'https:',
    });

    this.logger.log(`COS provider initialized, bucket=${this.bucket}, region=${this.region}`);
  }

  getBucket(): string {
    return this.bucket;
  }

  getRegion(): string {
    return this.region;
  }

  async generateUploadUrl(
    objectKey: string,
    expiresInSeconds = 3600,
  ): Promise<CosUploadUrlResult> {
    return new Promise((resolve, reject) => {
      this.cos.getObjectUrl(
        {
          Bucket: this.bucket,
          Region: this.region,
          Key: objectKey,
          Method: 'PUT',
          Sign: true,
          Expires: expiresInSeconds,
        },
        (err, data) => {
          if (err) {
            this.logger.error(`Failed to generate upload URL for ${objectKey}`, err);
            return reject(err);
          }
          resolve({
            uploadUrl: data.Url,
            bucket: this.bucket,
            region: this.region,
            objectKey,
            expireSeconds: expiresInSeconds,
          });
        },
      );
    });
  }

  async generateDownloadUrl(
    objectKey: string,
    expiresInSeconds = 86400,
  ): Promise<string> {
    return new Promise((resolve, reject) => {
      this.cos.getObjectUrl(
        {
          Bucket: this.bucket,
          Region: this.region,
          Key: objectKey,
          Method: 'GET',
          Sign: true,
          Expires: expiresInSeconds,
        },
        (err, data) => {
          if (err) {
            this.logger.error(`Failed to generate download URL for ${objectKey}`, err);
            return reject(err);
          }
          resolve(data.Url);
        },
      );
    });
  }

  async headObject(objectKey: string): Promise<CosObjectInfo | null> {
    return new Promise((resolve, reject) => {
      this.cos.headObject(
        {
          Bucket: this.bucket,
          Region: this.region,
          Key: objectKey,
        },
        (err, data) => {
          if (err) {
            if (err.statusCode === 404) {
              return resolve(null);
            }
            this.logger.error(`headObject failed for ${objectKey}`, err);
            return reject(err);
          }
          resolve({
            size: parseInt(data.headers?.['content-length'] || '0', 10),
            etag: (data.ETag || '').replace(/"/g, ''),
            contentType: data.headers?.['content-type'] || 'application/octet-stream',
          });
        },
      );
    });
  }

  async getObject(objectKey: string): Promise<Buffer | null> {
    return new Promise((resolve) => {
      this.cos.getObject(
        { Bucket: this.bucket, Region: this.region, Key: objectKey },
        (err, data) => {
          if (err) {
            if (err.statusCode === 404) return resolve(null);
            this.logger.error(`getObject failed for ${objectKey}`, err);
            return resolve(null);
          }
          resolve(data.Body as Buffer);
        },
      );
    });
  }

  async deleteObject(objectKey: string): Promise<void> {
    return new Promise((resolve, reject) => {
      this.cos.deleteObject(
        {
          Bucket: this.bucket,
          Region: this.region,
          Key: objectKey,
        },
        (err) => {
          if (err) {
            if (err.statusCode === 404) {
              this.logger.warn(`deleteObject: object not found ${objectKey}`);
              return resolve();
            }
            this.logger.error(`deleteObject failed for ${objectKey}`, err);
            return reject(err);
          }
          resolve();
        },
      );
    });
  }

  async healthCheck(): Promise<boolean> {
    try {
      await new Promise<void>((resolve, reject) => {
        this.cos.headBucket(
          { Bucket: this.bucket, Region: this.region },
          (err) => {
            if (err) return reject(err);
            resolve();
          },
        );
      });
      return true;
    } catch (err) {
      this.logger.warn('COS health check failed', err);
      return false;
    }
  }
}
