package be.iminds.ilabt.jfed.util;

import be.iminds.ilabt.jfed.lowlevel.GeniUser;
import be.iminds.ilabt.jfed.fedmon.webapi.service.json.*;
import be.iminds.ilabt.jfed.preferences.JFedCorePreferences;
import be.iminds.ilabt.jfed.preferences.JFedPreferences;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.testng.annotations.Test;

import java.io.File;
import java.io.IOException;
import java.security.NoSuchAlgorithmException;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.cert.X509Certificate;
import java.util.List;
import java.util.Random;

import static org.mockito.Mockito.doReturn;
import static org.testng.Assert.*;

import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.*;

import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;

public class JFedPasswordManagerTest {
    private static final Logger LOG = LoggerFactory.getLogger(JFedPasswordManagerTest.class);

    @Test
    public void testGetLogins() {
        JFedPasswordManager m = new JFedPasswordManager();
        m.addLogin(new JFedPasswordManager.LoginInfo("s1", "s1user1", "s1pass1"));
        m.addLogin(new JFedPasswordManager.LoginInfo("s1", "s1user2", "s1pass2"));
        m.addLogin(new JFedPasswordManager.LoginInfo("s2", "s2user3", "s2pass3"));
        m.addLogin(new JFedPasswordManager.LoginInfo("s2", "s2user4", "s2pass4"));
        m.addLogin(new JFedPasswordManager.LoginInfo("s3", "s3user5", "s3pass5"));

        List<JFedPasswordManager.LoginInfo> s1logins = m.getLogins("s1");
        List<JFedPasswordManager.LoginInfo> s2logins = m.getLogins("s2");
        List<JFedPasswordManager.LoginInfo> s3logins = m.getLogins("s3");
        List<JFedPasswordManager.LoginInfo> alllogins = m.getLogins();

        assertThat(s1logins, hasSize(2));
        assertThat(s2logins, hasSize(2));
        assertThat(s3logins, hasSize(1));
        assertThat(m.getLogins("X"), hasSize(0));
        assertThat(m.getLogins("1"), hasSize(0));
        assertThat(m.getLogins("s"), hasSize(0));
        assertThat(alllogins, hasSize(5));

        JFedPasswordManager.LoginInfo l1 = new JFedPasswordManager.LoginInfo("s1", "s1user1", "s1pass1");
        JFedPasswordManager.LoginInfo l2 = new JFedPasswordManager.LoginInfo("s1", "s1user2", "s1pass2");
        JFedPasswordManager.LoginInfo l3 = new JFedPasswordManager.LoginInfo("s2", "s2user3", "s2pass3");
        JFedPasswordManager.LoginInfo l4 = new JFedPasswordManager.LoginInfo("s2", "s2user4", "s2pass4");
        JFedPasswordManager.LoginInfo l5 = new JFedPasswordManager.LoginInfo("s3", "s3user5", "s3pass5");
        assertThat(s1logins, contains(l1, l2));
        assertThat(s2logins, contains(l3, l4));
        assertThat(s3logins, contains(l5));
        assertThat(alllogins, contains(l1, l2, l3, l4, l5));
    }

    @Test
    public void testOverwriteLogins() {
        JFedPasswordManager m = new JFedPasswordManager();
        m.addLogin(new JFedPasswordManager.LoginInfo("s1", "s1user1", "s1pass1"));
        m.addLogin(new JFedPasswordManager.LoginInfo("s1", "s1user2", "s1pass2"));
        m.addLogin(new JFedPasswordManager.LoginInfo("s2", "s2user3", "s2pass3"));
        m.addLogin(new JFedPasswordManager.LoginInfo("s1", "s1user1", "pass4"));
        m.addLogin(new JFedPasswordManager.LoginInfo("s1", "s2user3", "pass5"));

        List<JFedPasswordManager.LoginInfo> s1logins = m.getLogins("s1");
        List<JFedPasswordManager.LoginInfo> s2logins = m.getLogins("s2");
        List<JFedPasswordManager.LoginInfo> s3logins = m.getLogins("s3");
        List<JFedPasswordManager.LoginInfo> alllogins = m.getLogins();

        assertThat(s1logins, hasSize(3));
        assertThat(s2logins, hasSize(1));
        assertThat(s3logins, hasSize(0));
        assertThat(alllogins, hasSize(4));

        JFedPasswordManager.LoginInfo l1 = new JFedPasswordManager.LoginInfo("s1", "s1user1", "s1pass1");
        JFedPasswordManager.LoginInfo l2 = new JFedPasswordManager.LoginInfo("s1", "s1user2", "s1pass2");
        JFedPasswordManager.LoginInfo l3 = new JFedPasswordManager.LoginInfo("s2", "s2user3", "s2pass3");
        JFedPasswordManager.LoginInfo l4 = new JFedPasswordManager.LoginInfo("s1", "s1user1", "pass4");
        JFedPasswordManager.LoginInfo l5 = new JFedPasswordManager.LoginInfo("s1", "s2user3", "pass5");
        assertThat(s1logins, contains(l2, l4, l5));
        assertThat(s2logins, contains(l3));
        assertThat(alllogins, contains(l2, l3, l4, l5));
    }

    @Test
    public void testSaveLoadLogins() throws NoSuchAlgorithmException, IOException {
        JFedPasswordManager mo = new JFedPasswordManager();
        mo.addLogin(new JFedPasswordManager.LoginInfo("s1", "s1user1", "s1pass1"));
        mo.addLogin(new JFedPasswordManager.LoginInfo("s1", "s1user2", "s1pass2"));
        mo.addLogin(new JFedPasswordManager.LoginInfo("s2", "s2user3", "s2pass3"));
        mo.addLogin(new JFedPasswordManager.LoginInfo("s2", "s2user4", "s2pass4"));
        mo.addLogin(new JFedPasswordManager.LoginInfo("s3", "s3user5", "s3pass5"));

        SSHKeyHelper sshKeyHelper = new SSHKeyHelper();
        int r = new Random().nextInt();
        GeniUrn fakeUserUrn = GeniUrn.createGeniUrnFromDecodedParts("example.com", "user", "fakeForTest"+r);
        GeniUser fakeUser = mock(GeniUser.class);
        when(fakeUser.getPrivateKey()).thenReturn(sshKeyHelper.getSshPrivateKey());
        when(fakeUser.getPublicKey()).thenReturn(sshKeyHelper.getSshPublicKey());
        when(fakeUser.getUserUrnString()).thenReturn(fakeUserUrn+"");
        when(fakeUser.getUserUrn()).thenReturn(fakeUserUrn);
        JFedPreferences prefs = mock(JFedPreferences.class);

        mo.save(prefs, fakeUser);

        File saveFile = mo.getEncryptedSaveFile(prefs, fakeUser);
        assertTrue(saveFile.exists());

        JFedPasswordManager m = new JFedPasswordManager();
        m.load(prefs, fakeUser);
        LOG.debug("Loaded "+m.getLogins().size()+" logins from file.");

        //cleanup file already
        saveFile.delete();

        List<JFedPasswordManager.LoginInfo> s1logins = m.getLogins("s1");
        List<JFedPasswordManager.LoginInfo> s2logins = m.getLogins("s2");
        List<JFedPasswordManager.LoginInfo> s3logins = m.getLogins("s3");
        List<JFedPasswordManager.LoginInfo> alllogins = m.getLogins();

        assertThat(s1logins, hasSize(2));
        assertThat(s2logins, hasSize(2));
        assertThat(s3logins, hasSize(1));
        assertThat(m.getLogins("X"), hasSize(0));
        assertThat(m.getLogins("1"), hasSize(0));
        assertThat(m.getLogins("s"), hasSize(0));
        assertThat(alllogins, hasSize(5));

        JFedPasswordManager.LoginInfo l1 = new JFedPasswordManager.LoginInfo("s1", "s1user1", "s1pass1");
        JFedPasswordManager.LoginInfo l2 = new JFedPasswordManager.LoginInfo("s1", "s1user2", "s1pass2");
        JFedPasswordManager.LoginInfo l3 = new JFedPasswordManager.LoginInfo("s2", "s2user3", "s2pass3");
        JFedPasswordManager.LoginInfo l4 = new JFedPasswordManager.LoginInfo("s2", "s2user4", "s2pass4");
        JFedPasswordManager.LoginInfo l5 = new JFedPasswordManager.LoginInfo("s3", "s3user5", "s3pass5");
        assertThat(s1logins, contains(l1, l2));
        assertThat(s2logins, contains(l3, l4));
        assertThat(s3logins, contains(l5));
        assertThat(alllogins, contains(l1, l2, l3, l4, l5));
    }
}