package com.jscape.inet.ssh.protocol.v2.authentication;

import com.jscape.inet.ssh.protocol.messages.Message;
import com.jscape.inet.ssh.protocol.v2.marshaling.MessageCodec;
import com.jscape.inet.ssh.protocol.v2.messages.SshMsgUserAuthInfoRequest;
import com.jscape.inet.ssh.protocol.v2.messages.SshMsgUserAuthInfoResponse;
import com.jscape.inet.ssh.protocol.v2.messages.SshMsgUserAuthRequestKeyboardInteractive;
import com.jscape.inet.ssh.protocol.v2.messages.UserAuthInfoRequestPrompt;
import com.jscape.inet.ssh.protocol.v2.transport.TransportConnection;

import java.util.Collections;
import java.util.List;

import static com.jscape.util.Parameters.assertNotNull;

/**
 * SSH keyboard-interactive authentication base. Provides support for client authentications of keyboard-interactive type.
 *
 * @author Alex Usun
 */
public abstract class AbstractKeyboardInteractiveClientAuthentication
        extends AbstractClientAuthentication {

    protected static final String LANGUAGE_TAG = "en";

    protected final String username;

    /**
     * Creates a new operation instance.
     *
     * @param username an username
     */
    protected AbstractKeyboardInteractiveClientAuthentication(MessageCodec.Factory codecFactory, String username) {
        super(codecFactory);

        assertNotNull(username);
        this.username = username;
    }

    @Override
    public void applyTo(TransportConnection connection, String serviceName)
            throws Exception {
        init(connection);
        sendRequest(connection, serviceName);

        do {
            Message response = receive(connection);

            if (response instanceof SshMsgUserAuthInfoRequest) {
                handle((SshMsgUserAuthInfoRequest) response, connection);
            } else {
                assertSuccess(response);
                break;
            }
        } while (true);
    }

    @Override
    protected void init(TransportConnection connection) {
        super.init(connection);
        this.codecFactory.update(connection.session().messageCodec);
    }

    protected void sendRequest(TransportConnection connection, String serviceName)
            throws Exception {
        connection.write(new SshMsgUserAuthRequestKeyboardInteractive(this.username, serviceName, LANGUAGE_TAG, Collections.<String>emptyList()));
    }

    protected void handle(SshMsgUserAuthInfoRequest response, TransportConnection connection)
            throws Exception {
        List<String> responses = responsesFor(response.name, response.instruction, response.prompts);
        connection.write(new SshMsgUserAuthInfoResponse(responses));
    }

    protected abstract List<String> responsesFor(String name, String instruction, List<UserAuthInfoRequestPrompt> prompts)
            throws Exception;
}
