import { Injectable, Logger } from '@nestjs/common';
import { Pinecone } from '@pinecone-database/pinecone';
import { OpenAI } from 'openai';
import { GoogleGenerativeAI } from '@google/generative-ai';
import * as fs from 'fs';
import * as path from 'path';
import pdf from 'pdf-parse';
// import {pd} from 'pdf-parse';
import axios from 'axios';
import * as XLSX from 'xlsx';
import { PDFLoader } from '@langchain/community/document_loaders/fs/pdf';
import { RecursiveCharacterTextSplitter } from 'langchain/text_splitter';
import * as cheerio from 'cheerio';

@Injectable()
export class PineconeService {
  private genAI: GoogleGenerativeAI;
  private pineconeClient: Pinecone;
  private openaiClient: OpenAI;
  private prompt: any = `
   You are a knowledgeable and friendly assistant who explains complex topics in a simple, engaging way for a non-technical audience.
    You are given a reference context that may contain relevant information for answering the user's question.
    Provide a complete and clear answer, using relatable examples and analogies where possible to make the response easy to understand.

    If you cannot find relevant information in the provided context to answer the question,
    please apologize politely and explain that you don't have enough information to provide a proper answer.
    If the context is helpful, integrate it into your answer and summarize it and don't give the context in the answer; if it is not directly relevant, feel free to exclude it.`;

  //     `You are a knowledgeable and friendly assistant who explains complex topics in a simple, engaging way for a non-technical audience.
  //                 You are given a reference context that may contain relevant information for answering the user's question.
  //                 Provide a complete and clear answer, using relatable examples and analogies where possible to make the response easy to understand.

  //                 If you cannot find relevant information in the provided context to answer the question,
  //                 please apologize politely and explain that you don't have enough information to provide a proper answer.
  //                 If the context is helpful, integrate it into your answer; if it is not directly relevant, feel free to exclude it.
  // `

  constructor() {
    this.pineconeClient = new Pinecone({
      apiKey: process.env.PINECONE_API_KEY,
    });

    this.openaiClient = new OpenAI({
      apiKey: process.env.OPENAI_API_KEY,
    });

    this.genAI = new GoogleGenerativeAI(process.env.GOOGLE_API_KEY);
  }

  async addDocument(vectors: any) {
    try {
      const index = this.pineconeClient.Index(process.env.PINECONE_INDEX_NAME);
      const res = await index.upsert(vectors);
      // console.log('res', res);
      return true;
    } catch (error) {
      throw error;
    }
  }

  async embedContent(content: string) {
    try {
      // For embeddings, use the Text Embeddings model
      const model = this.genAI.getGenerativeModel({
        model: 'text-embedding-004',
      });

      //   const text = 'The quick brown fox jumps over the lazy dog.';
      content = content.trim().replace(/\s+/g, ' ');
      const result = await model.embedContent(content);
      const embedding = result.embedding;
      //   console.log(embedding.values.length);
      return embedding.values;
    } catch (error) {
      throw error;
    }
  }

  async getDocument(query: string) {
    try {
      // Convert content to an embedding
      const vector = await this.embedContent(query);
      // Upsert the document embedding into Pinecone
      const index = this.pineconeClient.Index(process.env.PINECONE_INDEX_NAME);
      const res = await index.query({
        vector: vector,
        topK: 5,

        includeMetadata: true,
        // includeValues: true,
        // filter: { genre: { '$eq': 'action' }
      });
      //   let context = '';
      //   for (let i = 0; i < res.matches.length; i++) {
      //     const element = res.matches[i];
      //     context += element.metadata.content;
      //   }
      //   console.log('res', res);
      for (let i = 0; i < res.matches.length; i++) {
        const element = res.matches[i];
        console.log('element', i, 'index', element.score);
      }
      const context = res.matches
        .map((match) => match.metadata.text)
        .join('\n\n');
      return context;
    } catch (error) {
      throw error;
    }
  }

  // async pdfUpload(file: any) {
  //   let folder_path = path.resolve(
  //     __dirname + '/assets/all_pdfs/' + file.originalname
  //   );
  //   try {
  //     fs.writeFileSync(folder_path, file.buffer);

  //     const splitDocs = await this.splitDocs(folder_path);
  //     const batchSize = 1;

  //     for (let i = 0; i < splitDocs.length; i += batchSize) {
  //       const batch = splitDocs.slice(i, i + batchSize);
  //       console.log('batch', batch.length);
  //       const vectors = await Promise.all(
  //         batch.map(async (doc) => {
  //           let content = doc.pageContent.trim().replace(/\s+/g, ' ');
  //           const embedding = await this.embedContent(content);
  //           // console.log("embedding.values.length",embedding);

  //           return {
  //             id: `docs ${+new Date()}`,
  //             values: embedding,
  //             metadata: {
  //               text: content,
  //               // source: doc.metadata.source,
  //               page: doc.metadata.page,
  //             },
  //           };
  //         })
  //       );
  //       console.log('vectors', vectors.length);

  //       this.addDocument(vectors);
  //     }
  //     fs.unlinkSync(folder_path);
  //     return splitDocs;
  //     // const pdfData = await pdf(file.buffer);
  //     // // console.log("pdfData", pdfData);clear

  //     // let string1= await this.trimToMaxWords(pdfData.text)
  //     // await this.addDocument(string1)
  //     // return pdfData.text
  //   } catch (error) {
  //     fs.unlinkSync(folder_path);

  //     throw error;
  //   }
  // }

  async pdfUpload(file: any) {
    let folder_path = path.resolve(
      __dirname + '/assets/all_pdfs/' + file.originalname
    );
    try {
      fs.writeFileSync(folder_path, file.buffer);

      const splitDocs = await this.splitDocs(folder_path);
      const batchSize = 1;
      let data_to_push = [];
      for (let i = 0; i < splitDocs.length; i++) {
        const batch = splitDocs.slice(i, i + batchSize);

        for (let index = 0; index < batch.length; index++) {
          const element = batch[index];
          data_to_push.push(this.addDataToPinecone(element.pageContent));
          if (data_to_push.length === 10) {
            let resolve = await Promise.all(data_to_push);
            // console.log('resolve', resolve[10]);
            data_to_push = [];
          }
        }
      }
      fs.unlinkSync(folder_path);
      return splitDocs;
    } catch (error) {
      fs.unlink(folder_path, () => {
        console.log('file deleted');
      });

      throw error;
    }
  }
  async splitDocs(folder_path: any) {
    try {
      const loader = new PDFLoader(folder_path);
      const docs = await loader.load();
      //   console.log("docs1", docs);
      const splitter = new RecursiveCharacterTextSplitter({
        chunkSize: 20000,
        chunkOverlap: 200,
      });
      const splitDocs = await splitter.splitDocuments(docs);
      console.log('splitDocs', splitDocs.length);
      console.log('splitDocs', splitDocs[0].pageContent.length);

      return splitDocs;
    } catch (error) {
      throw error;
    }
  }

  async getAnswer(query: string) {
    try {
      let context = await this.getDocument(query);
      console.log('context', context);

      //   const prompt = `
      //             ${this.prompt}
      //             QUESTION: ${query}
      //             CONTEXT: ${context}
      //             ANSWER:
      //           `.trim();

      const prompt1 = `
    Answer the following questions based on the information in this report:  ${context}\n 
    1.${query}
    
              `.trim();

      const prompt = `
    ${this.prompt}
    Context: ${context}
    
    Question: ${query}
              `.trim();

      // const prompt = (`
      // You are a knowledgeable and friendly assistant who explains complex topics in a simple, engaging way for a non-technical audience.
      // You are given a reference context that may contain relevant information for answering the user's question.
      // Provide a complete and clear answer, using relatable examples and analogies where possible to make the response easy to understand.

      // If you cannot find relevant information in the provided context to answer the question,
      // please apologize politely and explain that you don't have enough information to provide a proper answer.
      // If the context is helpful, integrate it into your answer; if it is not directly relevant, feel free to exclude it.

      //     QUESTION: ${query}
      //     CONTEXT: ${context}

      //     ANSWER:
      //   `).trim();

      const model = this.genAI.getGenerativeModel({
        model: 'gemini-1.5-flash',
      });
      const result = await model.generateContent(prompt);
      console.log(result.response.text());
      return {
        answer: result.response.text(),
      };
    } catch (error) {
      throw error;
    }
  }

  async trimToMaxWords(inputString, maxWords = 40000) {
    // // Split the input string by spaces (or other delimiters if needed)
    // const words = inputString.split(/\s+/);

    // // Take the first 200,000 words and join them back into a single string
    console.log('trimmedString', inputString.length);
    const trimmedString = inputString.slice(0, maxWords);
    console.log('trimmedString', trimmedString.length);

    return trimmedString;
  }

  async getPrompt() {
    return {
      prompt: this.prompt,
    };
  }

  async editPrompt(prompt: string) {
    this.prompt = prompt;
    return {
      prompt: this.prompt,
    };
  }

  async excelUpload(file: any) {
    let folder_path = path.resolve(
      __dirname + '/assets/all_excell/' + file.originalname
    );
    try {
      console.log('file', file);

      fs.writeFileSync(folder_path, file.buffer);
      const workbook = XLSX.readFile(folder_path);
      const sheetName = workbook.SheetNames[0];
      const sheet = workbook.Sheets[sheetName];
      const data = XLSX.utils.sheet_to_json(sheet);
      // console.log('data', data);
      let embeddings = [];
      for (const row of data) {
        const content = Object.entries(row)
          .map(([key, value]) => `${key}: "${value}"`)
          .join(', ');

        embeddings.push(content);
      }

      let data_to_push = [];

      for (let i = 0; i < embeddings.length; i++) {
        const content = embeddings[i];
        let embedding = this.addDataToPinecone(content);
        data_to_push.push(embedding);
        if (data_to_push.length === 10) {
          let resolve = await Promise.all(data_to_push);
          console.log('resolve', resolve[10]);
          data_to_push = [];
        }
        console.log('i', i);
      }

      fs.unlinkSync(folder_path);
      return data;
    } catch (error) {
      throw error;
    }
  }

  async addDataToPinecone(content: any) {
    try {
      content = content.trim().replace(/\s+/g, ' ');
      const embedding = await this.embedContent(content);
      let vector = [
        {
          id: `docs ${+new Date()}`,
          values: embedding,
          metadata: {
            text: content,
            // source: doc.metadata.source,
            // page: doc.metadata.page,
          },
        },
      ];
      this.addDocument(vector);
      return embedding;
    } catch (error) {
      throw error;
    }
  }

  async scrapAndUpload(url: string) {
    try {
      const { data: html } = await axios.get(url);
      const $ = cheerio.load(html);

      // Step 2: Extract text content (adjust selector as needed)
      const textContent = $('body').text().trim();
      console.log('textContent', textContent);

      let embeddings = this.getChunks(textContent);
      console.log('embeddings', embeddings.length);

      let data_to_push = [];

      for (let i = 0; i < embeddings.length; i++) {
        const content = embeddings[i];
        let embedding = this.addDataToPinecone(content);
        data_to_push.push(embedding);
        if (data_to_push.length === 10) {
          let resolve = await Promise.all(data_to_push);
          console.log('resolve', resolve[10]);
          data_to_push = [];
        }
        console.log('i', i);
      }

      // return await this.addDataToPinecone(textContent);
    } catch (error) {
      throw error;
    }
  }

  getChunks = (text: string): string[] => {
    const chunks: string[] = [];
    let start = 0;
    const maxChunkSize = 9000;
    while (start < text.length) {
      let end = start + maxChunkSize;

      // Ensure we don’t cut in the middle of a word
      if (end < text.length) {
        const lastSpace = text.lastIndexOf(' ', end);
        if (lastSpace > start) end = lastSpace;
      }

      chunks.push(text.slice(start, end));
      start = end + 1;
    }

    return chunks;
  };
}
