const db = require('../db/models');
const File_extractionsDBApi = require('../db/api/file_extractions');
const FilesDBApi = require('../db/api/files');
const processFile = require("../middlewares/upload");
const ValidationError = require('./notifications/errors/validation');
const csv = require('csv-parser');
const axios = require('axios');
const config = require('../config');
const stream = require('stream');
const fs = require('fs');
const path = require('path');
const { LocalAIApi } = require('../ai/LocalAIApi');
const pdf = require('pdf-parse');

module.exports = class File_extractionsService {
  static async create(data, currentUser) {
    const transaction = await db.sequelize.transaction();
    try {
      await File_extractionsDBApi.create(
        data,
        {
          currentUser,
          transaction,
        },
      );

      await transaction.commit();
    } catch (error) {
      await transaction.rollback();
      throw error;
    }
  };

  static async analyze(fileId, extractionType, currentUser) {
    const transaction = await db.sequelize.transaction();
    try {
      // 1. Fetch File Record
      const file = await FilesDBApi.findBy({ id: fileId }, { transaction });
      if (!file) {
        throw new ValidationError('File not found');
      }

      // 2. Resolve File Path
      // Assuming storage_key is the relative path in uploadDir
      // If storage_provider is 'local'
      let fileContent = '';
      
      if (file.storage_provider === 'local' || !file.storage_provider) {
         // storage_key usually stores 'folder/filename' or just 'filename' depending on implementation
         // In file.js uploadLocal: folder + filename.
         // Let's assume storage_key is the full relative path.
         const filePath = path.join(config.uploadDir, file.storage_key);
         
         if (!fs.existsSync(filePath)) {
             throw new ValidationError(`File not found on disk: ${filePath}`);
         }

         const ext = path.extname(filePath).toLowerCase();
         if (['.txt', '.md', '.csv', '.json', '.xml', '.html', '.js', '.ts'].includes(ext)) {
             fileContent = fs.readFileSync(filePath, 'utf8');
         } else if (ext === '.pdf') {
             const dataBuffer = fs.readFileSync(filePath);
             const data = await pdf(dataBuffer);
             fileContent = data.text;
         } else {
             throw new ValidationError(`Unsupported file type for analysis: ${ext}`);
         }
      } else {
          throw new ValidationError('Only local file storage is currently supported for analysis.');
      }

      if (!fileContent) {
          throw new ValidationError('File is empty.');
      }

      // 3. Construct AI Prompt
      let systemPrompt = "You are a legal AI assistant specialized in analyzing documents.";
      let userPrompt = "";

      switch (extractionType) {
          case 'summary':
              userPrompt = `Please provide a concise summary of the following document:\n\n${fileContent.substring(0, 20000)}`; // Truncate to avoid limit
              break;
          case 'dates': // Map to 'metadata' or 'summary' if 'dates' not in ENUM. ENUM has 'metadata', 'issues', 'citations', 'clauses'
              userPrompt = `Extract all key dates and deadlines from the following document. Format as a list:\n\n${fileContent.substring(0, 20000)}`;
              break;
          case 'entities':
              userPrompt = `Extract all key people, organizations, and legal entities mentioned in the following document:\n\n${fileContent.substring(0, 20000)}`;
              break;
          case 'issues':
              userPrompt = `Identify the key legal issues and disputes described in the following document:\n\n${fileContent.substring(0, 20000)}`;
              break;
          default:
              userPrompt = `Analyze the following document and extract key insights regarding ${extractionType}:\n\n${fileContent.substring(0, 20000)}`;
      }

      // 4. Call AI
      const aiResponse = await LocalAIApi.createResponse({
          input: [
              { role: 'system', content: systemPrompt },
              { role: 'user', content: userPrompt }
          ]
      });

      if (!aiResponse.success) {
          throw new Error(`AI Analysis failed: ${aiResponse.error}`);
      }

      const resultText = LocalAIApi.extractText(aiResponse);

      // 5. Save Result
      const extractionData = {
          fileId: file.id,
          extraction_type: extractionType,
          result_text: resultText,
          status: 'completed',
          started_at: new Date(),
          finished_at: new Date(),
          page_count: 1 // Placeholder
      };

      // Check if extraction exists to update or create? 
      // For now, create new.
      await File_extractionsDBApi.create(extractionData, { currentUser, transaction });

      await transaction.commit();
      return extractionData;

    } catch (error) {
      await transaction.rollback();
      throw error;
    }
  }

  static async bulkImport(req, res, sendInvitationEmails = true, host) {
    const transaction = await db.sequelize.transaction();

    try {
      await processFile(req, res);
      const bufferStream = new stream.PassThrough();
      const results = [];

      await bufferStream.end(Buffer.from(req.file.buffer, "utf-8")); // convert Buffer to Stream

      await new Promise((resolve, reject) => {
        bufferStream
          .pipe(csv())
          .on('data', (data) => results.push(data))
          .on('end', async () => {
            console.log('CSV results', results);
            resolve();
          })
          .on('error', (error) => reject(error));
      })

      await File_extractionsDBApi.bulkImport(results, {
          transaction,
          ignoreDuplicates: true,
          validate: true,
          currentUser: req.currentUser
      });

      await transaction.commit();
    } catch (error) {
      await transaction.rollback();
      throw error;
    }
  }

  static async update(data, id, currentUser) {
    const transaction = await db.sequelize.transaction();
    try {
      let file_extractions = await File_extractionsDBApi.findBy(
        {id},
        {transaction},
      );

      if (!file_extractions) {
        throw new ValidationError(
          'file_extractionsNotFound',
        );
      }

      const updatedFile_extractions = await File_extractionsDBApi.update(
        id,
        data,
        {
          currentUser,
          transaction,
        },
      );

      await transaction.commit();
      return updatedFile_extractions;

    } catch (error) {
      await transaction.rollback();
      throw error;
    }
  };

  static async deleteByIds(ids, currentUser) {
    const transaction = await db.sequelize.transaction();

    try {
      await File_extractionsDBApi.deleteByIds(ids, {
        currentUser,
        transaction,
      });

      await transaction.commit();
    } catch (error) {
      await transaction.rollback();
      throw error;
    }
  }

  static async remove(id, currentUser) {
    const transaction = await db.sequelize.transaction();

    try {
      await File_extractionsDBApi.remove(
        id,
        {
          currentUser,
          transaction,
        },
      );

      await transaction.commit();
    } catch (error) {
      await transaction.rollback();
      throw error;
    }
  }

  
};
