'use client';

import S3 from '@uppy/aws-s3';
import Uppy from '@uppy/core';
import { makeAutoObservable } from 'mobx';

import { toast } from '@/components/toast/Toast';
import { ApiClient } from '@/lib/apiClient';
import { components } from '@/lib/gen';

const POLL_UPLOAD_STATUS_RETRIES = 60;
type VideoUploadType = components['schemas']['VideoUploadType'];
// @TODO: Why isn't this in rootState...?
export class UploadState {
  apiClient: ApiClient;
  uppy: Uppy;
  uploadId: string | null = null;
  uploadClipMetadata?: {
    title?: string;
    imageUrl?: string;
    s3_id?: string;
  };
  isUploading = false;
  isProcessing = false;
  uploadedFilename: string | null = null;
  uploadType: string | null = null;
  setShouldConfirmClose: ((value: boolean) => void) | undefined;
  isVideoCover: boolean = false;
  requireVideoSprite: boolean = false;
  error?: string | null = null;
  currentStatus?: string | null = null;
  requireModeration: boolean = true;
  videoUploadType: VideoUploadType | null = null;

  constructor(
    apiClient: ApiClient,
    options: {
      setShouldConfirmClose?: (value: boolean) => void;
      isVideoCover?: boolean;
      requireVideoSprite?: boolean;
      requireModeration?: boolean;
      videoUploadType?: VideoUploadType | null;
      maxFileSizeMb?: number;
      maxFileSize?: number;
    }
  ) {
    const {
      setShouldConfirmClose,
      isVideoCover = false,
      requireVideoSprite = false,
      requireModeration = true,
      videoUploadType = null,
      maxFileSizeMb = 100,
      maxFileSize = maxFileSizeMb * 1024 * 1024,
    } = options || {};

    makeAutoObservable(this);

    this.apiClient = apiClient;
    this.requireModeration = requireModeration;
    this.setShouldConfirmClose = setShouldConfirmClose;
    this.isVideoCover = isVideoCover;
    this.requireVideoSprite = requireVideoSprite;
    this.videoUploadType = videoUploadType;
    this.uppy = new Uppy({
      restrictions: {
        maxNumberOfFiles: 1,
        maxFileSize,
      },
    }).use(S3, {
      getUploadParameters: async (file) => {
        const { data } = await apiClient.POST('/api/uploads/video/', {
          body: {
            extension: file.extension,
          },
        });

        if (!data) {
          throw new Error('Failed to fetch upload parameters');
        }

        this.isUploading = true;
        this.uploadId = data.id;
        this.setShouldConfirmClose?.(true);

        return {
          url: data?.url,
          fields: data.fields as Record<string, never>,
        };
      },
    });

    // Add upload event listeners
    this.uppy.on('upload-success', async () => {
      if (!this.uploadId) return;

      this.isUploading = false;
      this.isProcessing = true;
      this.uploadedFilename = this.uppy.getFiles()?.[0]?.name;
      this.uploadType = 'file_upload';

      const { data } = await apiClient.POST(
        '/api/uploads/video/{upload_id}/upload-finish/',
        {
          params: { path: { upload_id: this.uploadId } },
          body: {
            upload_type: this.uploadType,
            upload_filename: this.uploadedFilename,
            is_video_cover: this.isVideoCover,
            require_video_sprite: this.requireVideoSprite,
            fail_task_on_moderation: this.requireModeration,
            video_upload_type: this.videoUploadType || undefined,
          },
        }
      );

      if (data) {
        this.pollUploadStatus();
      }
    });

    this.uppy.on('upload-error', () => {
      toast({
        title: 'Upload failed',
        description: 'Please try again.',
        status: 'error',
        duration: 4000,
        isClosable: true,
      });
      this.isUploading = false;
    });

    this.uppy.on('complete', () => {
      // clean up the files
      this.uppy.getFiles().forEach((file) => {
        this.uppy.removeFile(file.id);
      });
    });
  }

  pollUploadStatus = async (retries = POLL_UPLOAD_STATUS_RETRIES) => {
    if (!this.uploadId) return;

    const { data } = await this.apiClient.GET(
      '/api/uploads/video/{upload_id}/',
      {
        params: { path: { upload_id: this.uploadId } },
      }
    );

    if (data?.status === 'error') {
      this.error = data?.error_message;
      this.isProcessing = false;
      toast({
        title: data?.error_message,
        description: 'Please upload a different video file.',
        status: 'error',
        duration: 4000,
        isClosable: true,
      });
      this.setShouldConfirmClose?.(false);
      return;
    }

    if (data?.status !== 'complete') {
      this.currentStatus = data?.status;
      if (retries > 0) {
        setTimeout(() => this.pollUploadStatus(retries - 1), 2000);
      } else {
        this.error = 'Video processing timed out. Please try again.';
        this.isProcessing = false;
      }
    } else {
      this.isProcessing = false;
      this.uploadClipMetadata = {
        title: (data as any).title || undefined,
        imageUrl: (data as any).image_url || undefined,
        s3_id: (data as any).s3_id || undefined,
      };
    }
  };
}
