#include "pythonrunner.h"
#include <QDebug>
#include <QDir>
#include <QStandardPaths>
#include <QCoreApplication>

PythonRunner::PythonRunner(QTextEdit* outputWidget, QObject* parent)
    : QObject(parent), m_outputWidget(outputWidget), m_process(nullptr), m_venvProcess(nullptr),
      ready(false), venvPreparing(false) {
    m_process = new QProcess(this);
    m_venvProcess = new QProcess(this);
    connect(m_process, &QProcess::readyReadStandardOutput, this, &PythonRunner::onOutputAvailable);
    connect(m_process, &QProcess::readyReadStandardError, this, &PythonRunner::onErrorAvailable);
    connect(m_process, QOverload<int, QProcess::ExitStatus>::of(&QProcess::finished), this, &PythonRunner::onProcessFinished);
    connect(m_venvProcess, &QProcess::readyReadStandardOutput, this, [this]() {
        QByteArray output = m_venvProcess->readAllStandardOutput();
        m_outputWidget->append(output);
        m_outputWidget->moveCursor(QTextCursor::End);
        emit venvPreparationProgress(output);
    });
    connect(m_venvProcess, &QProcess::readyReadStandardError, this, [this]() {
        QByteArray error = m_venvProcess->readAllStandardError();
        m_outputWidget->append(error);
        m_outputWidget->moveCursor(QTextCursor::End);
        emit venvPreparationProgress(error);
    });
    connect(m_venvProcess, QOverload<int, QProcess::ExitStatus>::of(&QProcess::finished), this, &PythonRunner::onVenvProcessFinished);
}

PythonRunner::~PythonRunner() {
    if (m_process) {
        m_process->terminate();
        m_process->waitForFinished(1000);
    }
    if (m_venvProcess) {
        m_venvProcess->terminate();
        m_venvProcess->waitForFinished(1000);
    }
}

bool PythonRunner::runScript(const QString& scriptContent) {
    if (m_process->state() == QProcess::Running) {
        m_process->terminate();
        m_process->waitForFinished(1000);
    }

    m_outputWidget->clear();

    // Check if venv is ready, if not prepare it
    if (!isVenvReady()) {
        m_outputWidget->append("Preparing Python environment...\n");
        m_outputWidget->moveCursor(QTextCursor::End);
        prepareVenv();
        return true; // Return early - venv preparation will handle script execution when ready
    }

    QString pythonPath = getVenvPythonPath();
    m_process->start(pythonPath, QStringList() << "-u" << "-c" << scriptContent);

    return true;
}

void PythonRunner::stopScript() {
    if (m_process && m_process->state() == QProcess::Running) {
        m_process->terminate();
    }
}

void PythonRunner::onOutputAvailable() {
    QByteArray output = m_process->readAllStandardOutput();
    m_outputWidget->append(output);
    m_outputWidget->moveCursor(QTextCursor::End);
}

void PythonRunner::onErrorAvailable() {
    QByteArray error = m_process->readAllStandardError();
    m_outputWidget->append(error);
    m_outputWidget->moveCursor(QTextCursor::End);
}

void PythonRunner::onProcessFinished(int exitCode, QProcess::ExitStatus exitStatus) {
    if (exitStatus == QProcess::NormalExit && exitCode != 0) {
        m_outputWidget->append(QString("Process exited with code %1\n").arg(exitCode));
    } else if (exitStatus == QProcess::CrashExit) {
        m_outputWidget->append("Python was stopped\n");
    }
    emit scriptFinished(exitCode);
}

void PythonRunner::onVenvProcessFinished(int exitCode, QProcess::ExitStatus exitStatus) {
    if (exitStatus == QProcess::NormalExit && exitCode == 0) {
        // Check if this was venv creation or pip installation
        QString venvPythonPath = getVenvPythonPath();
        if (!QFile::exists(venvPythonPath)) {
            // This was venv creation, now install eismultiplexer package
            installEismultiplexerPackage();
        } else {
            // This was pip installation, mark as ready
            ready = true;
            m_outputWidget->append("Python environment prepared successfully.\n");

            // Signal that venv preparation is complete
            {
                QMutexLocker locker(&venvMutex);
                venvPreparing = false;
                venvCondition.wakeAll();
            }

            emit venvPreparationFinished();
        }
    } else {
        ready = false;
        m_outputWidget->append(QString("Failed to prepare Python environment (code %1)\n").arg(exitCode));

        // Signal that venv preparation failed
        {
            QMutexLocker locker(&venvMutex);
            venvPreparing = false;
            venvCondition.wakeAll();
        }

        emit venvPreparationFinished();
    }
}

void PythonRunner::prepareVenv() {
    // Start venv preparation in a non-blocking manner
    QMutexLocker locker(&venvMutex);
    venvPreparing = true;

    // Create virtual environment
    QString pythonPath = getPythonExecutable();
    QString venvPath = getVenvPath();

    m_outputWidget->append(QString("Creating virtual environment at %1...\n").arg(venvPath));
    m_outputWidget->moveCursor(QTextCursor::End);

    QStringList args;
    args << "-m" << "venv" << venvPath;

    m_venvProcess->start(pythonPath, args);
    if (!m_venvProcess->waitForStarted()) {
        m_outputWidget->append("Failed to start virtual environment creation process.\n");
        ready = false;
        venvPreparing = false;
        return;
    }

    // Don't wait for completion - this will be handled by the finished signal
    // The installEismultiplexerPackage will be called in the finished signal handler
}

void PythonRunner::installEismultiplexerPackage() {
    QString venvPythonPath = getVenvPythonPath();
    QString venvPipPath = QString("%1 -m pip").arg(venvPythonPath);

    m_outputWidget->append("Installing eismultiplexer package...\n");
    m_outputWidget->moveCursor(QTextCursor::End);

    QStringList args;
    args << "install" << "eismultiplexer";

    m_venvProcess->start(venvPipPath, args);
    if (!m_venvProcess->waitForStarted()) {
        m_outputWidget->append("Failed to start pip installation process.\n");
        ready = false;

        // Signal that venv preparation failed
        {
            QMutexLocker locker(&venvMutex);
            venvPreparing = false;
            venvCondition.wakeAll();
        }

        emit venvPreparationFinished();
        return;
    }

    // Don't wait for completion - this will be handled by the finished signal
}

QString PythonRunner::getPythonExecutable() {
    QString pythonPath;

    // Try to find python3 first, then python
    QStringList pythonCandidates;
    pythonCandidates << "python3" << "python";

    for (const QString& candidate : pythonCandidates) {
        QProcessEnvironment env = QProcessEnvironment::systemEnvironment();
        QString path = env.value("PATH");
        QStringList pathDirs = path.split(';', Qt::SkipEmptyParts);

        for (const QString& dir : pathDirs) {
            QString pythonExecutable = QDir(dir).filePath(candidate);
            if (QFile::exists(pythonExecutable)) {
                pythonPath = pythonExecutable;
                break;
            }
        }
        if (!pythonPath.isEmpty()) {
            break;
        }
    }

    // If still not found, use the default python from QProcessEnvironment
    if (pythonPath.isEmpty()) {
        pythonPath = "python";
    }

    return pythonPath;
}

QString PythonRunner::getVenvPath() {
    // Create venv in a temporary directory
    QString venvDir = dir.path() + QDir::separator() + "venv";
    return venvDir;
}

QString PythonRunner::getVenvPythonPath() {
    QString venvPath = getVenvPath();
    QString venvPython;

    // Determine the correct python path for the venv based on platform
    QString pythonExecutable = "python";

    // On Windows, the python executable is in Scripts directory
    // On Linux/macOS, it's in bin directory
    QString venvBinDir;
    #ifdef Q_OS_WIN
    venvBinDir = venvPath + QDir::separator() + "Scripts";
    #else
    venvBinDir = venvPath + QDir::separator() + "bin";
    #endif

    venvPython = QDir(venvBinDir).filePath(pythonExecutable);
    return venvPython;
}

bool PythonRunner::isVenvReady() {
    QString venvPythonPath = getVenvPythonPath();
    return QFile::exists(venvPythonPath);
}
