import React, { useState, useCallback } from 'react';
import { Box, Text, useInput } from 'ink';
import Spinner from 'ink-spinner';
import fs from 'fs';
import path from 'path';
import { FilePicker } from './FilePicker.js';
import { parseCSV, processBatchBooks, saveReportAndSummary } from '../csv.js';

interface BatchScreenProps {
  onBack: () => void;
}

type ScreenState = 'file_picker' | 'processing' | 'results' | 'error';

interface ProcessingState {
  currentBook: number;
  totalBooks: number;
  currentTitle: string;
  currentAuthor: string;
  successCount: number;
  errorCount: number;
  skippedCount: number;
  downloadProgress: number;
  bookStatuses: Record<number, 'pending' | 'processing' | 'completed' | 'failed' | 'skipped'>;
}

interface BatchResults {
  report: any[];
  downloadedBooks: any[];
  totalBooks: number;
  successCount: number;
  errorCount: number;
  skippedCount: number;
}

export const BatchScreen: React.FC<BatchScreenProps> = ({ onBack }) => {
  const [screenState, setScreenState] = useState<ScreenState>('file_picker');
  const [csvFile, setCsvFile] = useState<string>('');
  const [errorMessage, setErrorMessage] = useState('');
  const [processingState, setProcessingState] = useState<ProcessingState>({
    currentBook: 0,
    totalBooks: 0,
    currentTitle: '',
    currentAuthor: '',
    successCount: 0,
    errorCount: 0,
    skippedCount: 0,
    downloadProgress: 0,
    bookStatuses: {}
  });
  const [batchResults, setBatchResults] = useState<BatchResults | null>(null);
  const [booksData, setBooksData] = useState<any[]>([]);
  const [logFile, setLogFile] = useState<string>('');
  const [abortController, setAbortController] = useState<AbortController | null>(null);
  const [isCancelling, setIsCancelling] = useState(false);

  // Create log file with timestamp
  const createLogFile = () => {
    const timestamp = new Date().toISOString().replace(/[:.]/g, '-');
    const logFileName = `batch_download_log_${timestamp}.txt`;
    setLogFile(logFileName);
    return logFileName;
  };

  // Log function to write to file
  const logToFile = async (message: string, logFileName?: string) => {
    const timestamp = new Date().toISOString();
    const logMessage = `[${timestamp}] ${message}\n`;
    
    try {
      const fileName = logFileName || logFile;
      if (fileName) {
        await fs.promises.appendFile(fileName, logMessage);
      }
    } catch (error) {
      console.error('Error writing to log file:', error);
    }
  };

  useInput((input, key) => {
    if (key.escape && screenState === 'file_picker') {
      onBack();
    } else if (key.escape && screenState === 'results') {
      onBack();
    } else if (key.escape && screenState === 'error') {
      setScreenState('file_picker');
      setErrorMessage('');
    } else if (key.escape && screenState === 'processing') {
      handleCancelProcessing();
    }
  });

  const handleCancelProcessing = async () => {
    if (abortController && !isCancelling) {
      setIsCancelling(true);
      await logToFile('User requested cancellation of batch processing');
      abortController.abort();
      setScreenState('file_picker');
      setIsCancelling(false);
      setAbortController(null);
    }
  };

  const handleFileSelect = useCallback(async (filePath: string) => {
    try {
      setCsvFile(filePath);
      
      // Create log file for this batch run
      const logFileName = createLogFile();
      await logToFile(`Starting batch download from CSV: ${filePath}`, logFileName);
      
      // Read and parse CSV file
      const csvContent = await fs.promises.readFile(filePath, 'utf-8');
      const books = parseCSV(csvContent);
      
      await logToFile(`Parsed ${books.length} books from CSV`, logFileName);
      
      if (books.length === 0) {
        await logToFile('ERROR: No valid books found in CSV file', logFileName);
        setErrorMessage('No valid books found in CSV file');
        setScreenState('error');
        return;
      }

      // Store books data for table display
      setBooksData(books);

      // Start processing
      const initialStatuses: Record<number, 'pending' | 'processing' | 'completed' | 'failed'> = {};
      for (let i = 0; i < books.length; i++) {
        initialStatuses[i] = 'pending';
      }
      
      setProcessingState({
        currentBook: 0,
        totalBooks: books.length,
        currentTitle: '',
        currentAuthor: '',
        successCount: 0,
        errorCount: 0,
        skippedCount: 0,
        downloadProgress: 0,
        bookStatuses: initialStatuses
      });
      setScreenState('processing');

      // Process books with progress tracking
      const searchOptions = {
        maxResults: 20,
        topics: ['l', 'f'],
        objects: ['f', 'e'],
        language: 'eng'
      };

      await logToFile(`Starting processing with search options: ${JSON.stringify(searchOptions)}`, logFileName);

      // Create abort controller for this batch operation
      const controller = new AbortController();
      setAbortController(controller);

      // Create a progress-tracking version of the batch processor
      const { report, downloadedBooks } = await processBatchBooksWithProgress(
        books,
        searchOptions,
        (currentBook, totalBooks, currentTitle, currentAuthor, successCount, errorCount, skippedCount, downloadProgress = 0, bookStatuses = {}) => {
          setProcessingState({
            currentBook,
            totalBooks,
            currentTitle,
            currentAuthor,
            successCount,
            errorCount,
            skippedCount,
            downloadProgress,
            bookStatuses
          });
        },
        logFileName,
        controller.signal
      );

      // Save report and show results
      await saveReportAndSummary(report, downloadedBooks, logFileName);
      
      await logToFile(`Processing complete. Success: ${downloadedBooks.length}, Failed: ${books.length - downloadedBooks.length}`, logFileName);
      
      setBatchResults({
        report,
        downloadedBooks,
        totalBooks: books.length,
        successCount: downloadedBooks.length,
        errorCount: books.length - downloadedBooks.length - processingState.skippedCount,
        skippedCount: processingState.skippedCount
      });
      
      setScreenState('results');
      
    } catch (error) {
      if (error instanceof Error && (error.name === 'AbortError' || error.message.includes('cancelled'))) {
        await logToFile('Batch processing cancelled by user', logFile);
        setScreenState('file_picker');
      } else {
        const errorMsg = `Error processing CSV: ${error instanceof Error ? error.message : 'Unknown error'}`;
        await logToFile(`FATAL ERROR: ${errorMsg}`, logFile);
        setErrorMessage(errorMsg);
        setScreenState('error');
      }
    } finally {
      setAbortController(null);
      setIsCancelling(false);
    }
  }, [logFile]);

  const handleBackToFilePicker = useCallback(() => {
    setScreenState('file_picker');
    setErrorMessage('');
    setBatchResults(null);
  }, []);

  // Progress-tracking version of batch processor
  const processBatchBooksWithProgress = async (
    books: any[],
    searchOptions: any,
    onProgress: (current: number, total: number, title: string, author: string, successCount: number, errorCount: number, skippedCount: number, downloadProgress?: number, bookStatuses?: Record<number, 'pending' | 'processing' | 'completed' | 'failed' | 'skipped'>) => void,
    logFileName?: string,
    signal?: AbortSignal
    ) => {
    const report: any[] = [];
    const downloadedBooks: any[] = [];
    let successCount = 0;
    let errorCount = 0;
    let skippedCount = 0;

    // Initialize book statuses
    const bookStatuses: Record<number, 'pending' | 'processing' | 'completed' | 'failed' | 'skipped'> = {};
    for (let i = 0; i < books.length; i++) {
      bookStatuses[i] = 'pending';
    }

    for (let i = 0; i < books.length; i++) {
      // Check if operation was cancelled
      if (signal?.aborted) {
        await logToFile(`Operation cancelled by user at book ${i + 1}/${books.length}`, logFileName);
        break;
      }

      const book = books[i];
      const requestedAuthor = book.author || '';
      const requestedTitle = book.title || '';
      
      await logToFile(`\n--- Processing book ${i + 1}/${books.length}: ${requestedAuthor} - ${requestedTitle} ---`, logFileName);
      
      // Mark current book as processing
      bookStatuses[i] = 'processing';
      onProgress(i + 1, books.length, requestedTitle, requestedAuthor, successCount, errorCount, skippedCount, 0, bookStatuses);

      try {
        // Use the existing processBatchBooks logic for single book with progress tracking
        const singleBookResult = await processSingleBook(book, searchOptions, (progress) => {
          onProgress(i + 1, books.length, requestedTitle, requestedAuthor, successCount, errorCount, skippedCount, progress, bookStatuses);
        }, logFileName, signal);
        
        if (singleBookResult.success) {
          successCount++;
          downloadedBooks.push(singleBookResult.downloadedBook);
          bookStatuses[i] = 'completed';
          await logToFile(`✓ SUCCESS: ${requestedAuthor} - ${requestedTitle} (${singleBookResult.reportEntry.status})`, logFileName);
        } else if ((singleBookResult as any).skipped) {
          skippedCount++;
          bookStatuses[i] = 'skipped';
          await logToFile(`⏭ SKIPPED: ${requestedAuthor} - ${requestedTitle} (${singleBookResult.reportEntry.status})`, logFileName);
        } else {
          errorCount++;
          bookStatuses[i] = 'failed';
          await logToFile(`✗ FAILED: ${requestedAuthor} - ${requestedTitle} (${singleBookResult.reportEntry.status})`, logFileName);
        }
        
        report.push(singleBookResult.reportEntry);
        
      } catch (error) {
        errorCount++;
        bookStatuses[i] = 'failed';
        const errorMsg = `Error: ${error instanceof Error ? error.message : 'Unknown error'}`;
        await logToFile(`✗ EXCEPTION: ${requestedAuthor} - ${requestedTitle} - ${errorMsg}`, logFileName);
        report.push({
          requestedAuthor,
          requestedTitle,
          requestedYear: book.year || '',
          downloadedAuthor: 'N/A',
          downloadedTitle: 'N/A',
          filePath: 'N/A',
          status: errorMsg
        });
      }

      // Update progress after processing this book
      onProgress(i + 1, books.length, requestedTitle, requestedAuthor, successCount, errorCount, skippedCount, 0, bookStatuses);

      // Small delay between requests
      await new Promise(resolve => setTimeout(resolve, 1000));
    }

    return { report, downloadedBooks };
  };

  // Process single book (with retry logic matching main CSV processing)
  const processSingleBook = async (book: any, searchOptions: any, onProgress?: (progress: number) => void, logFileName?: string, signal?: AbortSignal) => {
    // Import search and AI functions
    const { searchLibGen } = await import('../search.js');
    const { downloadBookSilent } = await import('../download.js');
    const { standardizeAuthorTitle, selectBestResult, retryDownloadWithAI } = await import('../ai.js');
    const { addToBlacklist } = await import('../blacklist.js');
    
    const requestedAuthor = book.author || '';
    const requestedTitle = book.title || '';
    const requestedYear = book.year || '';
    
    // Search for the book
    let searchQuery = `${requestedAuthor} ${requestedTitle}`.trim();
    if (requestedYear) {
      searchQuery += ` ${requestedYear}`;
    }
    
    await logToFile(`  Searching with query: "${searchQuery}"`, logFileName);
    
    // Check if operation was cancelled
    if (signal?.aborted) {
      return {
        success: false,
        reportEntry: {
          requestedAuthor,
          requestedTitle,
          requestedYear,
          downloadedAuthor: 'N/A',
          downloadedTitle: 'N/A',
          filePath: 'N/A',
          status: 'Cancelled'
        }
      };
    }
    
    const searchResults = await searchLibGen(searchQuery, {
      ...searchOptions,
      maxResults: 20
    }, signal);
    
    await logToFile(`  Found ${searchResults.length} search results`, logFileName);
    
    if (searchResults.length === 0) {
      await logToFile(`  No search results found for: ${requestedAuthor} - ${requestedTitle}`, logFileName);
      return {
        success: false,
        reportEntry: {
          requestedAuthor,
          requestedTitle,
          requestedYear,
          downloadedAuthor: 'N/A',
          downloadedTitle: 'N/A',
          filePath: 'N/A',
          status: 'Not Found'
        }
      };
    }

    // Use AI to select best result
    await logToFile(`  Asking AI to select best result from ${searchResults.length} options`, logFileName);
    const selectedResult = await selectBestResult(requestedAuthor, requestedTitle, searchResults);
    
    if (!selectedResult) {
      await logToFile(`  AI determined no good matches from search results`, logFileName);
      return {
        success: false,
        reportEntry: {
          requestedAuthor,
          requestedTitle,
          requestedYear,
          downloadedAuthor: 'N/A',
          downloadedTitle: 'N/A',
          filePath: 'N/A',
          status: 'No Good Match Found'
        }
      };
    }

    await logToFile(`  AI selected: ${selectedResult.author} - ${selectedResult.title} (${selectedResult.year}) [${selectedResult.md5}]`, logFileName);

    // Standardize author and title
    const standardized = await standardizeAuthorTitle(selectedResult.author, selectedResult.title);
    let standardizedResult = {
      ...selectedResult,
      author: standardized.author,
      title: standardized.title
    };

    await logToFile(`  Standardized to: ${standardizedResult.author} - ${standardizedResult.title}`, logFileName);

    // Attempt download with retry logic
    await logToFile(`  Attempting initial download...`, logFileName);
    let downloadResult = await (downloadBookSilent as any)(standardizedResult, onProgress, signal);
    let retryCount = 0;
    const maxRetries = 3;
    
    await logToFile(`  Initial download result: success=${downloadResult.success}, shouldBlacklist=${downloadResult.shouldBlacklist}, error="${downloadResult.error}"`, logFileName);
    
    while (!downloadResult.success && downloadResult.shouldBlacklist && retryCount < maxRetries) {
      await logToFile(`  Download failed (attempt ${retryCount + 1}): ${downloadResult.error}`, logFileName);
      await logToFile(`  Initiating retry ${retryCount + 1}/${maxRetries}...`, logFileName);
      
      // Try to get next best result using AI
      const nextResult = await retryDownloadWithAI(
        requestedAuthor, 
        requestedTitle, 
        searchResults, 
        standardizedResult.md5, 
        downloadResult.error,
        addToBlacklist
      );
      
      if (!nextResult) {
        await logToFile(`  No more alternatives available after ${retryCount + 1} attempts`, logFileName);
        break;
      }
      
      await logToFile(`  AI selected alternative: ${nextResult.author} - ${nextResult.title} (${nextResult.year}) [${nextResult.md5}]`, logFileName);
      
      // Standardize the new result
      const newStandardized = await standardizeAuthorTitle(nextResult.author, nextResult.title);
      standardizedResult = {
        ...nextResult,
        author: newStandardized.author,
        title: newStandardized.title
      };
      
      await logToFile(`  Standardized alternative to: ${standardizedResult.author} - ${standardizedResult.title}`, logFileName);
      await logToFile(`  Attempting download of alternative...`, logFileName);
      
      downloadResult = await (downloadBookSilent as any)(standardizedResult, onProgress, signal);
      retryCount++;
      
      await logToFile(`  Retry ${retryCount} result: success=${downloadResult.success}, shouldBlacklist=${downloadResult.shouldBlacklist}, error="${downloadResult.error}"`, logFileName);
    }
    
    if (downloadResult.success) {
      const status = retryCount > 0 ? `Downloaded (after ${retryCount} retries)` : 'Downloaded';
      await logToFile(`  FINAL SUCCESS: ${status} - ${downloadResult.filePath}`, logFileName);
      return {
        success: true,
        downloadedBook: standardizedResult,
        reportEntry: {
          requestedAuthor,
          requestedTitle,
          requestedYear,
          downloadedAuthor: standardizedResult.author,
          downloadedTitle: standardizedResult.title,
          filePath: downloadResult.filePath,
          status
        }
      };
    } else if ((downloadResult as any).alreadyExists) {
      const status = 'File Already Exists';
      await logToFile(`  SKIPPED: ${status} - file already exists`, logFileName);
      return {
        success: false,
        skipped: true,
        reportEntry: {
          requestedAuthor,
          requestedTitle,
          requestedYear,
          downloadedAuthor: standardizedResult.author,
          downloadedTitle: standardizedResult.title,
          filePath: 'Already Exists',
          status
        }
      };
    } else {
      const status = `Download Failed: ${downloadResult.error}${retryCount > 0 ? ` (after ${retryCount} retries)` : ''}`;
      await logToFile(`  FINAL FAILURE: ${status}`, logFileName);
      return {
        success: false,
        reportEntry: {
          requestedAuthor,
          requestedTitle,
          requestedYear,
          downloadedAuthor: standardizedResult.author,
          downloadedTitle: standardizedResult.title,
          filePath: 'N/A',
          status
        }
      };
    }
  };

  if (screenState === 'processing') {
    // Calculate visible books for table display
    const currentIndex = processingState.currentBook - 1;
    const visibleBooks = booksData.slice(Math.max(0, currentIndex - 2), currentIndex + 8);
    const visibleStartIndex = Math.max(0, currentIndex - 2);
    
    // Calculate column widths similar to ResultsTable
    const terminalWidth = process.stdout.columns || 100;
    const authorWidth = 20;
    const yearWidth = 6;
    const statusWidth = 16; // Increased to accommodate [Processing...]
    const paddingAndBorders = 8;
    const titleWidth = Math.max(30, terminalWidth - authorWidth - yearWidth - statusWidth - paddingAndBorders);
    
    const truncateText = (text: string, maxLength: number) => {
      if (text.length <= maxLength) return text;
      return text.substring(0, maxLength - 3) + '...';
    };
    
    const getStatusIndicator = (index: number) => {
      const status = processingState.bookStatuses[index];
      const isCurrentBook = index === currentIndex;
      
      switch (status) {
        case 'processing':
          if (isCurrentBook && processingState.downloadProgress > 0) {
            return { text: `[${processingState.downloadProgress}%]`, color: 'yellow' as const };
          }
          return { text: '[Processing...]', color: 'yellow' as const };
        case 'completed':
          return { text: '[✓ Done]', color: 'green' as const };
        case 'skipped':
          return { text: '[⏭ Skipped]', color: 'yellow' as const };
        case 'failed':
          return { text: '[✗ Failed]', color: 'red' as const };
        case 'pending':
        default:
          return { text: '[Pending]', color: 'gray' as const };
      }
    };

    return (
      <Box flexDirection="column" padding={2}>
        <Box marginBottom={2}>
          <Text bold color="blue">
            Processing CSV Batch Download
          </Text>
        </Box>
        
        <Box flexDirection="column" alignItems="center" marginBottom={2}>
          <Box marginBottom={1}>
            <Text color="blue">
              <Spinner type="dots" />
              {' '}Processing book {processingState.currentBook} of {processingState.totalBooks}
            </Text>
          </Box>
          
          {processingState.currentTitle && (
            <Box marginBottom={1}>
              <Text color="cyan">
                Current: {processingState.currentAuthor} - {processingState.currentTitle}
              </Text>
              {processingState.downloadProgress > 0 && (
                <Text color="yellow">
                  Download Progress: {processingState.downloadProgress}%
                </Text>
              )}
            </Box>
          )}
          
          <Box flexDirection="row" gap={2}>
            <Text color="green">
              Downloaded: {processingState.successCount}
            </Text>
            <Text color="yellow">
              Skipped: {processingState.skippedCount}
            </Text>
            <Text color="red">
              Failed: {processingState.errorCount}
            </Text>
          </Box>
        </Box>
        
        {/* Books Queue Table */}
        <Box flexDirection="column" marginBottom={2}>
          <Box marginBottom={1}>
            <Text bold color="cyan">
              Books Queue:
            </Text>
          </Box>
          
          {/* Table Header */}
          <Box borderStyle="single" borderTop={true} borderBottom={true} paddingX={1}>
            <Box width={titleWidth}>
              <Text bold color="cyan">Title</Text>
            </Box>
            <Box width={authorWidth}>
              <Text bold color="cyan">Author</Text>
            </Box>
            <Box width={yearWidth}>
              <Text bold color="cyan">Year</Text>
            </Box>
            <Box width={statusWidth}>
              <Text bold color="cyan">Status</Text>
            </Box>
          </Box>
          
          {/* Table Rows */}
          {visibleBooks.map((book, index) => {
            const actualIndex = visibleStartIndex + index;
            const isCurrentBook = actualIndex === currentIndex;
            const statusIndicator = getStatusIndicator(actualIndex);
            
            return (
              <Box 
                key={actualIndex} 
                borderStyle="single" 
                borderBottom={true} 
                paddingX={1}
                borderColor={isCurrentBook ? 'yellow' : undefined}
              >
                <Box width={titleWidth}>
                  <Text color={isCurrentBook ? 'white' : undefined}>
                    {truncateText(book.title, titleWidth - 2)}
                  </Text>
                </Box>
                <Box width={authorWidth}>
                  <Text color={isCurrentBook ? 'white' : undefined}>
                    {truncateText(book.author, authorWidth - 2)}
                  </Text>
                </Box>
                <Box width={yearWidth}>
                  <Text color={isCurrentBook ? 'white' : undefined}>
                    {truncateText(book.year || 'N/A', yearWidth - 2)}
                  </Text>
                </Box>
                <Box width={statusWidth}>
                  <Text color={statusIndicator.color}>
                    {statusIndicator.text}
                  </Text>
                </Box>
              </Box>
            );
          })}
        </Box>
        
        <Box>
          <Text color="gray">
            {isCancelling 
              ? 'Cancelling batch operation...' 
              : 'This may take several minutes... Press [Esc] to cancel'
            }
          </Text>
        </Box>
      </Box>
    );
  }

  if (screenState === 'results') {
    return (
      <Box flexDirection="column" padding={2}>
        <Box marginBottom={2}>
          <Text bold color="blue">
            Batch Download Complete
          </Text>
        </Box>
        
        <Box flexDirection="column" marginBottom={2}>
          <Text color="gray">
            CSV File: {csvFile}
          </Text>
          <Text color="gray">
            Total Books: {batchResults?.totalBooks || 0}
          </Text>
          <Text color="green">
            Successfully Downloaded: {batchResults?.successCount || 0}
          </Text>
          <Text color="yellow">
            Skipped (Already Exist): {batchResults?.skippedCount || 0}
          </Text>
          <Text color="red">
            Failed: {batchResults?.errorCount || 0}
          </Text>
        </Box>
        
        {batchResults && batchResults.downloadedBooks.length > 0 && (
          <Box flexDirection="column" marginBottom={2}>
            <Text color="cyan" bold>
              Downloaded Books:
            </Text>
            <Box flexDirection="column" paddingLeft={2}>
              {batchResults.downloadedBooks.slice(0, 10).map((book, index) => (
                <Text key={index} color="gray">
                  {book.author} - {book.title} ({book.year})
                </Text>
              ))}
              {batchResults.downloadedBooks.length > 10 && (
                <Text color="gray">
                  ... and {batchResults.downloadedBooks.length - 10} more
                </Text>
              )}
            </Box>
          </Box>
        )}
        
        <Box flexDirection="column" gap={1}>
          <Text color="gray">
            A detailed report has been saved to your current directory.
          </Text>
          {logFile && (
            <Text color="gray">
              Debug log saved to: {logFile}
            </Text>
          )}
          <Box flexDirection="row" gap={2}>
            <Text color="gray">
              [Esc] Back to Menu
            </Text>
          </Box>
        </Box>
      </Box>
    );
  }

  if (screenState === 'error') {
    return (
      <Box flexDirection="column" padding={2}>
        <Box marginBottom={2}>
          <Text bold color="red">
            Error
          </Text>
        </Box>
        
        <Box marginBottom={2}>
          <Text color="red">
            {errorMessage}
          </Text>
        </Box>
        
        <Box flexDirection="row" gap={2}>
          <Text color="gray">
            [Esc] Back to File Picker
          </Text>
        </Box>
      </Box>
    );
  }

  return (
    <FilePicker
      onSubmit={handleFileSelect}
      onCancel={onBack}
      title="Select CSV File for Batch Download"
      placeholder="Enter path to CSV file..."
      fileFilter={(filename) => filename.endsWith('.csv')}
    />
  );
}; 