#include "mainwindow.h"
#include "settingsdialog.h"
#include <QIcon>
#include <QApplication>
#include <QScreen>
#include <QResizeEvent>
#include <QFileInfo>
#include <QMessageBox>
#include <QInputDialog>
#include <QMenuBar>
#include <QMenu>
#include <QAction>
#include <QFileDialog>
#include <QDesktopServices>
#include <QSettings>
#include <QStandardPaths>
#include <stdexcept>

// Define settings file path
const QString MainWindow::CONFIG_FILE_PATH = QDir::homePath() + "/.config/ScreenshotOCRGallery/settings.ini";

MainWindow::MainWindow(QWidget *parent)
    : QMainWindow(parent)
    , m_dbManager(new DatabaseManager(this))
    , m_typingTimer(new QTimer(this))
    , m_searchingAnimationTimer(new QTimer(this))
    , m_searchingDots(1)
    , m_isTyping(false)
    , m_hasValidDatabase(false)
    , m_databasePath("")
    , m_screenshotsDir("")
    , m_imagePreloadCount(DEFAULT_PRELOAD_COUNT)
{
    // Set window title and size
    setWindowTitle(tr("Screenshot OCR Gallery"));
    resize(DEFAULT_WINDOW_WIDTH, DEFAULT_WINDOW_HEIGHT);
    
    // Set window icon
    QIcon appIcon;
    appIcon.addFile(":/icons/orcs-gallery-64.png", QSize(64, 64));
    appIcon.addFile(":/icons/orcs-gallery-128.png", QSize(128, 128));
    appIcon.addFile(":/icons/orcs-gallery-256.png", QSize(256, 256));
    appIcon.addFile(":/icons/orcs-gallery-512.png", QSize(512, 512));
    setWindowIcon(appIcon);
    
    // Remove fixed minimum size to allow for single column layout at any width
    // setMinimumSize(640, 480);
    
    // Center window on screen
    QScreen *screen = QGuiApplication::primaryScreen();
    if (screen) {
        const QRect availableGeometry = screen->availableGeometry();
        const QSize size = geometry().size();
        move((availableGeometry.width() - size.width()) / 2,
             (availableGeometry.height() - size.height()) / 2);
    }
    
    // Load settings first
    loadSettings();
    
    // Set up UI
    createLayout();

    // Apply loaded preload count to the gallery (createLayout uses the hardcoded default)
    m_imageGallery->setPageSize(m_imagePreloadCount);
    
    // Initialize typing inactivity timer
    m_typingTimer->setSingleShot(true);
    m_typingTimer->setInterval(500); // 500ms of no typing before searching
    connect(m_typingTimer, &QTimer::timeout, this, &MainWindow::handleTypingInactivityTimeout);
    
    // Only create animation timer for searching visual feedback
    
    m_searchingAnimationTimer = new QTimer(this);
    m_searchingAnimationTimer->setInterval(200); // Animation speed for searching dots
    m_searchingDots = 1;
    connect(m_searchingAnimationTimer, &QTimer::timeout, this, [this]() {
        m_searchingDots = (m_searchingDots % 3) + 1; // Cycle between 1, 2, and 3 dots
        QString dots;
        for(int i = 0; i < m_searchingDots; ++i) dots += ".";
        QString searchingIndicator = QString("%1 %2").arg(tr("Searching")).arg(dots);
        statusBar()->showMessage(searchingIndicator);
    });
    
    // Initialize database and display images
    initializeDatabase();
    
    // Connect database signals for status updates
    if (m_dbManager) {
        connect(m_dbManager, &DatabaseManager::searchStarted, 
                this, [this](const QString &text) {
                    statusBar()->showMessage(tr("Searching..."), 1000);
                });
        
        connect(m_dbManager, &DatabaseManager::searchResultsReady,
                this, [this](const QList<DatabaseManager::ImageItem> &results, const QString &text,
                           int offset, int limit, int totalCount) {
                    if (text.isEmpty()) {
                        statusBar()->showMessage(tr("Showing all images (paginated) - %1 total").arg(totalCount));
                    } else {
                        statusBar()->showMessage(tr("Found %1 results for \"%2\" (showing %3 to %4)")
                                                .arg(totalCount)
                                                .arg(text)
                                                .arg(offset - results.size() + 1)
                                                .arg(offset), 3000);
                    }
                });
    }
    
    displayAllImages();
    
    // Set focus to search bar and disable autocomplete which can cause UI lag
    m_searchBar->setFocus();
    m_searchBar->setAutoFillBackground(true);
    m_searchBar->setAttribute(Qt::WA_MacShowFocusRect, false); // Reduce drawing overhead
    
    // Set status bar
    statusBar()->showMessage(tr("Ready"));
    updateStatusBar();
}

MainWindow::~MainWindow()
{
    // All QObjects will be automatically deleted via parent-child relationship
}

void MainWindow::createLayout()
{
    // Create central widget
    m_centralWidget = new QWidget(this);
    setCentralWidget(m_centralWidget);
    
    // Create main layout
    m_mainLayout = new QVBoxLayout(m_centralWidget);
    m_mainLayout->setSpacing(5);  // Reduce spacing between elements
    m_mainLayout->setContentsMargins(5, 5, 5, 5);  // Reduce margins
    
    // Title removed - now shown only in the title bar
    
    // Create menu bar
    QMenuBar *menuBar = new QMenuBar(this);
    setMenuBar(menuBar);
    
    // Create File menu
    QMenu *fileMenu = menuBar->addMenu(tr("&File"));
    
    // Add Open action
    QAction *openAction = new QAction(tr("&Open"), this);
    openAction->setShortcut(QKeySequence::Open);
    connect(openAction, &QAction::triggered, this, &MainWindow::handleOpenFile);
    fileMenu->addAction(openAction);
    
    // Add Open With action
    QAction *openWithAction = new QAction(tr("Open &With..."), this);
    connect(openWithAction, &QAction::triggered, this, &MainWindow::handleOpenFileWith);
    fileMenu->addAction(openWithAction);
    
    // Add separator
    fileMenu->addSeparator();
    
    // Add Settings action
    QAction *settingsAction = new QAction(tr("&Settings"), this);
    connect(settingsAction, &QAction::triggered, this, &MainWindow::handleSettings);
    fileMenu->addAction(settingsAction);

    // Add Update Database action
    QAction *updateDbAction = new QAction(tr("&Update Database"), this);
    updateDbAction->setToolTip(tr("Run OCR on new screenshots and update the database"));
    connect(updateDbAction, &QAction::triggered, this, &MainWindow::handleUpdateDatabase);
    fileMenu->addAction(updateDbAction);

    // Add separator
    fileMenu->addSeparator();
    
    // Add Quit action
    QAction *quitAction = new QAction(tr("&Quit"), this);
    quitAction->setShortcut(QKeySequence::Quit);
    connect(quitAction, &QAction::triggered, qApp, &QApplication::quit);
    fileMenu->addAction(quitAction);
    
    // Create search bar
    m_searchBar = new QLineEdit(this);
    m_searchBar->setPlaceholderText(tr("Search OCR text..."));
    m_searchBar->setClearButtonEnabled(true);
    
    // Create gallery widget
    m_imageGallery = new ImageGallery(this);
    m_imageGallery->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
    
    // Add widgets to layout
    m_mainLayout->addWidget(m_searchBar);
    m_mainLayout->addWidget(m_imageGallery, 1);
    
    // Connect signals - use textEdited instead of textChanged to handle only user input
    connect(m_searchBar, &QLineEdit::textEdited, this, &MainWindow::handleSearchTextChanged);
}

void MainWindow::initializeDatabase()
{
    // Ensure the parent directory exists
    QFileInfo dbFileInfo(m_databasePath);
    QDir dbDir = dbFileInfo.absoluteDir();
    if (!dbDir.exists()) {
        if (!dbDir.mkpath(".")) {
            QString errorMsg = tr("Could not create database directory: %1").arg(dbDir.absolutePath());
            statusBar()->showMessage(errorMsg, 10000);
            QMessageBox::critical(this, tr("Database Error"), errorMsg);
            m_hasValidDatabase = false;
            return;
        }
        qDebug() << "Created database directory:" << dbDir.absolutePath();
    }

    // Create the database with the required schema if it does not exist yet
    if (!dbFileInfo.exists() || !dbFileInfo.isFile()) {
        qDebug() << "Database not found – creating new database at:" << m_databasePath;
        if (!DatabaseManager::createDatabase(m_databasePath)) {
            QString errorMsg = tr("Could not create database: %1").arg(m_databasePath);
            statusBar()->showMessage(errorMsg, 10000);
            QMessageBox::critical(this, tr("Database Error"), errorMsg);
            m_hasValidDatabase = false;
            return;
        }
        statusBar()->showMessage(tr("Created new database at %1").arg(m_databasePath), 5000);
    }
    
    // Try to initialize database
    if (!m_dbManager->initialize(m_databasePath)) {
        QString errorMsg = tr("Failed to connect to database");
        statusBar()->showMessage(errorMsg, 10000);
        QMessageBox::warning(this, tr("Database Error"), 
                             tr("Failed to connect to database: %1\nThe application will continue with limited functionality.")
                             .arg(m_databasePath));
        qDebug() << "Failed to initialize database";
        m_hasValidDatabase = false;
    } else {
        statusBar()->showMessage(tr("Connected to database"), 3000);
        m_imageGallery->setDatabaseManager(m_dbManager);
        m_hasValidDatabase = true;
    }
}

void MainWindow::displayAllImages()
{
    if (!m_hasValidDatabase) {
        m_imageGallery->clearGallery();
        statusBar()->showMessage(tr("No database connection available. Cannot display images."), 5000);
        return;
    }
    
    // Get total image count first
    int totalImageCount = m_dbManager->getImageCount();
    
    // Get the first page of images with pagination
    QList<DatabaseManager::ImageItem> allImages = m_dbManager->getAllImages(0, m_imagePreloadCount);
    if (allImages.isEmpty()) {
        statusBar()->showMessage(tr("No images found in database"), 5000);
    } else {
        statusBar()->showMessage(tr("Loaded %1 of %2 images").arg(allImages.size()).arg(totalImageCount), 3000);
    }
    
    // Clear existing images and display first page
    bool hasMoreImages = (totalImageCount > allImages.size());
    m_imageGallery->displayImages(allImages, true);
    
    // ALWAYS force update to ensure Load More button appears correctly
    // This is critical for when we reset the search
    m_imageGallery->handleSearchResults(allImages, "", 0, m_imagePreloadCount, totalImageCount);
    
    updateStatusBar();
}

void MainWindow::handleSearchTextChanged()
{
    if (!m_hasValidDatabase) {
        statusBar()->showMessage(tr("Database not available for search"), 3000);
        return;
    }
    
    // Capture current text
    QString currentText = m_searchBar->text();
    
    // If search bar is cleared, immediately show all images
    if (currentText.isEmpty()) {
        m_lastSearchText.clear();
        m_typingTimer->stop();
        // No typing animation to stop
        m_searchingAnimationTimer->stop();
        m_isTyping = false;
        // No typing dots to reset
        m_searchingDots = 1;
        
        // Show loading status
        statusBar()->showMessage(tr("Loading gallery..."));
        
        // First, explicitly reset current offset in gallery 
        m_imageGallery->resetPagination();
        
        // Clear gallery and load first page
        displayAllImages(); // Show all images immediately
        return; // Skip the timer since we've already updated
    }
    
    // Update last search text
    m_lastSearchText = currentText;
    
    // Indicate user is typing and reset the inactivity timer
    m_isTyping = true;
    m_typingTimer->start();
    
    // Don't show any typing indicator in the status bar
    // Only update the search text display without animation
    
    // Update the search text display but don't search yet
    m_imageGallery->updateSearchTextDisplay(m_lastSearchText);
}

void MainWindow::handleTypingInactivityTimeout()
{
    // User has stopped typing for 500ms, perform the search
    m_isTyping = false;
    
    // Start search animation directly without typing animation
    
    // Update the status bar with search indicator
    QString dots;
    for(int i = 0; i < m_searchingDots; ++i) dots += ".";
    QString searchingIndicator = QString("%1 %2").arg(tr("Searching")).arg(dots);
    statusBar()->showMessage(searchingIndicator);
    
    // Start the searching animation
    if (!m_searchingAnimationTimer->isActive()) {
        m_searchingAnimationTimer->start();
    }
    
    // Actually perform the search
    performSearch();
}

void MainWindow::performSearch()
{
    if (!m_hasValidDatabase || m_isTyping) {
        // Don't search if database is not available or if user is still typing
        return;
    }
    
    try {
        // Clear the gallery and prepare for new search with pagination
        m_imageGallery->clearGallery();
        
        // Directly pass the search to the image gallery
        // The gallery will handle the threaded search operation with pagination
        m_imageGallery->handleSearchTextChanged(m_lastSearchText);
        updateStatusBar();
        
        // Show searching status
        statusBar()->showMessage(tr("Searching..."), 1000);
    } catch (const std::exception &e) {
        QString errorMsg = tr("Search error: %1").arg(e.what());
        statusBar()->showMessage(errorMsg, 5000);
        qDebug() << errorMsg;
    }
}

// File menu action handlers
void MainWindow::handleOpenFile()
{
    QString filePath = QFileDialog::getOpenFileName(this,
        tr("Open Image File"), m_screenshotsDir,
        tr("Image Files (*.png *.jpg *.jpeg *.bmp *.gif)"));
    
    if (!filePath.isEmpty()) {
        QDesktopServices::openUrl(QUrl::fromLocalFile(filePath));
    }
}

void MainWindow::handleOpenFileWith()
{
    QString filePath = QFileDialog::getOpenFileName(this,
        tr("Open Image File"), m_screenshotsDir,
        tr("Image Files (*.png *.jpg *.jpeg *.bmp *.gif)"));
    
    if (!filePath.isEmpty()) {
        // Get the program to open the file with
        bool ok;
        QString program = QInputDialog::getText(this, tr("Open With"),
                                              tr("Enter program name:"), QLineEdit::Normal,
                                              "", &ok);
        if (ok && !program.isEmpty()) {
            QProcess *process = new QProcess(this);
            connect(process, QOverload<int, QProcess::ExitStatus>::of(&QProcess::finished),
                    process, &QProcess::deleteLater);
            
            process->start(program, QStringList() << filePath);
        }
    }
}

void MainWindow::handleSettings()
{
    SettingsDialog settingsDialog(this);
    
    // Show the settings dialog
    if (settingsDialog.exec() == QDialog::Accepted) {
        // Save and apply new settings
        m_databasePath = settingsDialog.getDatabasePath();
        m_screenshotsDir = settingsDialog.getScreenshotsDir();
        m_imagePreloadCount = settingsDialog.getImagePreloadCount();
        
        // Apply the new settings
        applySettings();
    }
}

void MainWindow::handleUpdateDatabase()
{
    // If a dialog is already open, just bring it to the front
    if (m_updateDialog) {
        m_updateDialog->raise();
        m_updateDialog->activateWindow();
        return;
    }

    // Locate the OCR script next to the application binary
    const QString appDir = QCoreApplication::applicationDirPath();
    // Try <appDir>/../OCR-scripts/ (dev tree) then <appDir>/OCR-scripts/ (installed)
    QString scriptPath = appDir + "/../OCR-scripts/ocr_screenshots.py";
    if (!QFileInfo::exists(scriptPath))
        scriptPath = appDir + "/OCR-scripts/ocr_screenshots.py";

    m_updateDialog = new UpdateDatabaseDialog(scriptPath, m_databasePath, m_screenshotsDir, this);

    // Clean up pointer when the dialog is closed
    connect(m_updateDialog, &QDialog::finished, this, [this]() {
        m_updateDialog->deleteLater();
        m_updateDialog = nullptr;
    });

    // Reload the gallery automatically when OCR finishes
    connect(m_updateDialog, &UpdateDatabaseDialog::updateFinished, this, [this]() {
        statusBar()->showMessage(tr("Database updated – reloading gallery…"), 3000);
        // The DB connection is already open; just refresh the gallery
        if (m_hasValidDatabase)
            displayAllImages();
    });

    m_updateDialog->show();
}

void MainWindow::loadSettings()
{
    // Ensure config directory exists
    QFileInfo configFile(CONFIG_FILE_PATH);
    QDir configDir = configFile.absoluteDir();
    if (!configDir.exists()) {
        configDir.mkpath(".");
    }
    
    QSettings settings(CONFIG_FILE_PATH, QSettings::IniFormat);
    
    // Load database path with fallback to XDG data dir default
    const QString defaultDbPath = QStandardPaths::writableLocation(QStandardPaths::AppLocalDataLocation)
                                  + "/screenshot_ocr.db";
    m_databasePath = settings.value("databasePath", defaultDbPath).toString();
    
    // Load screenshots directory with fallback to home/Screenshots
    m_screenshotsDir = settings.value("screenshotsDir", QDir::homePath() + "/Screenshots").toString();
    
    // Load preload count
    m_imagePreloadCount = settings.value("imagePreloadCount", DEFAULT_PRELOAD_COUNT).toInt();
}

void MainWindow::applySettings()
{
    // Re-initialize database with new path
    bool wasValid = m_hasValidDatabase;
    m_hasValidDatabase = false;
    
    // Try to initialize the database with the new path
    initializeDatabase();
    
    // Show appropriate message
    if (!wasValid && m_hasValidDatabase) {
        statusBar()->showMessage(tr("Successfully connected to database"), 3000);
    } else if (wasValid && !m_hasValidDatabase) {
        statusBar()->showMessage(tr("Lost connection to database"), 5000);
    } else if (!wasValid && !m_hasValidDatabase) {
        statusBar()->showMessage(tr("Could not connect to database"), 5000);
    } else {
        // Update page size in ImageGallery
        if (m_imageGallery) {
            m_imageGallery->setPageSize(m_imagePreloadCount);
        }
        
        // Refresh gallery with new database
        displayAllImages();
        
        // Ensure the status is updated with correct count
        int totalImageCount = m_dbManager->getImageCount();
        statusBar()->showMessage(tr("Database loaded with %1 total images").arg(totalImageCount), 3000);
    }
}

void MainWindow::updateStatusBar()
{
    if (!m_hasValidDatabase) {
        statusBar()->showMessage(tr("Database not connected"));
        return;
    }
    
    try {
        // Get the total count of images in the database
        int totalImages = m_dbManager->getImageCount();
        
        if (m_lastSearchText.isEmpty()) {
            statusBar()->showMessage(tr("Displaying images (paginated) - %1 total").arg(totalImages));
        } else {
            // Status message will be updated when search results arrive
            // via the searchResultsReady signal
            statusBar()->showMessage(tr("Searching..."));
        }
    } catch (const std::exception &e) {
        statusBar()->showMessage(tr("Error updating status: %1").arg(e.what()), 5000);
    }
}

void MainWindow::resizeEvent(QResizeEvent *event)
{
    QMainWindow::resizeEvent(event);
    
    // Notify the image gallery of the resize event
    if (m_imageGallery) {
        // The gallery will handle its own layout updates through its resize event
        // Just make sure it's visible and has the correct size policy
        m_imageGallery->setVisible(true);
    }
    
    // Update status bar to show current window dimensions
    // This helps with debugging layout issues
    if (statusBar() && statusBar()->isVisible()) {
        QString sizeInfo = QString("Window size: %1×%2").arg(width()).arg(height());
        statusBar()->showMessage(sizeInfo, 1000);
    }
}