#include "sshmanager.h"
#include <QDebug>
#include <QFile>
#include <fcntl.h>
#include <sys/stat.h>

SSHManager::SSHManager(QObject *parent)
    : QObject(parent)
    , m_sshSession(nullptr, [](ssh_session session) { if (session) ssh_free(session); })
    , m_sftpSession(nullptr, [](sftp_session session) { if (session) sftp_free(session); })
{
}

SSHManager::~SSHManager() = default;

bool SSHManager::connectToHost(const QString &hostname, const QString &username, const QString &password)
{
    m_sshSession.reset(ssh_new());
    if (!m_sshSession) {
        emit errorOccurred(tr("Failed to create SSH session"));
        return false;
    }

    ssh_options_set(m_sshSession.get(), SSH_OPTIONS_HOST, hostname.toStdString().c_str());
    ssh_options_set(m_sshSession.get(), SSH_OPTIONS_USER, username.toStdString().c_str());

    if (ssh_connect(m_sshSession.get()) != SSH_OK) {
        emit errorOccurred(tr("Failed to connect to SSH server: %1").arg(ssh_get_error(m_sshSession.get())));
        return false;
    }

    if (ssh_userauth_password(m_sshSession.get(), nullptr, password.toStdString().c_str()) != SSH_AUTH_SUCCESS) {
        emit errorOccurred(tr("Failed to authenticate: %1").arg(ssh_get_error(m_sshSession.get())));
        return false;
    }

    if (!initializeSFTP()) {
        return false;
    }

    emit connectionStatusChanged(true);
    return true;
}

bool SSHManager::disconnectFromHost()
{
    m_sftpSession.reset();
    m_sshSession.reset();
    emit connectionStatusChanged(false);
    return true;
}

bool SSHManager::executeCommand(const QString &command, QString &output)
{
    ssh_channel channel = ssh_channel_new(m_sshSession.get());
    if (!channel) {
        emit errorOccurred(tr("Failed to create SSH channel"));
        return false;
    }

    if (ssh_channel_open_session(channel) != SSH_OK) {
        ssh_channel_free(channel);
        emit errorOccurred(tr("Failed to open SSH channel"));
        return false;
    }

    if (ssh_channel_request_exec(channel, command.toStdString().c_str()) != SSH_OK) {
        ssh_channel_close(channel);
        ssh_channel_free(channel);
        emit errorOccurred(tr("Failed to execute command"));
        return false;
    }

    char buffer[256];
    int nbytes;
    output.clear();
    while ((nbytes = ssh_channel_read(channel, buffer, sizeof(buffer), 0)) > 0) {
        output.append(QString::fromUtf8(buffer, nbytes));
    }

    ssh_channel_send_eof(channel);
    ssh_channel_close(channel);
    ssh_channel_free(channel);

    return true;
}

bool SSHManager::uploadFile(const QString &localPath, const QString &remotePath)
{
    sftp_file file = sftp_open(m_sftpSession.get(), remotePath.toStdString().c_str(), O_WRONLY | O_CREAT | O_TRUNC, S_IRWXU);
    if (!file) {
        emit errorOccurred(tr("Failed to open remote file for writing"));
        return false;
    }

    QFile localFile(localPath);
    if (!localFile.open(QIODevice::ReadOnly)) {
        sftp_close(file);
        emit errorOccurred(tr("Failed to open local file for reading"));
        return false;
    }

    const int bufferSize = 4096;
    char buffer[bufferSize];
    qint64 bytesRead;
    while ((bytesRead = localFile.read(buffer, bufferSize)) > 0) {
        if (sftp_write(file, buffer, bytesRead) != bytesRead) {
            localFile.close();
            sftp_close(file);
            emit errorOccurred(tr("Failed to write to remote file"));
            return false;
        }
    }

    localFile.close();
    sftp_close(file);
    return true;
}

bool SSHManager::downloadFile(const QString &remotePath, const QString &localPath)
{
    sftp_file file = sftp_open(m_sftpSession.get(), remotePath.toStdString().c_str(), O_RDONLY, 0);
    if (!file) {
        emit errorOccurred(tr("Failed to open remote file for reading"));
        return false;
    }

    QFile localFile(localPath);
    if (!localFile.open(QIODevice::WriteOnly)) {
        sftp_close(file);
        emit errorOccurred(tr("Failed to open local file for writing"));
        return false;
    }

    const int bufferSize = 4096;
    char buffer[bufferSize];
    int bytesRead;
    while ((bytesRead = sftp_read(file, buffer, bufferSize)) > 0) {
        if (localFile.write(buffer, bytesRead) != bytesRead) {
            localFile.close();
            sftp_close(file);
            emit errorOccurred(tr("Failed to write to local file"));
            return false;
        }
    }

    localFile.close();
    sftp_close(file);
    return true;
}

bool SSHManager::initializeSFTP()
{
    m_sftpSession.reset(sftp_new(m_sshSession.get()));
    if (!m_sftpSession) {
        emit errorOccurred(tr("Failed to create SFTP session"));
        return false;
    }

    if (sftp_init(m_sftpSession.get()) != SSH_OK) {
        emit errorOccurred(tr("Failed to initialize SFTP session: %1").arg(ssh_get_error(m_sshSession.get())));
        return false;
    }

    return true;
}
