/*
 * Copyright (C) 2025-2026  Tim Zimmermann <tim@aglais.org>
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program 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 Affero General Public License for more details.
 *
 * You should have received a copy of the GNU Affero General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

package org.aglais.zms.ui.tab.settings;

import org.aglais.zms.AglaisZMS;
import org.aglais.zms.ClientConfig;
import org.aglais.zms.db.core.model.ModifiedStateTracker;
import org.aglais.zms.db.core.model.User;
import org.aglais.zms.ui.MainFrame;
import org.aglais.zms.ui.component.DangerButton;
import org.aglais.zms.ui.tab.ISavableTab;
import org.aglais.zms.ui.tab.SettingsTab;
import org.aglais.zms.ui.tab.SubTab;
import org.aglais.zms.util.ConfigUtil;
import org.aglais.zms.util.Log;
import org.jetbrains.annotations.NotNull;

import javax.swing.*;
import java.awt.*;
import java.awt.event.WindowEvent;

/**
 * Tab for managing the user profile
 */
public class SettingsProfileTab extends SubTab<SettingsTab> implements ISavableTab {

    private static final String LOG_TAG = SettingsProfileTab.class.getSimpleName();

    private JPanel contentPane;
    private JTextField usernameTextField;
    private JPasswordField passwordField;
    private JButton saveButton;
    private DangerButton logoutButton;

    private ModifiedStateTracker<User> userModifiedTracker;

    /**
     * Constructs a new {@link SettingsProfileTab}
     *
     * @param zms    The main application instance
     * @param parent The parent {@link SettingsTab} containing this sub-tab
     */
    public SettingsProfileTab(AglaisZMS zms, SettingsTab parent) {
        super(zms, parent);

        saveButton.addActionListener((event) -> saveChangesWithUI());
        logoutButton.addActionListener((event) -> {
            ClientConfig config = zms.getConfig();
            config.server = new ClientConfig.Server(config.server.url(), config.server.username(), null);
            ConfigUtil.saveConfig(config.configPath, config);

            zms.restart();
        });
        // Only show logout button when the password was stored
        logoutButton.setVisible(zms.getConfig().server.encryptedPassword() != null);

        // Editable text fields
        usernameTextField.addCaretListener((event) -> {
            User user = userModifiedTracker.getData();
            user.setName(usernameTextField.getText());
            updateModifiedState();
        });
        passwordField.addCaretListener((event) -> {
            updateModifiedState();
        });
    }

    /**
     * Updates the enabled state of the save button based on whether the user has been modified.
     */
    private void updateModifiedState() {
        saveButton.setEnabled(userModifiedTracker.isModified() || passwordField.getPassword().length > 0);
    }

    @Override
    public void onShow() {
        contentPane.getRootPane().setDefaultButton(saveButton);

        userModifiedTracker = new ModifiedStateTracker<>(zms.getClient().getSelf());

        User user = userModifiedTracker.getData();
        usernameTextField.setText(user.getName());
        passwordField.setText("");
    }

    @Override
    public @NotNull JComponent getContentPane() {
        return contentPane;
    }

    @Override
    public String getSaveTitle() {
        return userModifiedTracker != null ? userModifiedTracker.getData().getName() : null;
    }

    @Override
    public boolean hasUnsavedChanges() {
        return userModifiedTracker != null && userModifiedTracker.isModified();
    }

    @Override
    public boolean saveChanges() {
        User user = userModifiedTracker.getData();

        if (passwordField.getPassword().length > 0)
            user.setClearTextPassword(new String(passwordField.getPassword()));
        else
            user.setPassword(null);
        if (zms.getClient().updateUser(user)) {
            userModifiedTracker.applyChanges();
            updateModifiedState();

            // Try to update the client's credentials in case the connection drops and has to be reopened
            if (zms.getClient().setCredentials(user.getName(),
                    passwordField.getPassword().length > 0 ? passwordField.getPassword() : null)) {
                ClientConfig config = zms.getConfig();

                config.server = new ClientConfig.Server(config.server.url(), user.getName(),
                        config.server.encryptedPassword() != null && passwordField.getPassword().length > 0
                                ? zms.encryptPassword(passwordField.getPassword())
                                : config.server.encryptedPassword());
                ConfigUtil.saveConfig(config.configPath, config);
                passwordField.setText("");
            } else {
                // This should never happen
                Log.wtf(LOG_TAG, "Failed to update credentials for user");
            }

            return true;
        }

        return false;
    }

    @Override
    public void discardChanges() {
        userModifiedTracker.discardChanges();
    }
}
