#include "databasemanager.h"
#include <QSqlQuery>
#include <QSqlError>
#include <QDebug>
#include <QVariant>
#include <QFileInfo>

DatabaseManager::DatabaseManager(QObject *parent)
    : QObject(parent)
    , m_initialized(false)
{
    // Initialize search cache
    m_searchCache.clear();
    
    // Create index on ocr_text if it doesn't exist
    // This will be executed once the database is initialized
}

DatabaseManager::~DatabaseManager()
{
    if (m_db.isOpen()) {
        m_db.close();
    }
}

bool DatabaseManager::initialize(const QString &dbPath)
{
    // Check if database is already initialized
    if (m_initialized) {
        return true;
    }

    // Check if file exists
    QFileInfo fileInfo(dbPath);
    if (!fileInfo.exists() || !fileInfo.isFile()) {
        qDebug() << "Database file does not exist:" << dbPath;
        return false;
    }

    // Set up database connection
    m_db = QSqlDatabase::addDatabase("QSQLITE");
    m_db.setDatabaseName(dbPath);

    // Open database
    if (!m_db.open()) {
        qDebug() << "Failed to open database:" << m_db.lastError().text();
        return false;
    }

    // Verify required table exists
    QSqlQuery query;
    if (!query.exec("SELECT name FROM sqlite_master WHERE type='table' AND name='ocr_results'")) {
        qDebug() << "Failed to execute query:" << query.lastError().text();
        m_db.close();
        return false;
    }
    
    if (!query.next()) {
        qDebug() << "The required table 'ocr_results' does not exist in the database.";
        m_db.close();
        return false;
    }

    // Verify the table has the required columns
    if (!query.exec("PRAGMA table_info(ocr_results)")) {
        qDebug() << "Failed to get table info:" << query.lastError().text();
        m_db.close();
        return false;
    }
    
    bool hasFullPath = false;
    bool hasOcrText = false;
    
    while (query.next()) {
        QString columnName = query.value(1).toString();
        if (columnName == "full_path") hasFullPath = true;
        if (columnName == "ocr_text") hasOcrText = true;
    }
    
    if (!hasFullPath || !hasOcrText) {
        qDebug() << "Missing required columns in ocr_results table. Need 'full_path' and 'ocr_text'";
        m_db.close();
        return false;
    }

    // Create an index on the ocr_text column if it doesn't exist
    // This will speed up text searches dramatically
    query.exec("CREATE INDEX IF NOT EXISTS idx_ocr_text ON ocr_results(ocr_text)");
    
    m_initialized = true;
    qDebug() << "Database initialized successfully.";
    return true;
}

QList<DatabaseManager::ImageItem> DatabaseManager::getAllImages()
{
    QList<ImageItem> images;
    
    if (!m_initialized) {
        qDebug() << "Database not initialized.";
        return images;
    }

    // Verify database is still connected
    if (!m_db.isOpen() && !m_db.open()) {
        qDebug() << "Database connection lost and cannot be reopened:" << m_db.lastError().text();
        m_initialized = false;
        return images;
    }
    
    // Start transaction to speed up query
    m_db.transaction();

    QSqlQuery query;
    query.prepare("SELECT full_path, ocr_text FROM ocr_results");
    
    if (!query.exec()) {
        qDebug() << "Failed to fetch images:" << query.lastError().text();
        return images;
    }

    // Check if files exist as we add them
    // Reserve space for results to avoid reallocations
    images.reserve(query.size() > 0 ? query.size() : 100);
    
    while (query.next()) {
        ImageItem item;
        item.filePath = query.value(0).toString();
        item.ocrText = query.value(1).toString();
        
        // Only add images that have a non-empty path
        if (!item.filePath.isEmpty()) {
            images.append(item);
        }
    }

    m_db.commit();
    return images;
}

QList<DatabaseManager::ImageItem> DatabaseManager::searchImages(const QString &searchText)
{
    QList<ImageItem> images;
    
    if (!m_initialized) {
        qDebug() << "Database not initialized.";
        return images;
    }

    // Verify database is still connected
    if (!m_db.isOpen() && !m_db.open()) {
        qDebug() << "Database connection lost and cannot be reopened:" << m_db.lastError().text();
        m_initialized = false;
        return images;
    }

    // If search text is empty, return all images
    if (searchText.isEmpty()) {
        // Clear the search cache when empty search is performed
        m_searchCache.clear();
        return getAllImages();
    }

    // Check if we have a cached result for this search query
    if (m_searchCache.contains(searchText)) {
        return m_searchCache[searchText];
    }

    // Start transaction to speed up queries
    m_db.transaction();

    QSqlQuery query;
    
    // Optimize the query based on length of search text
    if (searchText.length() <= 3) {
        // For short search terms, use a more targeted approach
        query.prepare("SELECT full_path, ocr_text FROM ocr_results WHERE ocr_text LIKE :search");
        query.bindValue(":search", "%" + searchText + "%");
    } else {
        // For longer search terms, use LIKE with a more specific pattern at start
        // which can utilize indexes better if they exist
        query.prepare("SELECT full_path, ocr_text FROM ocr_results WHERE ocr_text LIKE :search OR ocr_text LIKE :wordstart");
        query.bindValue(":search", "%" + searchText + "%");
        query.bindValue(":wordstart", "% " + searchText + "%");
    }
    
    if (!query.exec()) {
        qDebug() << "Failed to search images:" << query.lastError().text();
        m_db.rollback();
        return images;
    }

    // Reserve space for results to avoid reallocations
    images.reserve(query.size() > 0 ? query.size() : 100);

    while (query.next()) {
        ImageItem item;
        item.filePath = query.value(0).toString();
        item.ocrText = query.value(1).toString();
        
        // Only add images that have a non-empty path
        if (!item.filePath.isEmpty()) {
            images.append(item);
        }
    }

    m_db.commit();
    
    // Cache the result for future queries
    if (images.size() > 0) {
        m_searchCache.insert(searchText, images);
        
        // Limit cache size to avoid memory issues
        if (m_searchCache.size() > MAX_CACHE_SIZE) {
            // Remove the first key (oldest entry)
            if (!m_searchCache.isEmpty()) {
                m_searchCache.remove(m_searchCache.firstKey());
            }
        }
    }

    return images;
}