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'); // For PDF text extraction
const authRoutes = require("../src/routes/auth")
const connectDB = require('../config/db')
const LibraryRoute = require("../src/routes/Library")
const app = express();
app.use(cors());
app.use(express.json());
app.use("/api/auth" , authRoutes);

app.use("/api/lib",  LibraryRoute);
connectDB();

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

const searchCache = new NodeCache({ 
  stdTTL: 432000,  
  checkperiod: 600 
});

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

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



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

// ** Helper Function: Extract keyword matches with page numbers **
const extractKeywordMatches = async (pdfPath, keyword) => {
    try {
        // Make sure the file exists before processing
        if (!fs.existsSync(pdfPath)) {
            console.warn(`File not found: ${pdfPath}`);
            return [];
        }

        const dataBuffer = fs.readFileSync(pdfPath);
        
        if (path.extname(pdfPath).toLowerCase() !== '.pdf') {
            // Return empty if the file is not a 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 separated by \n\n

        // Loop through pages and find keyword matches
        for (let pageIndex = 0; pageIndex < pages.length; pageIndex++) {
            const pageText = pages[pageIndex];
            if (pageText.toLowerCase().includes(keyword.toLowerCase())) {
                matches.push({
                    page: pageIndex, 
                });
            }
        }

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

// ** Endpoint: Search for PDFs by keywords **
app.get('/search', async (req, res) => {
    const { keyword, exclude } = req.query;

    // Log incoming request details
    console.log(`Search Request Received:`, { 
        keyword, 
        exclude, 
        timestamp: new Date().toISOString() 
    });

    if (!keyword || keyword.trim() === '') {
        console.warn('Search attempt with empty keyword');
        return res.status(400).json({ error: 'No search keyword provided.' });
    }

    // Create a consistent cache key
    const cacheKey = `search:${keyword}:${exclude || 'noexclude'}`;
    
    // Log cache key generation
    console.log(`Generated Cache Key: ${cacheKey}`);

    // Check cache first
    const cachedResults = searchCache.get(cacheKey);
    
    if (cachedResults) {
        console.log(`Cache HIT for key: ${cacheKey}`);
        console.log('Returning cached results:', {
            resultCount: cachedResults.length,
            cacheTimestamp: new Date().toISOString()
        });
        return res.json(cachedResults);
    }

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

    try {
        await poolConnect;
        const request = pool.request();
        let query;

        // Sanitize keyword and split by spaces and +
        const sanitizedKeyword = keyword.replace(/[^\w\s\+\-]/g, '').trim();
        if (!sanitizedKeyword) {
            console.warn('Invalid search keyword after sanitization');
            return res.status(400).json({ error: 'Invalid search keyword' });
        }

        // Split the keyword by '+' to treat it as boolean AND
        const terms = sanitizedKeyword.split('+').map(term => term.trim()).filter(Boolean);

        // Construct the SQL query to search for the keyword
        query = 'SELECT Filename FROM files WHERE PdfData LIKE @term0';
        request.input('term0', sql.NVarChar, `%${terms[0]}%`);

        // Add conditions for each additional term
        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 = [];

        console.log(`SQL Query returned ${result.recordset.length} records`);

        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.13.30\\c$\\GGN Office Repository', relativePath);

            const matches = await extractKeywordMatches(absolutePath, keyword);

            responseData.push({
                Filename: fullPath,
                relativePath,
                matches: matches.map(match => match.page),
            });
        }

        // Cache the results before sending
        console.log(`Caching results for key: ${cacheKey}`);
        console.log(`Cached results count: ${responseData.length}`);
        searchCache.set(cacheKey, responseData);

        console.log('Sending search results');
        res.json(responseData);
        

    } catch (error) {
        console.error('Error processing search:', {
            message: error.message,
            stack: error.stack,
            timestamp: new Date().toISOString()
        });
        res.status(500).json({ error: 'An error occurred while processing your search.' });
    }
});   
// ** Endpoint: Serve PDF files **
app.get('/files/*', (req, res) => {
    const relativePath = req.params[0];
    const basePath = '\\\\192.168.13.30\\c$\\GGN Office Repository';
    const filePath = path.join(basePath, relativePath);

    try {
        fs.accessSync(filePath, fs.constants.R_OK);
        res.sendFile(filePath);
    } catch (err) {
        console.error(`Error accessing file: ${filePath}`, err);
        res.status(404).json({ error: 'File not found or inaccessible' });
    }
});

// ** Endpoint: Test Database Connection **
app.get('/db-test', async (req, res) => {
    try {
        await pool.connect();
        res.status(200).json({ status: 'success', message: 'Database connection successful' });
    } catch (error) {
        console.error('Database connection error:', error.message);
        res.status(500).json({ status: 'error', message: 'Database connection failed' });
    }
});

// ** Endpoint: Add documents to user library  **
app.post('/api/lib/library/add', (req, res) => {
    const { filename, originalFilePath } = req.body;
    const token = req.headers.authorization?.split(' ')[1]; 

    if (!token) {
        return res.status(401).json({ error: 'Unauthorized access' });
    }

    try {
        // Add logic to verify user token and store entry in user library database
        // Example: Insert the file path into a user's "library" collection in the database
        res.status(201).json({ message: 'File added to library successfully', filename });
    } catch (error) {
        console.error('Error adding file to library:', error.message);
        res.status(500).json({ error: 'Failed to add file to library' });
    }
});


app.use((req, res) => {
    res.status(404).json({ status: 'error', message: 'Route not found' });
});

const PORT = process.env.PORT || 3006;
app.listen(PORT, '192.168.7.206', () => {
    console.log(`Server running on http://192.168.7.206:${PORT}`);
});

this is my server.js 
import React, { useState, useEffect } from 'react';
import Navbar from '../components/Navbar';
import Footer from '../components/Footer';
import { useNavigate } from 'react-router-dom';
import { useSearchContext } from '../context/searchcontext';
import './Home.css';

const Home = () => {
    const { searchResults, setSearchResults, addToSearchHistory } = useSearchContext();
    const navigate = useNavigate();
    const [searchTerm, setSearchTerm] = useState('');
    const [excludeWords, setExcludeWords] = useState('');
    const [error, setError] = useState(null);
    const [isLoading, setIsLoading] = useState(false);
    const [isLoggedIn, setIsLoggedIn] = useState(false);
    const [selectedPdf, setSelectedPdf] = useState(null);

    useEffect(() => {
        const token = localStorage.getItem('token');
        setIsLoggedIn(!!token);
    }, []);

    const handleSearchTermChange = (event) => {
        setSearchTerm(event.target.value);
    };

    const handleExcludeWordsChange = (event) => {
        setExcludeWords(event.target.value);
    };

    const handleAddToLibrary = async (pdf) => {
        const token = localStorage.getItem('token');
        if (!token) {
            navigate('/login', {
                state: {
                    message: 'Please log in to add documents to your library',
                    redirectAfterLogin: '/home',
                },
            });
            return;
        }

        try {
            const response = await fetch('http://192.168.7.206:3006/api/lib/library/add', {
                method: 'POST',
                headers: {
                    'Content-Type': 'application/json',
                    'Authorization': `Bearer ${token}`,
                },
                body: JSON.stringify({
                    filename: pdf.relativePath,
                    originalFilePath: pdf.Filename,
                }),
            });

            if (response.ok) {
                alert('Document added to library');
            } else {
                const errorData = await response.json();
                alert(errorData.message || 'Failed to add document');
            }
        } catch (error) {
            console.error('Error adding to library:', error);
            alert('Network error. Please try again.');
        }
    };

    const handleSubmit = async (event) => {
        event.preventDefault();
        setIsLoading(true);
        setError(null);

        try {
            const response = await fetch(
                `http://192.168.7.206:3006/search?keyword=${encodeURIComponent(searchTerm)}&exclude=${encodeURIComponent(excludeWords || '')}`
            );

            if (!response.ok) {
                throw new Error(`Network response was not ok: ${response.status}`);
            }

            const data = await response.json();
            setSearchResults(data);
            setError(null);
            addToSearchHistory(searchTerm, data.length);
        } catch (error) {
            console.error('Error fetching search results:', error);
            setError(`Failed to fetch search results: ${error.message}`);
        } finally {
            setIsLoading(false);
        }
    };

    // Function to view PDF based on search result
    const handlePdfView = (result) => {
        const relativePath = result.relativePath;
        const firstPage = result.matches[0]; // Navigate to the first matching page
        const keyword = searchTerm || ""; // Use the search term for highlighting

        // Construct the URL for the PDF file
        const pdfUrl = `http://192.168.7.206:3006/files/${encodeURIComponent(relativePath)}#page=${firstPage}&zoom=100&search=${encodeURIComponent(keyword)}`;

        // Open the PDF in a new tab
        window.open(pdfUrl, '_blank');
    };

    // Function to handle PDF page redirection when a page number is clicked
    const handlePageRedirect = (pageNumber, relativePath) => {
        const pdfUrl = `http://192.168.7.206:3006/files/${encodeURIComponent(relativePath)}#page=${pageNumber}`;
        window.open(pdfUrl, '_blank');
    };

    // Render the search results
    const renderResults = () => {
        if (!searchResults.length) {
            return <p>No results found</p>;
        }

        return (
            <div className="scrollable-container">
                <p>{searchResults.length} document(s) found:</p>
                <ul style={{ listStyleType: 'none', padding: 0 }}>
                    {searchResults.map((result, index) => {
                        const relativePath = result.relativePath;
                        const pages = result.matches; // All pages where the keyword is found

                        return (
                            <li key={index} style={{ margin: '5px 8px' }}>
                                <span
                                    style={{
                                        fontWeight: 'normal',
                                        color: 'blue',
                                        cursor: 'pointer',
                                        textDecoration: 'underline',
                                    }}
                                    onClick={() => handlePdfView(result)}
                                >
                                    {relativePath}
                                </span>
                                {pages.length > 0 && (
                                    <span style={{ color: 'gray' }}>
                                        &nbsp; (Page no.:{''}
                                        {pages.map((page, i) => (
                                            <span key={i}>
                                                <a
                                                    href="#"
                                                    onClick={() => handlePageRedirect(page, relativePath)}
                                                    style={{ color: 'blue', textDecoration: 'underline' }}
                                                >
                                                    {page}
                                                </a>
                                                {i < pages.length - 1 && ','}
                                            </span>
                                        ))}
                                        )
                                    </span>
                                )}
                                <span
                                    className="add-to-library"
                                    onClick={() => handleAddToLibrary(result)}
                                    title="Add to Library"
                                    style={{
                                        marginLeft: '10px',
                                        cursor: 'pointer',
                                        color: 'green',
                                        fontSize: '20px',
                                        display: 'inline-block',
                                    }}
                                >
                                    <button className="add-button">Add</button>
                                </span>
                            </li>
                        );
                    })}
                </ul>
            </div>
        );
    };

    return (
        <div className="app-container">
            <Navbar />
            <main>
                <form onSubmit={handleSubmit}>
                    <div className="search-input">
                        <input
                            type="text"
                            placeholder="Enter text"
                            value={searchTerm}
                            onChange={handleSearchTermChange}
                            required
                        />
                        <input
                            type="text"
                            placeholder="Exclude these words (Optional)"
                            value={excludeWords}
                            onChange={handleExcludeWordsChange}
                        />
                        <button type="submit">Search</button>
                    </div>
                </form>

                {isLoading && <p>Searching...</p>}
                {error && <p style={{ color: 'red' }}>{error}</p>}
                <div>{renderResults()}</div>
            </main>
            <Footer />
        </div>
    );
};

export default Home;
this is my home.js ....i want the srached keyword to be highlightred also ....tell me ways of doing that ffrom best to oworst approach tehn i will decide how to proceed next