import api from './api';

export interface UploadedFile {
  id: string;
  name: string;
  size: number;
  type: string;
  url: string;
  uploadedAt: string;
}

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

interface PresignedUrlResponse {
  uploadUrl: string;
  publicUrl: string;
  key: string;
}

type UploadFolder = 'avatars' | 'documents' | 'properties' | 'verification' | 'messages';

class UploadService {
  private basePath = '/upload';

  /**
   * Get presigned URL from backend
   */
  private async getPresignedUrl(
    filename: string,
    contentType: string,
    folder: UploadFolder = 'documents'
  ): Promise<PresignedUrlResponse> {
    const response = await api.post<{ data: PresignedUrlResponse }>(
      `${this.basePath}/presigned-url`,
      {
        filename,
        contentType,
        folder,
      }
    );

    return response.data.data;
  }

  /**
   * Upload file directly to S3 using presigned URL
   */
  private async uploadToS3(
    uploadUrl: string,
    file: File,
    onProgress?: (progress: UploadProgress) => void
  ): Promise<void> {
    return new Promise((resolve, reject) => {
      const xhr = new XMLHttpRequest();

      xhr.upload.addEventListener('progress', (event) => {
        if (event.lengthComputable && onProgress) {
          const percentage = Math.round((event.loaded * 100) / event.total);
          onProgress({
            loaded: event.loaded,
            total: event.total,
            percentage,
          });
        }
      });

      xhr.addEventListener('load', () => {
        if (xhr.status >= 200 && xhr.status < 300) {
          resolve();
        } else {
          reject(new Error(`Upload failed with status ${xhr.status}`));
        }
      });

      xhr.addEventListener('error', () => {
        reject(new Error('Upload failed'));
      });

      xhr.open('PUT', uploadUrl);
      xhr.setRequestHeader('Content-Type', file.type);
      xhr.send(file);
    });
  }

  /**
   * Upload a single file to S3
   */
  async uploadFile(
    file: File,
    folder: UploadFolder = 'documents',
    onProgress?: (progress: UploadProgress) => void
  ): Promise<UploadedFile> {
    // Step 1: Get presigned URL from backend
    const { uploadUrl, publicUrl, key } = await this.getPresignedUrl(
      file.name,
      file.type,
      folder
    );

    // Step 2: Upload directly to S3
    await this.uploadToS3(uploadUrl, file, onProgress);

    // Step 3: Return uploaded file info
    return {
      id: key, // Use the S3 key as the ID
      name: file.name,
      size: file.size,
      type: file.type,
      url: publicUrl,
      uploadedAt: new Date().toISOString(),
    };
  }

  /**
   * Upload multiple files to S3
   */
  async uploadFiles(
    files: File[],
    folder: UploadFolder = 'documents',
    onProgress?: (fileIndex: number, progress: UploadProgress) => void
  ): Promise<UploadedFile[]> {
    const uploadedFiles: UploadedFile[] = [];

    for (let i = 0; i < files.length; i++) {
      const file = files[i];
      const uploadedFile = await this.uploadFile(
        file,
        folder,
        onProgress ? (progress) => onProgress(i, progress) : undefined
      );
      uploadedFiles.push(uploadedFile);
    }

    return uploadedFiles;
  }

  /**
   * Delete a file from S3
   */
  async deleteFile(fileKey: string): Promise<void> {
    // The key needs to be URL encoded for path parameter
    const encodedKey = encodeURIComponent(fileKey);
    await api.delete(`${this.basePath}/${encodedKey}`);
  }

  /**
   * Upload avatar image for AGENTS (uses agent profile ID as filename for auto-replacement)
   * S3 path: {env}/agents/{agentId}/avatar.{ext}
   * Returns the S3 key (not full URL) for storage in database
   */
  async uploadAvatar(
    file: File,
    onProgress?: (progress: UploadProgress) => void
  ): Promise<UploadedFile> {
    // Step 1: Get presigned URL from backend (uses agent ID as filename)
    const response = await api.post<{ data: PresignedUrlResponse }>(
      `${this.basePath}/avatar-presigned-url`,
      {
        filename: file.name,
        contentType: file.type,
      }
    );
    const { uploadUrl, key } = response.data.data;

    // Step 2: Upload directly to S3
    await this.uploadToS3(uploadUrl, file, onProgress);

    // Step 3: Return uploaded file info with KEY (not full URL) for database storage
    return {
      id: key,
      name: file.name,
      size: file.size,
      type: file.type,
      url: key, // Store the S3 key, not the full URL
      uploadedAt: new Date().toISOString(),
    };
  }

  /**
   * Upload avatar image for regular USERS (uses user ID as folder)
   * S3 path: {env}/users/{userId}/avatar.{ext}
   * Returns the S3 key (not full URL) for storage in database
   */
  async uploadUserAvatar(
    file: File,
    onProgress?: (progress: UploadProgress) => void
  ): Promise<UploadedFile> {
    // Step 1: Get presigned URL from backend (uses user ID as folder)
    const response = await api.post<{ data: PresignedUrlResponse }>(
      `${this.basePath}/user-avatar-presigned-url`,
      {
        filename: file.name,
        contentType: file.type,
      }
    );
    const { uploadUrl, key } = response.data.data;

    // Step 2: Upload directly to S3
    await this.uploadToS3(uploadUrl, file, onProgress);

    // Step 3: Return uploaded file info with KEY (not full URL) for database storage
    return {
      id: key,
      name: file.name,
      size: file.size,
      type: file.type,
      url: key, // Store the S3 key, not the full URL
      uploadedAt: new Date().toISOString(),
    };
  }

  /**
   * Upload a message attachment (image or document) to S3
   * Uses the message-presigned-url endpoint which allows USER and AGENT roles
   * Returns the S3 key (not full URL) — use getPresignedDownloadUrl() to get a viewable URL
   */
  async uploadMessageFile(
    file: File,
    onProgress?: (progress: UploadProgress) => void
  ): Promise<UploadedFile> {
    // Step 1: Get presigned URL from backend (messages folder)
    const response = await api.post<{ data: PresignedUrlResponse }>(
      `${this.basePath}/message-presigned-url`,
      {
        filename: file.name,
        contentType: file.type,
      }
    );
    const { uploadUrl, key } = response.data.data;

    // Step 2: Upload directly to S3
    await this.uploadToS3(uploadUrl, file, onProgress);

    // Step 3: Return uploaded file info with KEY (not full URL) for database storage
    return {
      id: key,
      name: file.name,
      size: file.size,
      type: file.type,
      url: key, // Store the S3 key, not the full URL
      uploadedAt: new Date().toISOString(),
    };
  }

  /**
   * Get a presigned download URL for an S3 key
   */
  async getPresignedDownloadUrl(key: string): Promise<string> {
    const response = await api.get<{ data: { url: string } }>(
      `${this.basePath}/presigned-download-url`,
      { params: { key } }
    );
    return response.data.data.url;
  }
}

export const uploadService = new UploadService();
