const express = require('express');
const sql = require('mssql');
const cors = require('cors');
const NodeCache = require('node-cache');
const fs = require('fs');
const path = require('path');
const pdfParse = require('pdf-parse');
const connectDB = require('../config/db');
const authRoutes = require("../src/routes/auth");
const LibraryRoute = require("../src/routes/Library");

const app = express();
app.use(cors());
app.use(express.json());
connectDB();

// Setting up routes
app.use("/api/auth", authRoutes);
app.use("/api/lib", LibraryRoute);

// SQL Database Configuration
const sqlconfig = {
    server: '192.168.2.5',
    database: 'ocr',
    user: 'sa',
    password: 'sa@12345',
    options: {
        encrypt: false,
        trustServerCertificate: true,
    },
    pool: {
        max: 10,
        min: 0,
        idleTimeoutMillis: 30000,
    },
    requestTimeout: 50000,
};

// Cache configuration
const searchCache = new NodeCache({
    stdTTL: 432000, // Cache persists for 5 days 
    checkperiod: 600 // Check expired keys every 10 minutes 
});

// Database connection pool
const pool = new sql.ConnectionPool(sqlconfig);
const poolConnect = pool.connect();

pool.on('error', (err) => {
    console.error('SQL Pool Error: ', err);
});

// Utility functions
const getRelativePath = (fullPath) => {
    const basePath = '\\\\192.168.2.5\\c$\\GGN Office Repository\\';
    return fullPath.startsWith(basePath)
        ? fullPath.slice(basePath.length).replace(/\\/g, '/')
        : fullPath.replace(/\\/g, '/');
};

const getSnippet = (text, keyword, contextLength = 100) => {
    const lowerText = text.toLowerCase();
    const lowerKeyword = keyword.toLowerCase();
    const startIndex = lowerText.indexOf(lowerKeyword);

    if (startIndex === -1) return null;

    const snippetStart = Math.max(0, startIndex - contextLength);
    const snippetEnd = Math.min(text.length, startIndex + keyword.length + contextLength);
    const snippet = text.substring(snippetStart, snippetEnd);

    return `...${snippet}...`;
};

const extractKeywordMatches = async (pdfPath, keywords) => {
    try {
        if (!fs.existsSync(pdfPath)) {
            console.warn(`File not found: ${pdfPath}`);
            return [];
        }

        const dataBuffer = fs.readFileSync(pdfPath);

        if (path.extname(pdfPath).toLowerCase() !== '.pdf') {
            console.warn(`File is not a PDF: ${pdfPath}`);
            return [];
        }

        const pdfData = await pdfParse(dataBuffer);
        const matches = [];
        const pages = pdfData.text.split('\n\n'); // Assuming pages are split by \n\n

        for (let pageIndex = 0; pageIndex < pages.length; pageIndex++) {
            const pageText = pages[pageIndex];

            keywords.forEach(keyword => {
                if (pageText.toLowerCase().includes(keyword.toLowerCase())) {
                    const snippet = getSnippet(pageText, keyword, 100);
                    matches.push({
                        page: pageIndex, // Change to 0-based index
                        keyword,
                        snippet,
                    });
                }
            });
        }

        return matches;
    } catch (error) {
        console.error(`Error extracting keyword matches from: ${pdfPath}`, error);
        return [];
    }
};

// Search endpoint
app.get('/search', async (req, res) => {
    const { keyword, exclude } = req.query;

    if (!keyword || keyword.trim() === '') {
        return res.status(400).json({ error: 'No search keyword provided.' });
    }

    const cacheKey = `search:${keyword}:${exclude || 'noexclude'}`;
    const cachedResults = searchCache.get(cacheKey);

    if (cachedResults) {
        console.log(`Cache HIT for key: ${cacheKey}`);
        return res.json(cachedResults);
    }

    console.log(`Cache MISS for key: ${cacheKey}`);

    try {
        await poolConnect;
        const request = pool.request();
        const sanitizedKeyword = keyword.replace(/[^\w\s\+\-]/g, '').trim();
        const terms = sanitizedKeyword.split('+').map(term => term.trim()).filter(Boolean);
        
        let query = 'SELECT Filename FROM files WHERE PdfData LIKE @term0';
        request.input('term0', sql.NVarChar, `%${terms[0]}%`);

        terms.slice(1).forEach((term, index) => {
            query += ` AND PdfData LIKE @term${index + 1}`;
            request.input(`term${index + 1}`, sql.NVarChar, `%${term}%`);
        });

        if (exclude && exclude.trim() !== '') {
            const excludedWords = exclude.split(/\s+/).filter(Boolean);
            excludedWords.forEach((word, index) => {
                const sanitizedExclude = word.replace(/[^\w\s]/g, '').trim();
                if (sanitizedExclude) {
                    query += ` AND PdfData NOT LIKE @exclude${index}`;
                    request.input(`exclude${index}`, sql.NVarChar, `%${sanitizedExclude}%`);
                }
            });
        }

        console.log('Executing SQL Query:', query);
        const result = await request.query(query);
        const responseData = [];

        for (const record of result.recordset) {
            const fullPath = record.Filename;
            if (path.extname(fullPath).toLowerCase() !== '.pdf') {
                console.log(`Skipping non-PDF file: ${fullPath}`);
                continue;
            }

            const relativePath = getRelativePath(fullPath);
            const absolutePath = path.join('\\\\192.168.2.5\\c$\\GGN Office Repository', relativePath);
            const matches = await extractKeywordMatches(absolutePath, terms);

            responseData.push({
                Filename: fullPath,
                relativePath,
                matches,
            });
        }

        searchCache.set(cacheKey, responseData);
        res.json(responseData);

    } catch (error) {
        console.error('Error processing search:', error);
        res.status(500).json({ error: 'An error occurred while processing your search.' });
    }
});

// File serving endpoint with directory handling
app.get('/files/*', (req, res) => {
    const relativePath = req.params[0];
    const basePath = '\\\\192.168.2.5\\c$\\GGN Office Repository';
    const filePath = path.join(basePath, relativePath);

    fs.stat(filePath, (err, stats) => {
        if (err) {
            console.error(`Error accessing path: ${filePath}`, err);
            return res.status(404).json({ error: 'Path not found or inaccessible' });
        }

        if (stats.isDirectory()) {
            // If it's a directory, read the directory and list the contents
            fs.readdir(filePath, (err, files) => {
                if (err) {
                    console.error(`Error reading directory: ${filePath}`, err);
                    return res.status(500).json({ error: 'Unable to read directory' });
                }

                // Create a list of file links
                const fileLinks = files.map(file => {
                    return `<li><a href="/files/${path.join(relativePath, file).replace(/\\/g, '/')}">${file}</a></li>`;
                }).join('');
                
                // Serve the directory contents
                res.send(`
                    <h1>Contents of ${relativePath}</h1>
                    <ul>${fileLinks}</ul>
                `);
            });
        } else if (stats.isFile()) {
            // If it's a file, send the file to the client
            res.sendFile(filePath, (err) => {
                if (err) {
                    console.error(`Error sending file: ${filePath}`, err);
                    res.status(500).json({ error: 'Error sending file' });
                }
            });
        } else {
            res.status(400).json({ error: 'Neither a file nor a directory' });
        }
    });
});

// Start the server
const PORT = process.env.PORT || 3006;
app.listen(PORT, '192.168.7.221', () => {
    console.log(`Server running at http://192.168.7.221:${PORT}`);
});                    in this node cache is working    for like Ankita....but if i type ankita it is not returing the rseults .....so make the node cahache case insensitive