/**
 * \file core/sage_run/sagebatchprocess.cpp
 * \date 11/05/2025
 * \author Olivier Langella
 * \brief handles execution of a Sage process
 */

/*******************************************************************************
 * Copyright (c) 2025 Olivier Langella
 *<Olivier.Langella@universite-paris-saclay.fr>.
 *
 * This file is part of i2MassChroQ.
 *
 *     i2MassChroQ is free software: you can redistribute it and/or modify
 *     it under the terms of the GNU General Public License as published by
 *     the Free Software Foundation, either version 3 of the License, or
 *     (at your option) any later version.
 *
 *     i2MassChroQ is distributed in the hope that it will be useful,
 *     but WITHOUT ANY WARRANTY; without even the implied warranty of
 *     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *     GNU General Public License for more details.
 *
 *     You should have received a copy of the GNU General Public License
 *     along with i2MassChroQ.  If not, see <http://www.gnu.org/licenses/>.
 *
 ******************************************************************************/

#include "sagebatchprocess.h"
#include <pappsomspp/core/exception/exceptioninterrupted.h>
#include <QtSystemDetection>
#include <qfileinfo.h>
#include <qlogging.h>
#include "../slurm_process/slurmprocess.h"

SageBatchProcess::SageBatchProcess(MainWindow *p_main_window,
                                   WorkMonitorInterface *p_monitor,
                                   const SageRunBatch sage_param)
{
  mp_mainWindow  = p_main_window;
  mp_monitor     = p_monitor;
  m_sageRunBatch = sage_param;

  prepareTemporaryDirectory();
}

SageBatchProcess::~SageBatchProcess()
{
}


void
SageBatchProcess::readyReadStandardOutput()
{
  QString message(mpa_sageProcess->readAllStandardOutput());

  qDebug() << message;
  mp_monitor->appendText(message);
  m_lastMessage = message;
  if(mp_monitor->shouldIstop())
    {
      qDebug() << "killing";
      mpa_sageProcess->kill();
      delete mpa_sageProcess;
      mpa_sageProcess = nullptr;
      throw pappso::ExceptionInterrupted(
        QObject::tr("Sage stopped by the user"));
    }
}

void
SageBatchProcess::readyReadStandardError()
{
  QString message(mpa_sageProcess->readAllStandardError());
  qDebug() << message;
  mp_monitor->appendText(message);
  m_sageErrorString += message;
  if(mp_monitor->shouldIstop())
    {
      qDebug() << "killing";
      mpa_sageProcess->kill();
      delete mpa_sageProcess;
      mpa_sageProcess = nullptr;
      throw pappso::ExceptionInterrupted(
        QObject::tr("Sage stopped by the user"));
    }
}


void
SageBatchProcess::run()
{
  m_sageErrorString = "";
  mp_monitor->setTotalSteps(1);

  // Check if the bin exist and is executable then check the masschroqml file
  // mp_monitor->message(QObject::tr("running MassChroQ checks \n"), 0);
  // checkMassChroQRunBatch();
  // checkMassChroQMLValidity();
  qDebug() << "checks finished";

  QTemporaryFile jsonf(QString("%1/sage").arg(mp_tmpDir->path()));
  jsonf.setAutoRemove(true);
  if(jsonf.open())
    {
      jsonf.write(m_sageRunBatch.sageJsonDocument.toJson());
      jsonf.close();
    }
  else
    {
      throw pappso::PappsoException(
        tr("Unable to write json file %1")
          .arg(QFileInfo(jsonf).absoluteFilePath()));
    }
  // Set the maschroq bin and the arguments
  mp_monitor->message(QObject::tr("running Sage\n"), 1);

  // writing JSON file


  QStringList arguments;


  qDebug();
  // Run Sage with the given arguments
  if(m_isSlurm)
    {
      //--mail-type=ALL
      arguments
        << QString("--cpus-per-task=%1").arg(m_sageRunBatch.sageThreads);
      arguments << QString("--mem=%1").arg(m_sageRunBatch.sageSlurmMemory);
      arguments << "--ntasks=1"
                << "--nodes=1";
      arguments << m_sageRunBatch.sageBin;
      mpa_sageProcess = new SlurmProcess();
    }
  else
    {
      mpa_sageProcess = new QProcess();
    }

  if(m_sageRunBatch.useQurl())
    {
      // since sage 0.15beta2
      arguments
        << QUrl::fromLocalFile(QFileInfo(jsonf).absoluteFilePath()).toString();
    }
  else
    {
      arguments << QFileInfo(jsonf).absoluteFilePath();
    }

  QProcessEnvironment env = QProcessEnvironment::systemEnvironment();
  env.insert("RAYON_NUM_THREADS",
             QString("%1").arg(m_sageRunBatch.sageThreads));
  mpa_sageProcess->setProcessEnvironment(env);

  qDebug();
  connect(mpa_sageProcess,
          &QProcess::readyReadStandardOutput,
          this,
          &SageBatchProcess::readyReadStandardOutput);
  connect(mpa_sageProcess,
          &QProcess::readyReadStandardError,
          this,
          &SageBatchProcess::readyReadStandardError);

  qDebug();

  if(m_isSlurm)
    {
      ((SlurmProcess *)mpa_sageProcess)
        ->srun(QFileInfo(jsonf).absoluteDir(), arguments);
    }
  else
    {
      mpa_sageProcess->start(m_sageRunBatch.sageBin, arguments);
    }
  qDebug() << "QProcess::exitCode=" << mpa_sageProcess->exitCode();
  if(!mpa_sageProcess->waitForStarted())
    {

      QString err = QObject::tr(
                      "Could not start Sage process "
                      "'%1' with arguments '%2': %3\n%4")
                      .arg(m_sageRunBatch.sageBin)
                      .arg(arguments.join(QStringLiteral(" ")))
                      .arg(m_sageErrorString)
                      .arg(m_lastMessage);
      throw pappso::PappsoException(err);
    }

  qDebug() << "QProcess::exitCode=" << mpa_sageProcess->exitCode();
  while(mpa_sageProcess->waitForFinished(1000) == false)
    {
      // mp_monitor->appendText(mpa_mcqProcess->readAll().data());
      qDebug();
      if(mp_monitor->shouldIstop())
        {
          qDebug() << "killing";
          mpa_sageProcess->kill();
          delete mpa_sageProcess;
          throw pappso::ExceptionInterrupted(
            QObject::tr("Sage stopped by the user"));
        }
    }
  qDebug() << "QProcess::exitCode=" << mpa_sageProcess->exitCode();
  // Stop the proccess and check if the exit was good
  QProcess::ExitStatus Status = mpa_sageProcess->exitStatus();
  qDebug() << "QProcess::ExitStatus=" << Status;

  if((Status != QProcess::ExitStatus::NormalExit) ||
     (mpa_sageProcess->exitCode() != 0))
    {

      delete mpa_sageProcess;
      qDebug() << m_sageErrorString;
      QString message(
        QObject::tr("error executing Sage Status != 0 : %1 %2\n %3\n %4")
          .arg(m_sageRunBatch.sageBin)
          .arg(arguments.join(" "))
          .arg(m_sageErrorString)
          .arg(m_lastMessage));
      qDebug() << message;
      throw pappso::PappsoException(message);
    }
  delete mpa_sageProcess;
}

void
SageBatchProcess::prepareTemporaryDirectory()
{
  // /gorgone/pappso/tmp
  QSettings settings;
  QString slurm_tmp_dir =
    QString("%1/i2masschroq")
      .arg(settings.value("slurm/tmp_dir", "/tmp").toString());

  if(mp_tmpDir != nullptr)
    {
      delete mp_tmpDir;
      mp_tmpDir = nullptr;
    }
  mp_tmpDir = new QTemporaryDir(slurm_tmp_dir);
  mp_tmpDir->setAutoRemove(
    settings.value("slurm/tmp_dir_autoremove", true).toBool());

  if(!mp_tmpDir->isValid())
    {
      // try the operating system temporary directory
      mp_tmpDir = new QTemporaryDir(QDir::tempPath() + "/sage");
      mp_tmpDir->setAutoRemove(true);
    }

  if(!mp_tmpDir->isValid())
    {
      // dir.path() returns the unique directory path
      throw pappso::PappsoException(
        QObject::tr("problem creating slurm temporary directory in %1\n")
          .arg(slurm_tmp_dir));
    }
}
