'use client';

import S3 from '@uppy/aws-s3';
import Uppy from '@uppy/core';

import { SONGIFY_CONFIG, getSongifyConfig } from '@/config/songify';

export interface UploadStateOptions {
  setS3FileName: (filename: string) => void;
  maxFileSizeMb: number;
  maxFileSize: number;
}

export class UploadState {
  uploadedFilename: string | null = null;
  error: string | null = null;
  uploadedFile: any = null; // Store the uploaded file object
  uploadSuccess: boolean = false; // Track if upload was successful
  isRemovingInvalidFile: boolean = false; // Track if we're removing a file due to validation
  uppy: Uppy;
  private setS3FileName: (filename: string) => void;

  constructor(options: UploadStateOptions) {
    const { setS3FileName, maxFileSize } = options;

    // Store the callback for later use
    this.setS3FileName = setS3FileName;

    this.uppy = new Uppy({
      restrictions: {
        maxNumberOfFiles: 1,
        maxFileSize,
        allowedFileTypes: ['video/*'],
      },
      debug: true, // Enable Uppy debug mode
    }).use(S3, {
      getUploadParameters: async (file) => {
        const controller = new AbortController();
        const timeoutId = setTimeout(() => controller.abort(), 10000);

        try {
          this.error = null; // Clear previous errors

          const config = getSongifyConfig();
          const params = new URLSearchParams({
            s3_file_name: file.name, // TODO: naming convention for raw
            content_type: file.type || 'video/mp4', // Send the actual file MIME type
            expires_in: '3600',
          });

          const response = await fetch(
            `${config.presignedUrlEndpoint}?${params.toString()}`,
            { signal: controller.signal }
          );

          clearTimeout(timeoutId);

          if (!response.ok) {
            throw new Error(
              `Failed to get presigned URL: ${response.status} ${response.statusText}`
            );
          }

          const data = await response.json();

          if (!data) {
            throw new Error('No presigned URL received from server');
          }

          const { url, fields } = data;

          return {
            method: 'POST',
            url: url, // bucket root URL
            fields: fields, // policy/x-amz-* fields from your signer
            headers: {}, // don't add headers for POST; fields go in the body
          };
        } catch (error) {
          clearTimeout(timeoutId);
          const errorMessage =
            error instanceof Error ? error.message : 'Unknown error';
          this.error = `Failed to get upload URL: ${errorMessage}`;

          // Show user-friendly error
          alert(`Upload setup failed: ${errorMessage}`);

          // Re-throw so Uppy knows the upload failed
          throw error;
        }
      },
    });

    // Duration validation
    this.uppy.on('file-added', (file) => {
      if (!file.type?.startsWith('video/')) {
        return;
      }

      const video = document.createElement('video');
      video.preload = 'metadata';
      let validationCompleted = false; // Prevent multiple validations
      let cleanupCompleted = false; // Prevent multiple cleanup operations

      const cleanup = () => {
        if (cleanupCompleted) {
          return; // Prevent multiple cleanup operations
        }
        cleanupCompleted = true;
        URL.revokeObjectURL(video.src);
        video.remove();
      };

      const validateDuration = () => {
        if (validationCompleted) {
          return; // Prevent multiple validations
        }
        validationCompleted = true;

        // Check for valid duration (not NaN, not Infinity)
        if (!isFinite(video.duration) || video.duration <= 0) {
          this.isRemovingInvalidFile = true;
          this.uppy.removeFile(file.id);
          this.uppy.info('Could not determine video duration', 'error', 5000);
          cleanup();
          return;
        }

        if (video.duration < SONGIFY_CONFIG.VIDEO_DURATION_MIN) {
          this.isRemovingInvalidFile = true;
          this.uppy.removeFile(file.id);
          this.uppy.info(
            `Video must be at least ${SONGIFY_CONFIG.VIDEO_DURATION_MIN} seconds long`,
            'error',
            5000
          );
          cleanup();
          return;
        }

        if (video.duration > SONGIFY_CONFIG.VIDEO_DURATION_MAX) {
          this.isRemovingInvalidFile = true;
          this.uppy.removeFile(file.id);
          this.uppy.info(
            `Video must be no longer than ${Math.floor(SONGIFY_CONFIG.VIDEO_DURATION_MAX / 60)} minutes ${SONGIFY_CONFIG.VIDEO_DURATION_MAX % 60} seconds`,
            'error',
            5000
          );
          cleanup();
          return;
        }

        // Validation passed, clean up
        cleanup();
      };

      // Use loadedmetadata as the primary event - it's the most reliable for getting duration
      video.addEventListener('loadedmetadata', validateDuration, {
        once: true,
      });

      video.onerror = () => {
        if (validationCompleted) {
          return; // Prevent multiple error handling
        }
        validationCompleted = true;

        this.isRemovingInvalidFile = true;
        this.uppy.removeFile(file.id);
        this.uppy.info('Could not read video file', 'error', 5000);
        cleanup();
      };

      video.src = URL.createObjectURL(file.data);
    });

    this.uppy.on('upload-success', async (file, _response) => {
      if (!file) return;
      this.uploadedFilename = file.name;
      this.uploadedFile = file; // Store the uploaded file
      this.uploadSuccess = true; // Mark upload as successful
      this.error = null;

      // Call the callback to update the App component's state
      this.setS3FileName(file.name);
    });

    this.uppy.on('upload-error', (file, error, _response) => {
      if (!file) return;
      const errorMessage = error?.message || 'Unknown error';
      this.error = `Upload failed: ${errorMessage}`;
      alert(`Upload failed for ${file.name}: ${errorMessage}`);
    });

    this.uppy.on('complete', (result) => {
      // Don't clean up files if upload was successful
      // Keep the file in the dashboard to show it was uploaded
      if (result.failed.length > 0) {
        // Only remove files if there were failures
        this.uppy.getFiles().forEach((file) => {
          this.uppy.removeFile(file.id);
        });
      }
    });

    this.uppy.on('file-removed', (file) => {
      // Don't reset if we're removing an invalid file during validation
      if (this.isRemovingInvalidFile) {
        this.isRemovingInvalidFile = false;
        return;
      }

      // Only reset if this was the uploaded file
      if (file.id === this.uploadedFile?.id) {
        this.reset();
      }
    });
  }

  reset = () => {
    this.uploadedFilename = null;
    this.error = null;
    this.uploadedFile = null;
    this.uploadSuccess = false;
    this.isRemovingInvalidFile = false;

    // Clear the s3FileName state in the App component
    this.setS3FileName('');

    // Clear all files from Uppy
    this.uppy.getFiles().forEach((file) => {
      this.uppy.removeFile(file.id);
    });
  };
}
