#include "databasemanager.h"
#include <QSqlQuery>
#include <QSqlError>
#include <QDebug>
#include <QStandardPaths>
#include <QDir>

DatabaseManager& DatabaseManager::instance()
{
    static DatabaseManager instance;
    return instance;
}

DatabaseManager::DatabaseManager() = default;

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

bool DatabaseManager::initialize(const QString& databasePath)
{
    QMutexLocker locker(&m_mutex);
    if (m_initialized) {
        return true;
    }

    QString dbPath = databasePath;
    if (dbPath.isEmpty()) {
        // Use application data location
        QDir dir(QStandardPaths::writableLocation(QStandardPaths::AppDataLocation));
        if (!dir.exists()) {
            dir.mkpath(".");
        }
        dbPath = dir.filePath("wino-mail.sqlite");
    }

    m_databasePath = dbPath;
    m_ownerThread = QThread::currentThreadId();

    qDebug() << "[DatabaseManager] Opening database at:" << dbPath;

    m_db = QSqlDatabase::addDatabase("QSQLITE", "wino_mail_connection");
    m_db.setDatabaseName(dbPath);

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

    qDebug() << "[DatabaseManager] Database opened successfully";

    QSqlQuery query(m_db);
    // Enable foreign keys
    qDebug() << "[DatabaseManager] Enabling foreign keys";
    query.exec("PRAGMA foreign_keys = ON;");

    // Create tables if they don't exist
    qDebug() << "[DatabaseManager] Creating Account table";

    // Account table
    if (!query.exec(
        "CREATE TABLE IF NOT EXISTS Account ("
        "id INTEGER PRIMARY KEY AUTOINCREMENT, "
        "email TEXT NOT NULL, "
        "displayName TEXT, signature TEXT, "
        "type INTEGER NOT NULL, " // 0=Outlook,1=Gmail,2=IMAP
        "accessToken TEXT, "
        "refreshToken TEXT, "
        "tokenExpires DATETIME, "
        "incomingHost TEXT, "
        "incomingPort INTEGER, "
        "incomingSsl INTEGER, "
        "outgoingHost TEXT, "
        "outgoingPort INTEGER, "
        "outgoingSsl INTEGER, "
        "connUsername TEXT, "
        "connPassword TEXT, "
        "authMethod TEXT"
        ");")) {
        qWarning() << "Failed to create Account table:" << query.lastError().text();
        return false;
    }
    qDebug() << "[DatabaseManager] Account table created successfully";

    // Folder table
    if (!query.exec(
        "CREATE TABLE IF NOT EXISTS Folder ("
        "id INTEGER PRIMARY KEY AUTOINCREMENT, "
        "accountId INTEGER NOT NULL, "
        "name TEXT NOT NULL, "
        "parentFolderId TEXT, "
        "isInbox BOOLEAN DEFAULT 0, "
        "isSent BOOLEAN DEFAULT 0, "
        "isDrafts BOOLEAN DEFAULT 0, "
        "isTrash BOOLEAN DEFAULT 0, "
        "unreadCount INTEGER DEFAULT 0, "
        "lastSynced DATETIME, "
        "FOREIGN KEY(accountId) REFERENCES Account(id) ON DELETE CASCADE"
        ");")) {
        qWarning() << "Failed to create Folder table:" << query.lastError().text();
        return false;
    }

    // MailCopy table (simplified)
    if (!query.exec(
        "CREATE TABLE IF NOT EXISTS MailCopy ("
        "id INTEGER PRIMARY KEY AUTOINCREMENT, "
        "folderId INTEGER NOT NULL, "
        "messageId TEXT UNIQUE, "
        "subject TEXT, "
        "sender TEXT, "
        "recipient TEXT, "
        "date DATETIME, "
        "read BOOLEAN DEFAULT 0, "
        "flagged BOOLEAN DEFAULT 0, "
        "hasAttachment BOOLEAN DEFAULT 0, "
        "size INTEGER, "
        "uid INTEGER, "
        "fileId TEXT, " // references the .eml file in storage
        "bodyHtml TEXT, "
        "toAddr TEXT, "
        "ccAddr TEXT, "
        "bccAddr TEXT, "
        "FOREIGN KEY(folderId) REFERENCES Folder(id) ON DELETE CASCADE"
        ");")) {
        qWarning() << "Failed to create MailCopy table:" << query.lastError().text();
        return false;
    }

    // Attachments table (optional)
    if (!query.exec(
        "CREATE TABLE IF NOT EXISTS Attachment ("
        "id INTEGER PRIMARY KEY AUTOINCREMENT, "
        "mailCopyId INTEGER NOT NULL, "
        "filename TEXT, "
        "mimeType TEXT, "
        "size INTEGER, "
        "contentId TEXT, "
        "storedPath TEXT, "
        "FOREIGN KEY(mailCopyId) REFERENCES MailCopy(id) ON DELETE CASCADE"
        ");")) {
        qWarning() << "Failed to create Attachment table:" << query.lastError().text();
        return false;
    }

    // ===== Categories table =====
    if (!query.exec(
        "CREATE TABLE IF NOT EXISTS Category ("
        "id INTEGER PRIMARY KEY AUTOINCREMENT, "
        "accountId INTEGER, "  // NULL = global category, otherwise per-account
        "name TEXT NOT NULL, "
        "color TEXT NOT NULL DEFAULT '#1976D2', "  // hex color
        "parentCategoryId INTEGER, "  // for nested categories
        "sortOrder INTEGER DEFAULT 0, "
        "createdAt DATETIME DEFAULT CURRENT_TIMESTAMP, "
        "FOREIGN KEY(accountId) REFERENCES Account(id) ON DELETE CASCADE, "
        "FOREIGN KEY(parentCategoryId) REFERENCES Category(id) ON DELETE SET NULL"
        ");")) {
        qWarning() << "Failed to create Category table:" << query.lastError().text();
        return false;
    }

    // ===== MailCategory (many-to-many) table =====
    if (!query.exec(
        "CREATE TABLE IF NOT EXISTS MailCategory ("
        "mailCopyId INTEGER NOT NULL, "
        "categoryId INTEGER NOT NULL, "
        "assignedAt DATETIME DEFAULT CURRENT_TIMESTAMP, "
        "PRIMARY KEY (mailCopyId, categoryId), "
        "FOREIGN KEY(mailCopyId) REFERENCES MailCopy(id) ON DELETE CASCADE, "
        "FOREIGN KEY(categoryId) REFERENCES Category(id) ON DELETE CASCADE"
        ");")) {
        qWarning() << "Failed to create MailCategory table:" << query.lastError().text();
        return false;
    }

    // ===== Rules table =====
    if (!query.exec(
        "CREATE TABLE IF NOT EXISTS Rule ("
        "id INTEGER PRIMARY KEY AUTOINCREMENT, "
        "accountId INTEGER, "  // NULL = global rule
        "name TEXT NOT NULL, "
        "description TEXT, "
        "enabled BOOLEAN DEFAULT 1, "
        "priority INTEGER DEFAULT 100, "  // lower = higher priority
        "matchAll BOOLEAN DEFAULT 1, "  // 1 = AND, 0 = OR
        "conditions TEXT NOT NULL, "  // JSON array of conditions
        "actions TEXT NOT NULL, "  // JSON array of actions
        "createdAt DATETIME DEFAULT CURRENT_TIMESTAMP, "
        "updatedAt DATETIME DEFAULT CURRENT_TIMESTAMP, "
        "lastRun DATETIME, "
        "runCount INTEGER DEFAULT 0, "
        "FOREIGN KEY(accountId) REFERENCES Account(id) ON DELETE CASCADE"
        ");")) {
        qWarning() << "Failed to create Rule table:" << query.lastError().text();
        return false;
    }

    // ===== Templates table =====
    if (!query.exec(
        "CREATE TABLE IF NOT EXISTS Template ("
        "id INTEGER PRIMARY KEY AUTOINCREMENT, "
        "accountId INTEGER, "  // NULL = global template
        "name TEXT NOT NULL, "
        "subject TEXT, "
        "bodyHtml TEXT, "
        "bodyText TEXT, "
        "variables TEXT, "  // JSON array of variable definitions {name, label, defaultValue, type}
        "isDefault BOOLEAN DEFAULT 0, "
        "createdAt DATETIME DEFAULT CURRENT_TIMESTAMP, "
        "updatedAt DATETIME DEFAULT CURRENT_TIMESTAMP, "
        "FOREIGN KEY(accountId) REFERENCES Account(id) ON DELETE CASCADE"
        ");")) {
        qWarning() << "Failed to create Template table:" << query.lastError().text();
        return false;
    }

    // Existing installations were created before storedPath was introduced.
    // SQLite reports an error when the column already exists, which is safe to
    // ignore here because the CREATE TABLE above covers new databases.
    query.exec("ALTER TABLE Account ADD COLUMN signature TEXT");
    query.exec("ALTER TABLE MailCopy ADD COLUMN toAddr TEXT");
    query.exec("ALTER TABLE MailCopy ADD COLUMN ccAddr TEXT");
    query.exec("ALTER TABLE MailCopy ADD COLUMN bccAddr TEXT");
    query.exec("ALTER TABLE MailCopy ADD COLUMN bodyHtml TEXT");
    query.exec("ALTER TABLE MailCopy ADD COLUMN uid INTEGER");
    query.exec("ALTER TABLE MailCopy ADD COLUMN fileId TEXT");
    query.exec("ALTER TABLE Attachment ADD COLUMN storedPath TEXT");
    query.exec("CREATE INDEX IF NOT EXISTS idx_mailcopy_folder_uid ON MailCopy(folderId, uid)");
    query.exec("CREATE INDEX IF NOT EXISTS idx_attachment_mailcopy ON Attachment(mailCopyId)");
    query.exec("CREATE INDEX IF NOT EXISTS idx_category_account ON Category(accountId)");
    query.exec("CREATE INDEX IF NOT EXISTS idx_mailcategory_mail ON MailCategory(mailCopyId)");
    query.exec("CREATE INDEX IF NOT EXISTS idx_mailcategory_category ON MailCategory(categoryId)");
    query.exec("CREATE INDEX IF NOT EXISTS idx_rule_account ON Rule(accountId)");
    query.exec("CREATE INDEX IF NOT EXISTS idx_rule_enabled ON Rule(enabled)");
    query.exec("CREATE INDEX IF NOT EXISTS idx_template_account ON Template(accountId)");
    query.exec("CREATE INDEX IF NOT EXISTS idx_template_default ON Template(isDefault)");

    m_initialized = true;
    qDebug() << "Database initialized at:" << dbPath;
    return true;
}

QSqlDatabase DatabaseManager::database()
{
    if (QThread::currentThreadId() == m_ownerThread)
        return m_db;

    const QString connectionName = QStringLiteral("wino_mail_connection_%1")
        .arg(reinterpret_cast<quintptr>(QThread::currentThreadId()), 0, 16);
    if (QSqlDatabase::contains(connectionName))
        return QSqlDatabase::database(connectionName);

    QMutexLocker locker(&m_mutex);
    if (QSqlDatabase::contains(connectionName))
        return QSqlDatabase::database(connectionName);
    QSqlDatabase db = QSqlDatabase::addDatabase(QStringLiteral("QSQLITE"), connectionName);
    db.setDatabaseName(m_databasePath);
    if (!db.open()) {
        qWarning() << "Failed to open thread-local database:" << db.lastError().text();
        return db;
    }
    QSqlQuery pragma(db);
    pragma.exec(QStringLiteral("PRAGMA foreign_keys = ON"));
    return db;
}
