import { logger } from '../utils/logger';
import { DocumentProcessorServiceClient } from '@google-cloud/documentai';
import { Storage } from '@google-cloud/storage';
import { config } from '../config/env';
import pdf from 'pdf-parse';

interface ProcessingResult {
  success: boolean;
  content: string;
  metadata?: any;
  error?: string;
}

interface DocumentAIOutput {
  text: string;
  entities: Array<{
    type: string;
    mentionText: string;
    confidence: number;
  }>;
  tables: Array<any>;
  pages: Array<any>;
  mimeType: string;
}

interface PageChunk {
  startPage: number;
  endPage: number;
  buffer: Buffer;
}

export class DocumentAiGenkitProcessor {
  private gcsBucketName: string;
  private documentAiClient: DocumentProcessorServiceClient;
  private storageClient: Storage;
  private processorName: string;
  private readonly MAX_PAGES_PER_CHUNK = 30;

  constructor() {
    this.gcsBucketName = config.googleCloud.gcsBucketName;
    this.documentAiClient = new DocumentProcessorServiceClient();
    this.storageClient = new Storage();
    
    // Construct the processor name
    this.processorName = `projects/${config.googleCloud.projectId}/locations/${config.googleCloud.documentAiLocation}/processors/${config.googleCloud.documentAiProcessorId}`;
    
    logger.info('Document AI + Genkit processor initialized', {
      projectId: config.googleCloud.projectId,
      location: config.googleCloud.documentAiLocation,
      processorId: config.googleCloud.documentAiProcessorId,
      processorName: this.processorName,
      maxPagesPerChunk: this.MAX_PAGES_PER_CHUNK
    });
  }

  async processDocument(
    documentId: string, 
    userId: string, 
    fileBuffer: Buffer, 
    fileName: string, 
    mimeType: string
  ): Promise<ProcessingResult> {
    const startTime = Date.now();
    
    try {
      logger.info('Starting Document AI + Agentic RAG processing', { 
        documentId, 
        userId, 
        fileName,
        fileSize: fileBuffer.length,
        mimeType
      });

      // Step 1: Extract text using Document AI or fallback
      const extractedText = await this.extractTextFromDocument(fileBuffer, fileName, mimeType);
      
      if (!extractedText) {
        throw new Error('Failed to extract text from document');
      }

      logger.info('Text extraction completed', {
        textLength: extractedText.length
      });

      // Step 2: Process extracted text through Agentic RAG
      const agenticRagResult = await this.processWithAgenticRAG(documentId, extractedText);
      
      const processingTime = Date.now() - startTime;
      
      return {
        success: true,
        content: agenticRagResult.summary || extractedText,
        metadata: {
          processingStrategy: 'document_ai_agentic_rag',
          processingTime,
          extractedTextLength: extractedText.length,
          agenticRagResult,
          fileSize: fileBuffer.length,
          fileName,
          mimeType
        }
      };

    } catch (error) {
      const processingTime = Date.now() - startTime;
      const errorMessage = error instanceof Error ? error.message : String(error);
      const errorStack = error instanceof Error ? error.stack : undefined;
      const errorDetails = error instanceof Error ? {
        name: error.name,
        message: error.message,
        stack: error.stack
      } : {
        type: typeof error,
        value: error
      };
      
      logger.error('Document AI + Agentic RAG processing failed', { 
        documentId, 
        error: errorMessage,
        errorDetails,
        stack: errorStack,
        processingTime
      });

      return {
        success: false,
        content: '',
        error: `Document AI + Agentic RAG processing failed: ${errorMessage}`,
        metadata: {
          processingStrategy: 'document_ai_agentic_rag',
          processingTime,
          error: errorMessage,
          errorDetails,
          stack: errorStack
        }
      };
    }
  }

  private async extractTextFromDocument(fileBuffer: Buffer, fileName: string, mimeType: string): Promise<string> {
    try {
      // Check document size first
      const pdfData = await pdf(fileBuffer);
      const totalPages = pdfData.numpages;
      
      logger.info('PDF analysis completed', {
        totalPages,
        textLength: pdfData.text?.length || 0
      });

      // If document has more than 30 pages, use pdf-parse fallback
      if (totalPages > this.MAX_PAGES_PER_CHUNK) {
        logger.warn('Document exceeds Document AI page limit, using pdf-parse fallback', {
          totalPages,
          maxPagesPerChunk: this.MAX_PAGES_PER_CHUNK
        });
        
        return pdfData.text || '';
      }

      // For documents <= 30 pages, use Document AI
      logger.info('Using Document AI for text extraction', {
        totalPages,
        maxPagesPerChunk: this.MAX_PAGES_PER_CHUNK
      });

      // Upload file to GCS
      const gcsFilePath = await this.uploadToGCS(fileBuffer, fileName);
      
      // Process with Document AI
      const documentAiOutput = await this.processWithDocumentAI(gcsFilePath, mimeType);
      
      // Cleanup GCS file
      await this.cleanupGCSFiles(gcsFilePath);
      
      return documentAiOutput.text;

    } catch (error) {
      logger.error('Text extraction failed, using pdf-parse fallback', { 
        error: error instanceof Error ? error.message : String(error) 
      });
      
      // Fallback to pdf-parse
      try {
        const pdfData = await pdf(fileBuffer);
        return pdfData.text || '';
      } catch (fallbackError) {
        logger.error('Both Document AI and pdf-parse failed', { 
          originalError: error instanceof Error ? error.message : String(error),
          fallbackError: fallbackError instanceof Error ? fallbackError.message : String(fallbackError)
        });
        throw new Error('Failed to extract text from document using any method');
      }
    }
  }

  private async processWithAgenticRAG(documentId: string, extractedText: string): Promise<any> {
    try {
      logger.info('Processing extracted text with Agentic RAG', {
        documentId,
        textLength: extractedText.length
      });

      // Import and use the optimized agentic RAG processor
      logger.info('Importing optimized agentic RAG processor...');
      const { optimizedAgenticRAGProcessor } = await import('./optimizedAgenticRAGProcessor');
      
      logger.info('Agentic RAG processor imported successfully', {
        processorType: typeof optimizedAgenticRAGProcessor,
        hasProcessLargeDocument: typeof optimizedAgenticRAGProcessor?.processLargeDocument === 'function'
      });
      
      logger.info('Calling processLargeDocument...');
      const result = await optimizedAgenticRAGProcessor.processLargeDocument(
        documentId, 
        extractedText, 
        {}
      );

      logger.info('Agentic RAG processing completed', {
        success: result.success,
        summaryLength: result.summary?.length || 0,
        analysisDataKeys: result.analysisData ? Object.keys(result.analysisData) : [],
        resultType: typeof result
      });

      return result;

    } catch (error) {
      const errorMessage = error instanceof Error ? error.message : String(error);
      const errorStack = error instanceof Error ? error.stack : undefined;
      const errorDetails = error instanceof Error ? {
        name: error.name,
        message: error.message,
        stack: error.stack
      } : {
        type: typeof error,
        value: error
      };
      
      logger.error('Agentic RAG processing failed', {
        documentId,
        error: errorMessage,
        errorDetails,
        stack: errorStack
      });
      throw error;
    }
  }

  private async uploadToGCS(fileBuffer: Buffer, fileName: string): Promise<string> {
    try {
      const bucket = this.storageClient.bucket(this.gcsBucketName);
      const file = bucket.file(`uploads/${Date.now()}_${fileName}`);
      
      logger.info('Uploading file to GCS', { 
        fileName, 
        fileSize: fileBuffer.length,
        bucket: this.gcsBucketName,
        destination: file.name
      });

      await file.save(fileBuffer, {
        metadata: {
          contentType: 'application/pdf'
        }
      });

      logger.info('File uploaded successfully to GCS', { 
        gcsPath: `gs://${this.gcsBucketName}/${file.name}` 
      });

      return `gs://${this.gcsBucketName}/${file.name}`;
    } catch (error) {
      logger.error('Failed to upload file to GCS', { 
        fileName, 
        error: error instanceof Error ? error.message : String(error) 
      });
      throw error;
    }
  }

  private async processWithDocumentAI(gcsFilePath: string, mimeType: string): Promise<DocumentAIOutput> {
    try {
      logger.info('Processing with Document AI', { 
        gcsFilePath, 
        processorName: this.processorName,
        mimeType 
      });

      // Create the request
      const request = {
        name: this.processorName,
        rawDocument: {
          content: '', // We'll use GCS source instead
          mimeType: mimeType
        },
        gcsDocument: {
          gcsUri: gcsFilePath,
          mimeType: mimeType
        }
      };

      logger.info('Sending Document AI request', { 
        processorName: this.processorName,
        gcsUri: gcsFilePath 
      });

      // Process the document
      const [result] = await this.documentAiClient.processDocument(request);
      const { document } = result;

      if (!document) {
        throw new Error('Document AI returned no document');
      }

      logger.info('Document AI processing successful', {
        textLength: document.text?.length || 0,
        pagesCount: document.pages?.length || 0,
        entitiesCount: document.entities?.length || 0
      });

      // Extract text
      const text = document.text || '';

      // Extract entities
      const entities = document.entities?.map(entity => ({
        type: entity.type || 'UNKNOWN',
        mentionText: entity.mentionText || '',
        confidence: entity.confidence || 0
      })) || [];

      // Extract tables
      const tables = document.pages?.flatMap(page => 
        page.tables?.map(table => ({
          rows: table.headerRows?.length || 0,
          columns: table.bodyRows?.[0]?.cells?.length || 0
        })) || []
      ) || [];

      // Extract pages info
      const pages = document.pages?.map(page => ({
        pageNumber: page.pageNumber || 0,
        blocksCount: page.blocks?.length || 0
      })) || [];

      return {
        text,
        entities,
        tables,
        pages,
        mimeType: document.mimeType || mimeType
      };

    } catch (error) {
      logger.error('Document AI processing failed', { 
        gcsFilePath, 
        processorName: this.processorName,
        error: error instanceof Error ? error.message : String(error),
        stack: error instanceof Error ? error.stack : undefined
      });
      throw error;
    }
  }

  private async cleanupGCSFiles(gcsFilePath: string): Promise<void> {
    try {
      const bucketName = gcsFilePath.replace('gs://', '').split('/')[0];
      const fileName = gcsFilePath.replace(`gs://${bucketName}/`, '');
      
      logger.info('Cleaning up GCS files', { gcsFilePath, bucketName, fileName });
      
      const bucket = this.storageClient.bucket(bucketName);
      const file = bucket.file(fileName);
      
      await file.delete();
      
      logger.info('GCS file cleanup completed', { gcsFilePath });
    } catch (error) {
      logger.warn('Failed to cleanup GCS files', { 
        gcsFilePath, 
        error: error instanceof Error ? error.message : String(error) 
      });
      // Don't throw error for cleanup failures
    }
  }
}

export const documentAiGenkitProcessor = new DocumentAiGenkitProcessor(); 