// src/features/flyer/FlyerUploader.tsx
import React, { useState, useEffect, useRef, useCallback } from 'react';
import { useNavigate, Link } from 'react-router-dom';
import { uploadAndProcessFlyer, getJobStatus } from '../../services/aiApiClient';
import { generateFileChecksum } from '../../utils/checksum';
import { logger } from '../../services/logger.client';
import { ProcessingStatus } from './ProcessingStatus';
import type { ProcessingStage } from '../../types';
import { useDragAndDrop } from '../../hooks/useDragAndDrop';

type ProcessingState = 'idle' | 'uploading' | 'polling' | 'completed' | 'error';

interface FlyerUploaderProps {
  onProcessingComplete: () => void;
}

export const FlyerUploader: React.FC<FlyerUploaderProps> = ({ onProcessingComplete }) => {
  const [processingState, setProcessingState] = useState<ProcessingState>('idle');
  const [statusMessage, setStatusMessage] = useState<string | null>(null);
  const [jobId, setJobId] = useState<string | null>(null);
  const [errorMessage, setErrorMessage] = useState<string | null>(null);
  const [duplicateFlyerId, setDuplicateFlyerId] = useState<number | null>(null);
  const navigate = useNavigate();

  // Use a ref to manage the polling timeout to prevent memory leaks
  const pollingTimeoutRef = useRef<number | null>(null);

  // State for the ProcessingStatus component
  const [processingStages, setProcessingStages] = useState<ProcessingStage[]>([]);
  const [estimatedTime, setEstimatedTime] = useState(0);
  const [currentFile, setCurrentFile] = useState<string | null>(null);

  // Log status messages for easier debugging of the upload/processing flow.
  useEffect(() => {
    if (statusMessage) logger.info(`FlyerUploader Status: ${statusMessage}`);
  }, [statusMessage]);

  // This effect handles the polling logic
  useEffect(() => {
    if (processingState !== 'polling' || !jobId) {
      return;
    }

    const pollStatus = async () => {
      try {
        const statusResponse = await getJobStatus(jobId);
        if (!statusResponse.ok) {
          throw new Error(`Failed to get job status (HTTP ${statusResponse.status})`);
        }

        const job = await statusResponse.json();

        // Update the UI with the latest progress details from the worker
        if (job.progress) {
          setProcessingStages(job.progress.stages || []);
          setEstimatedTime(job.progress.estimatedTimeRemaining || 0);
          setStatusMessage(job.progress.message || null);
        }

        switch (job.state) {
          case 'completed':
            const flyerId = job.returnValue?.flyerId;
            if (flyerId) {
              setStatusMessage(`Processing complete! Redirecting to flyer ${flyerId}...`);
              setProcessingState('completed');
              // Call the callback to refetch the main flyer list
              onProcessingComplete();
              // Redirect to the new flyer's page after a short delay
              setTimeout(() => navigate(`/flyers/${flyerId}`), 1500);
            } else {
              throw new Error('Job completed but did not return a flyer ID.');
            }
            break;

          case 'failed':
            setErrorMessage(`Processing failed: ${job.failedReason || 'Unknown error'}`);
            setProcessingState('error');
            break;

          case 'active':
          case 'waiting':
          default:
            // If still active or waiting, poll again after a delay
            pollingTimeoutRef.current = window.setTimeout(pollStatus, 3000); // Poll every 3 seconds
            break;
        }
      } catch (error) {
        logger.error('Error during polling:', { error });
        setErrorMessage(error instanceof Error ? error.message : 'An unexpected error occurred during polling.');
        setProcessingState('error');
      }
    };

    pollStatus(); // Start the first poll

    // Cleanup function to clear the timeout if the component unmounts
    return () => {
      if (pollingTimeoutRef.current) {
        clearTimeout(pollingTimeoutRef.current);
      }
    };
  }, [processingState, jobId, onProcessingComplete, navigate]);

  const processFile = useCallback(async (file: File) => {
    setProcessingState('uploading');
    setErrorMessage(null);
    setDuplicateFlyerId(null);
    setCurrentFile(file.name);

    try {
      const checksum = await generateFileChecksum(file); // Checksum calculation can be part of the initial stages
      setStatusMessage('Uploading file...');

      const startResponse = await uploadAndProcessFlyer(file, checksum);

      if (!startResponse.ok) {
        const errorData = await startResponse.json();
        // Handle specific errors like duplicates
        if (startResponse.status === 409 && errorData.flyerId) {
            setErrorMessage(`This flyer has already been processed. You can view it here:`);
            setDuplicateFlyerId(errorData.flyerId);
        } else {
            setErrorMessage(errorData.message || `Upload failed with status ${startResponse.status}`);
        }
        setProcessingState('error');
        return;
      }

      const { jobId: newJobId } = await startResponse.json();
      setJobId(newJobId);
      setProcessingState('polling');

    } catch (error) {
      logger.error('An unexpected error occurred during file upload:', { error });
      setErrorMessage(error instanceof Error ? error.message : 'An unexpected error occurred.');
      setProcessingState('error');
    }
  }, []);

  const handleFileChange = (event: React.ChangeEvent<HTMLInputElement>) => {
    const file = event.target.files?.[0];
    if (file) {
      processFile(file);
    }
    // Clear the file input value to allow re-uploading the same file
    event.target.value = '';
  };

  /**
   * Handles the cancellation of polling. It stops watching the job's progress
   * and resets the component to its initial idle state. The background job
   * on the server will continue to process.
   */
  const resetUploaderState = useCallback(() => {
    // Setting the state to 'idle' will trigger the useEffect cleanup,
    // which clears the polling timeout.
    setProcessingState('idle');
    setJobId(null);
    setErrorMessage(null);
    setDuplicateFlyerId(null);
    setCurrentFile(null);
    setProcessingStages([]);
    setEstimatedTime(0);
    logger.info('Uploader state has been reset. Previous job ID was:', jobId);
  }, [jobId]);

    // --- Drag and Drop Hook ---
  const onFilesDropped = useCallback((files: FileList) => {
    if (files && files.length > 0) {
      processFile(files[0]); // This uploader only handles the first file.
    }
  }, [processFile]);

  const isProcessing = processingState === 'uploading' || processingState === 'polling';
  const { isDragging, dropzoneProps } = useDragAndDrop<HTMLLabelElement>({ onFilesDropped, disabled: isProcessing });

  const borderColor = isDragging ? 'border-brand-primary' : 'border-gray-400 dark:border-gray-600';
  const bgColor = isDragging ? 'bg-brand-light/50 dark:bg-brand-dark/20' : 'bg-gray-50/50 dark:bg-gray-800/20';

  // If processing, show the detailed status component. Otherwise, show the uploader.
  if (isProcessing || processingState === 'completed' || processingState === 'error') {
    return (
        <div className="max-w-4xl mx-auto">
            <ProcessingStatus stages={processingStages} estimatedTime={estimatedTime} currentFile={currentFile} />
            <div className="mt-4 text-center">
                {errorMessage && (
                    <div className="text-red-600 dark:text-red-400 font-semibold p-4 bg-red-100 dark:bg-red-900/30 rounded-md">
                        <p>{errorMessage}</p>
                        {duplicateFlyerId && (
                            <p>
                                <Link to={`/flyers/${duplicateFlyerId}`} className="text-blue-500 underline">Flyer #{duplicateFlyerId}</Link>
                            </p>
                        )}
                    </div>
                )}
                {processingState === 'polling' && (
                    <button
                        onClick={resetUploaderState}
                        className="mt-4 text-sm text-gray-500 hover:text-gray-800 dark:hover:text-gray-200 underline transition-colors"
                        title="The flyer will continue to process in the background."
                    >
                        Stop Watching Progress
                    </button>
                )}
                {(processingState === 'error' || processingState === 'completed') && (
                     <button
                        onClick={resetUploaderState} // This function resets the state, so it works for "Upload Another"
                        className="mt-4 text-sm bg-brand-secondary hover:bg-brand-dark text-white font-bold py-2 px-4 rounded-lg"
                    >
                        Upload Another Flyer
                    </button>
                )}
            </div>
        </div>
    );
  }

  return (
    <div className="max-w-xl mx-auto p-6 bg-white dark:bg-gray-800 rounded-lg shadow-md">
      <h2 className="text-2xl font-bold mb-4 text-center">Upload New Flyer</h2>
      
      <div className="flex flex-col items-center space-y-4">
        {/* File Input */}
        <label
          htmlFor="flyer-upload"
          className={`w-full text-center px-4 py-6 border-2 border-dashed rounded-lg cursor-pointer transition-colors duration-200 ease-in-out ${bgColor} ${borderColor} hover:border-brand-primary/70 dark:hover:border-brand-primary/50`}
          {...dropzoneProps}
        >
          <span className="text-lg font-medium">Click to select a file</span>
          <p className="text-sm text-gray-500 dark:text-gray-400 mt-1">or drag and drop a PDF or image</p>
          <input
            id="flyer-upload"
            type="file"
            className="hidden"
            onChange={handleFileChange}
            accept=".pdf,image/*"
          />
        </label>
      </div>
    </div>
  );
};