/*
 * JBoss, Home of Professional Open Source.
 * Copyright 2021 Red Hat, Inc., and individual contributors
 * as indicated by the @author tags.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package org.jboss.installer.screens;

import java.awt.Insets;
import org.jboss.installer.common.UiResources;
import org.jboss.installer.core.InstallationData;
import org.jboss.installer.core.LanguageUtils;
import org.jboss.installer.core.ValidationResult;
import org.jboss.installer.postinstall.PostInstallTask;
import org.jboss.installer.validators.AdminLoginValidator;
import org.jboss.installer.validators.PasswordSecurityValidator;

import javax.swing.Box;
import javax.swing.JComponent;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JPasswordField;
import javax.swing.JTextField;
import javax.swing.event.DocumentEvent;
import javax.swing.event.DocumentListener;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;

public class AdminCreationScreen extends DefaultScreen {

    public static final String NAME= "AdminCreationScreen";

    public static final String TITLE_KEY="admin_creation_screen.title";
    private static final String SIDE_NAV_KEY = "admin_creation_screen.side_nav";
    public static final String ADMIN_DESCRIPTION_KEY="admin_creation_screen.admin_description";
    public static final String PASSWORD_REQUIREMENTS_KEY="admin_creation_screen.password_requirements";
    public static final String CREATE_ADMIN_KEY = "admin_creation_screen.create_admin_user";
    public static final String ADMIN_USERNAME = "admin_creation_screen.admin_username";
    public static final String ADMIN_PASSWORD = "admin_creation_screen.admin_password";
    public static final String ADMIN_CONFIRM_PASSWORD = "admin_creation_screen.admin_confirm_password";
    public static final int FIELD_NAME_WIDTH = 200;
    public static final int FIELD_SIZE = 17;
    private static final double LABEL_COL_WEIGHT = 0.4;
    private static final double FIELD_COL_WEIGHT = 0.8;
    private static final int EMPTY_COL_WEIGHT = 1;

    private final JTextField usernameTextField = createUsernameTextField();
    private final JPasswordField passwordField = createPasswordField(true);
    private final JPasswordField confirmPasswordField = createPasswordField(false);
    private final JLabel passwordSecurityWarning = createWarningLabel();

    private final AdminLoginValidator adminLoginValidator;
    private final PasswordSecurityValidator passwordSecurityValidator;

    public AdminCreationScreen(LanguageUtils langUtils) {
        super(langUtils);
        this.adminLoginValidator = new AdminLoginValidator(langUtils);
        this.passwordSecurityValidator = new PasswordSecurityValidator(langUtils);
    }

    @Override
    public String getTitle() {
        return langUtils.getString(TITLE_KEY);
    }

    @Override
    public String getName() {
        return NAME;
    }

    @Override
    public String getSideNavName() {
        return langUtils.getString(SIDE_NAV_KEY);
    }

    @Override
    public JPanel getContent() {
        JPanel content = new JPanel(new GridBagLayout());
        GridBagConstraints c = initializeConstraints();
        c.gridwidth = 3;

        c.insets = DESCRIPTION_INSET;
        content.add(createDescription(ADMIN_DESCRIPTION_KEY), c);
        c.gridy++;

        c.insets = new Insets(0,0,10,0);
        content.add(createDescription(PASSWORD_REQUIREMENTS_KEY), c);
        c.gridy++;
        content.add(createFieldLabel(CREATE_ADMIN_KEY), c);
        c.gridy++;
        content.add(emptyRow(), c);

        // build a table for credentials. 3rd column is used to take up excess space
        c.gridy++;
        c.gridwidth = 1;
        createCredentialsTableColumn(content, c, ADMIN_USERNAME, usernameTextField);

        c.gridx = 0;
        c.gridy++;
        c.insets = NO_INSET;
        createCredentialsTableColumn(content, c, ADMIN_PASSWORD, passwordField);

        c.gridy++;
        c.gridx = 0;
        createCredentialsTableColumn(content, c, ADMIN_CONFIRM_PASSWORD, confirmPasswordField);

        c.gridy++;
        c.gridx = 1;
        c.gridwidth = 2;
        content.add(passwordSecurityWarning, c);

        // filler to take up spare space
        c.gridy++;
        c.gridx = 0;
        c.gridwidth = 3;
        c.weighty = 1;
        content.add(Box.createHorizontalBox(), c);

        return content;
    }

    @Override
    public JComponent getDefaultFocusComponent() {
        return usernameTextField;
    }

    public void createCredentialsTableColumn(JPanel content, GridBagConstraints c, String adminUsername, JTextField usernameTextField) {
        c.weightx = LABEL_COL_WEIGHT;
        content.add(createFieldName(adminUsername), c);
        c.gridx = 1;
        c.weightx = FIELD_COL_WEIGHT;
        content.add(usernameTextField, c);
        c.gridx = 2;
        c.weightx = EMPTY_COL_WEIGHT;
        content.add(Box.createHorizontalBox(), c);
    }

    private JPasswordField createPasswordField(boolean isPrimaryField) {
        JPasswordField passwordField = new JPasswordField();
        passwordField.setColumns(FIELD_SIZE);
        addToolTip(passwordField, "admin_creation_screen.password.tooltip");
        if (isPrimaryField) {
            passwordField.getDocument().addDocumentListener(getDocumentListener());
        }
        return passwordField;
    }

    private JTextField createUsernameTextField() {
        JTextField usernameTextField = createTextField("admin");
        usernameTextField.setColumns(FIELD_SIZE);
        usernameTextField.getDocument().addDocumentListener(getDocumentListener());
        addToolTip(usernameTextField, "admin_creation_screen.username.tooltip");
        return usernameTextField;
    }


    private JLabel createFieldName(String labelText) {
        JLabel label = createFieldLabel(labelText);
        label.setPreferredSize(new Dimension(FIELD_NAME_WIDTH, ROW_HEIGHT));
        return label;
    }

    private JLabel createWarningLabel() {
        JLabel label = createFieldLabel("", false);
        label.setForeground(Color.RED);
        return label;
    }

    private DocumentListener getDocumentListener() {
        return new DocumentListener() {
            @Override
            public void insertUpdate(DocumentEvent documentEvent) {
                passwordSecurityCheck();
            }

            @Override
            public void removeUpdate(DocumentEvent documentEvent) {
                passwordSecurityCheck();
            }

            @Override
            public void changedUpdate(DocumentEvent documentEvent) {
                passwordSecurityCheck();
            }
        };
    }

    private void passwordSecurityCheck() {
        passwordSecurityWarning.setVisible(false);
        String password = getPasswordString(passwordField);
        String username = UiResources.readTextValue(usernameTextField);
        if (password.length() == 0) {
            return;
        }

        ValidationResult securityResult = passwordSecurityValidator.passwordSecurityCheck(username,password);
        if (securityResult.getResult().equals(ValidationResult.Result.OK)) {
            return;
        }

        // wrap the warning in html to enable wrapping of message
        passwordSecurityWarning.setText("<html><p>"+securityResult.getMessage()+"</p></html>");
        passwordSecurityWarning.setVisible(true);
    }

    private String getPasswordString(JPasswordField passwordField) {
        return String.valueOf(passwordField.getPassword());
    }

    @Override
    public ValidationResult validate() {
        ValidationResult mainValidation = adminLoginValidator.validate
                (usernameTextField.getText(), getPasswordString(passwordField), getPasswordString(confirmPasswordField));
        if (mainValidation.getResult().equals(ValidationResult.Result.ERROR)) {
            return mainValidation;
        }
        return passwordSecurityValidator.passwordValidationWarning
                (usernameTextField.getText(), getPasswordString(passwordField));
    }

    @Override
    public void record(InstallationData installationData) {
        installationData.setAdminUsername(usernameTextField.getText());
        installationData.setPassword(getPasswordString(passwordField));
        installationData.addPostInstallTask(PostInstallTask.AddAdminUser);
    }

    @Override
    public void load(InstallationData installationData) {
        if (installationData.getAdminUsername() != null) {
            usernameTextField.setText(installationData.getAdminUsername());
            passwordField.setText(installationData.getPassword());
        }
    }

    @Override
    public void rollback(InstallationData installationData) {
        installationData.removePostInstallTask(PostInstallTask.AddAdminUser);
        // reset password confirmation to check that the user remembers previously entered value - https://bugzilla.redhat.com/show_bug.cgi?id=1088152#c3
        this.confirmPasswordField.setText("");
    }
}
