#include "change-password-panel.h"
#include "../DialogManager/dialog-manager.h"
#include "../ModalOverlay/modal-overlay-manager.h"
#include "../PasswordLineEdit/password-line-edit.h"
#include <QLabel>
#include <QVBoxLayout>
#include <QFormLayout>
#include <QHBoxLayout>
#include <QToolButton>
#include <QRegularExpression>
#include <QProcessEnvironment>
#include <QToolTip>

ChangePasswordPanel::ChangePasswordPanel(const QString &username, QWidget *parent)
    : OverlayPanelBase(":/plugin/change-password-panel.qss", parent), m_username(username)
{
    // Use base containers: contentLayout() and bottomLayout()
    auto *contentLayout = this->contentLayout();
    auto *bottomLayout = this->bottomLayout();

    auto *form = new QFormLayout();
    form->setSpacing(12); form->setHorizontalSpacing(16); form->setFieldGrowthPolicy(QFormLayout::ExpandingFieldsGrow);
    QFont fieldFont; fieldFont.setPixelSize(14); fieldFont.setWeight(QFont::Normal);

    m_oldPasswordEdit = new PasswordLineEdit();
    m_oldPasswordEdit->setPlaceholderText(tr("Enter"));
    m_oldPasswordEdit->setFixedHeight(32); m_oldPasswordEdit->setFont(fieldFont);
    form->addRow(tr("Current Password"), m_oldPasswordEdit);

    m_newPasswordEdit = new PasswordLineEdit();
    m_newPasswordEdit->setPlaceholderText(tr("Enter"));
    m_newPasswordEdit->setFixedHeight(32); m_newPasswordEdit->setFont(fieldFont);
    form->addRow(tr("New Password"), m_newPasswordEdit);

    m_confirmPasswordEdit = new PasswordLineEdit();
    m_confirmPasswordEdit->setPlaceholderText(tr("Enter"));
    m_confirmPasswordEdit->setFixedHeight(32); m_confirmPasswordEdit->setFont(fieldFont);
    form->addRow(tr("Confirm Password"), m_confirmPasswordEdit);

    contentLayout->addLayout(form);

    // Inline error row created by base; margins 0 top/bottom, spacing 8 between icon and text
    this->createInlineErrorRow(form, /*topBottomMargin=*/0, /*iconTextSpacing=*/8);

    auto *btnLayout = new QHBoxLayout(); btnLayout->addStretch();
    m_cancelButton = new DialogButton(tr("Cancel"), DialogButton::Cancel);
    m_confirmButton = new DialogButton(tr("Confirm"), DialogButton::Confirm); m_confirmButton->setEnabled(false);
    btnLayout->addWidget(m_cancelButton); btnLayout->addWidget(m_confirmButton);
    bottomLayout->addLayout(btnLayout);


    connect(m_cancelButton, &QToolButton::clicked, this, [this]{ emit requestClose(); });
    connect(m_confirmButton, &QToolButton::clicked, this, &ChangePasswordPanel::onConfirm);
    connect(m_oldPasswordEdit, &QLineEdit::textChanged, this, &ChangePasswordPanel::validateInputs);
    connect(m_newPasswordEdit, &QLineEdit::textChanged, this, &ChangePasswordPanel::validateInputs);
    connect(m_confirmPasswordEdit, &QLineEdit::textChanged, this, &ChangePasswordPanel::validateInputs);

    form->setLabelAlignment(Qt::AlignRight | Qt::AlignVCenter);
    for (int r=0; r<form->rowCount(); ++r) {
        if (auto *item = form->itemAt(r, QFormLayout::LabelRole)) {
            if (auto *lbl = qobject_cast<QLabel*>(item->widget())) {
                lbl->setMinimumHeight(32); lbl->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Fixed);
                QFont lf = lbl->font(); lf.setPixelSize(14); lf.setWeight(QFont::Normal); lbl->setFont(lf);
                lbl->setAlignment(Qt::AlignRight | Qt::AlignVCenter);
            }
        }
    }

    initializeRegexPatterns();
    m_oldPasswordEdit->setFocus();
    // no bubble; using inline error label
}

ChangePasswordPanel::~ChangePasswordPanel() {
    cleanupProcess();
}

// addVisibilityToggle removed: PasswordLineEdit encapsulates toggle behavior.

void ChangePasswordPanel::initializeRegexPatterns() {
    m_reCurrentPassword = QRegularExpression("(current|当前).*(password|密码)", QRegularExpression::CaseInsensitiveOption);
    m_reNewPassword = QRegularExpression("(new|新的?).*(password|密码)", QRegularExpression::CaseInsensitiveOption);
    m_reRetypePassword = QRegularExpression("(retype|confirm|re-?enter|重新输入|再次输入)", QRegularExpression::CaseInsensitiveOption);
    m_reAuthFailed = QRegularExpression("(incorrect|wrong|错误的?|sorry|抱歉|身份验证令牌|authentication.*token|manipulation.*error|password.*unchanged|密码.*未更改)", QRegularExpression::CaseInsensitiveOption);
    m_reWeakPassword = QRegularExpression("(bad.?password|too.?short|too.?simple|dictionary|太短|太简单|基于字典|palindrome|回文)", QRegularExpression::CaseInsensitiveOption);
    m_reSuccess = QRegularExpression();
}

void ChangePasswordPanel::validateInputs() {
    m_lastError.clear();
    bool haveAll = !m_oldPasswordEdit->text().isEmpty() && !m_newPasswordEdit->text().isEmpty() && !m_confirmPasswordEdit->text().isEmpty();
    bool valid = haveAll;
    QWidget *errorField = nullptr;
    if (valid && m_newPasswordEdit->text() != m_confirmPasswordEdit->text()) {
        m_lastError = tr("Passwords do not match. Please re-enter.");
        valid = false; errorField = m_confirmPasswordEdit;
    }
    if (valid && m_newPasswordEdit->text().length() < 6) {
        m_lastError = tr("Password must be at least 6 characters.");
        valid = false; errorField = m_newPasswordEdit;
    }
    if (valid && m_oldPasswordEdit->text() == m_newPasswordEdit->text()) {
        m_lastError = tr("New password is the same as the old one. Please choose a different password.");
        valid = false; errorField = m_newPasswordEdit;
    }
    if (!valid && errorField) {
        showInlineError(m_lastError);
    } else if (valid) {
        hideInlineError();
    }
    m_confirmButton->setEnabled(valid && m_state == None);
}

void ChangePasswordPanel::onConfirm() {
    if (!m_confirmButton->isEnabled()) return;
    m_oldPassword = m_oldPasswordEdit->text();
    m_newPassword = m_newPasswordEdit->text();
    setInputsEnabled(false);
    startPasswdProcess();
}

void ChangePasswordPanel::setInputsEnabled(bool en) {
    m_oldPasswordEdit->setEnabled(en);
    m_newPasswordEdit->setEnabled(en);
    m_confirmPasswordEdit->setEnabled(en);
    m_confirmButton->setEnabled(en && m_confirmButton->isEnabled());
}

void ChangePasswordPanel::startPasswdProcess() {
    if (m_passwdProcess) { m_passwdProcess->deleteLater(); m_passwdProcess = nullptr; }
    m_passwdProcess = new QProcess(this);
    m_state = WaitingOld; m_outputBuffer.clear();
    QProcessEnvironment env = QProcessEnvironment::systemEnvironment(); env.insert("LC_ALL", "C");
    m_passwdProcess->setProcessEnvironment(env);
    connect(m_passwdProcess, &QProcess::readyReadStandardOutput, this, &ChangePasswordPanel::onPasswdReadyRead);
    connect(m_passwdProcess, &QProcess::readyReadStandardError, this, &ChangePasswordPanel::onPasswdReadyRead);
    connect(m_passwdProcess, QOverload<int,QProcess::ExitStatus>::of(&QProcess::finished), this, &ChangePasswordPanel::onPasswdFinished);
    connect(m_passwdProcess, &QProcess::errorOccurred, this, &ChangePasswordPanel::onPasswdError);
    m_passwdProcess->start("/usr/bin/passwd", QStringList());
    if (!m_passwdProcess->waitForStarted(3000)) finishWithError(tr("Failed to start password change process"));
}

void ChangePasswordPanel::sendToPasswd(const QString &text) {
    if (m_passwdProcess && m_passwdProcess->state()==QProcess::Running) {
        m_passwdProcess->write(text.toUtf8()+"\n"); m_passwdProcess->waitForBytesWritten();
    }
}

void ChangePasswordPanel::onPasswdReadyRead() {
    if (!m_passwdProcess) return;
    QString out = QString::fromUtf8(m_passwdProcess->readAllStandardOutput());
    QString err = QString::fromUtf8(m_passwdProcess->readAllStandardError());
    if (!out.isEmpty()) { m_outputBuffer += out; handlePasswdOutput(out); }
    if (!err.isEmpty()) { m_outputBuffer += err; handlePasswdOutput(err); }
}

void ChangePasswordPanel::handlePasswdOutput(const QString &output) {
    if (m_state != Error && m_state != None && m_reAuthFailed.match(output).hasMatch()) { finishWithError(tr("Old password is incorrect")); return; }
    switch (m_state) {
    case WaitingOld:
        if (m_reCurrentPassword.match(output).hasMatch()) { sendToPasswd(m_oldPassword); m_state = WaitingNew; }
        break;
    case WaitingNew:
        if (m_reNewPassword.match(output).hasMatch()) { sendToPasswd(m_newPassword); m_state = WaitingRetype; }
        break;
    case WaitingRetype: {
        auto match = m_reWeakPassword.match(output);
        if (match.hasMatch()) {
            QString errorMsg = tr("New password is too weak or does not meet requirements");
            QString captured = match.captured(0).toLower();
            if (captured.contains("short") || captured.contains("太短")) errorMsg = tr("New password is too short");
            else if (captured.contains("simple") || captured.contains("太简单")) errorMsg = tr("New password is too simple");
            else if (captured.contains("dictionary") || captured.contains("字典")) errorMsg = tr("New password is based on a dictionary word");
            else if (captured.contains("palindrome") || captured.contains("回文")) errorMsg = tr("New password is a palindrome");
            finishWithError(errorMsg); return;
        }
        if (m_reRetypePassword.match(output).hasMatch()) { sendToPasswd(m_newPassword); m_state = Done; }
        break; }
    case Done:
        if (m_reSuccess.match(output).hasMatch()) finishWithSuccess();
        break;
    default: break;
    }
}

void ChangePasswordPanel::onPasswdFinished(int exitCode, QProcess::ExitStatus) {
    if (m_state == Error) return;
    if (m_state == Done && exitCode==0) { finishWithSuccess(); return; }
    QString errorMsg = normalizeFailureMessage(m_outputBuffer, exitCode);
    finishWithError(errorMsg);
}

void ChangePasswordPanel::onPasswdError(QProcess::ProcessError err) {
    if (m_state == Error) return;
    QString msg = tr("Password change process error");
    if (err == QProcess::FailedToStart) msg = tr("Failed to start password change process");
    else if (err == QProcess::Crashed) msg = tr("Password change process crashed");
    finishWithError(msg);
}

void ChangePasswordPanel::finishWithError(const QString &msg) {
    if (m_state == Error) return;
    m_state = Error; setInputsEnabled(true); if (m_passwdProcess) disconnect(m_passwdProcess,nullptr,this,nullptr);
    // Runtime/process errors should NOT duplicate inline error; hide inline row then popup.
    hideInlineError();
    showPopupError(tr("Password Change Failed"), msg);
    m_state = None;
    // Allow immediate resubmission if inputs are still valid
    validateInputs();
    emit passwordChangeFailed(msg);
}

void ChangePasswordPanel::finishWithSuccess() {
    emit passwordChanged(); emit requestClose();
}

void ChangePasswordPanel::showPopupError(const QString &title, const QString &msg) {
    // Popup only for process/runtime errors; inline reserved for validation stage.
    DialogManager::instance().showError(title, msg, this);
}

void ChangePasswordPanel::showFieldError(QWidget *field, const QString &msg) {
    if (!field) return;
    QPoint pos = field->mapToGlobal(QPoint(field->width()/2, field->height()));
    QToolTip::showText(pos, msg, field);
}

void ChangePasswordPanel::cleanupProcess() {
    if (!m_passwdProcess) return;
    if (m_passwdProcess->state() == QProcess::Running) {
        m_passwdProcess->terminate();
        if (!m_passwdProcess->waitForFinished(1000)) {
            m_passwdProcess->kill();
            m_passwdProcess->waitForFinished(500);
        }
    }
    disconnect(m_passwdProcess,nullptr,this,nullptr);
    m_passwdProcess->deleteLater();
    m_passwdProcess = nullptr;
    m_state = Error; // Prevent late signals altering UI
}

QString ChangePasswordPanel::normalizeFailureMessage(const QString &raw, int exitCode) const {
    QString lower = raw.toLower();
    if (lower.contains("authentication token manipulation error"))
        return tr("System error while updating authentication token");
    if (lower.contains("password unchanged"))
        return tr("Password unchanged (same as old or invalid)");
    if (lower.contains("bad password"))
        return tr("New password does not meet requirements");
    if (lower.contains("incorrect") || lower.contains("wrong") || lower.contains("authentication failed"))
        return tr("Old password is incorrect");
    if (lower.contains("too short"))
        return tr("New password is too short");
    if (lower.contains("too simple"))
        return tr("New password is too simple");
    if (lower.contains("dictionary"))
        return tr("New password is based on a dictionary word");
    if (lower.contains("palindrome"))
        return tr("New password is a palindrome");
    if (exitCode != 0 && raw.trimmed().isEmpty())
        return tr("Password change failed (exit code %1)").arg(exitCode);
    return tr("Password change failed");
}
