package local;

import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertTrue;

import java.io.File;
import java.io.IOException;
import java.lang.reflect.Field;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.security.NoSuchAlgorithmException;
import java.security.spec.InvalidKeySpecException;
import java.time.LocalDateTime;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import java.util.TreeMap;

import javax.crypto.NoSuchPaddingException;

import org.apache.commons.io.FileUtils;
import org.junit.jupiter.api.Test;

import hsr.dsa.local.ChatHistory;
import hsr.dsa.local.ChatMessage;
import hsr.dsa.local.Friend;
import hsr.dsa.local.LocalData;
import hsr.dsa.local.InvalidDataException;
import hsr.dsa.local.Profile;

class LocalDataTest {

	private String pathTestDirectories = "./test/";

	private LocalData localData;

	private String user1 = "user1";
	private String password1 = "password1";
	private String bcAddress1 = "bcAddress1";

	@org.junit.jupiter.api.BeforeEach
	void setUp() {

		try {
			localData = new LocalData();
		} catch (IOException e) {
			e.printStackTrace();
		}

		try {
			Field profilePath = localData.getClass().getDeclaredField("profilesFolder");
			profilePath.setAccessible(true);
			profilePath.set(localData, "./test/");
			profilePath.setAccessible(false);
		} catch (NoSuchFieldException | IllegalAccessException e) {
			e.printStackTrace();
		}
	}

	@Test
	void createProfile() {

		Profile newProfile = null;
		try {
			newProfile = localData.createProfileData(user1, password1, bcAddress1);
		} catch (Exception e) {
			e.printStackTrace();
		}

		assertEquals(user1, newProfile.getUsername());
	}

	@Test
	void loadProfileDecrypted() {
		try {
			localData.createProfileData(user1, password1, bcAddress1);
		} catch (Exception e) {
			e.printStackTrace();
		}

		Profile loadedProfile = null;
		try {
			loadedProfile = localData.loadProfileData(user1, password1);

		} catch (Exception e) {
			e.printStackTrace();
		}

		assertEquals(user1, loadedProfile.getUsername());
	}

	@Test
	void loadProfileEncrypted() {
		try {
			localData = new LocalData();
		} catch (IOException e) {
			e.printStackTrace();
		}

		try {
			localData.createProfileData(user1, password1, bcAddress1);
		} catch (Exception e) {
			e.printStackTrace();
		}

		try {
			localData.closeProfiles();
		} catch (Exception e) {
			e.printStackTrace();
		}

		try {
			FileUtils.deleteDirectory(new File(pathTestDirectories + user1 + "/decrypted"));
		} catch (IOException e) {
			e.printStackTrace();
		}

		Profile loadedProfile = null;
		try {
			loadedProfile = localData.loadProfileData(user1, password1);
		} catch (Exception e) {
			e.printStackTrace();
		}

		assertEquals(user1, loadedProfile.getUsername());
	}

	@Test
	void saveProfile() throws IOException {
		Profile newProfile = null;
		try {
			newProfile = localData.createProfileData(user1, password1, bcAddress1);
		} catch (Exception e) {
			e.printStackTrace();
		}

		String newFriendName = "newFriend";
		Friend newFriend = new Friend(newFriendName, "");
		newProfile.addFriend(newFriend);
		localData.saveProfileData(newProfile);

		Profile loadedProfile = null;

		try {
			loadedProfile = localData.loadProfileData(user1, password1);
		} catch (Exception e) {
			e.printStackTrace();
		}

		assertTrue(loadedProfile.getFriends().contains(newFriend));
	}

	@Test
	void deleteProfile() {
		try {
			localData.createProfileData(user1, password1, bcAddress1);
		} catch (Exception e) {
			e.printStackTrace();
		}

		String user2 = "user2";
		String password2 = "password2";
		String bcAddress2 = "bcAddress2";
		try {
			localData.createProfileData(user2, password2, bcAddress2);
		} catch (Exception e) {
			e.printStackTrace();
		}

		try {
			localData.deleteProfileData(user1);
		} catch (IOException e) {
			e.printStackTrace();
		}

		assertTrue(new File(pathTestDirectories + user2).exists());
		assertTrue(!new File(pathTestDirectories + user1).exists());
	}

	@Test
	void getListOfAllProfiles() {
		String user1 = "user1";
		String user2 = "user2";

		try {
			Files.createDirectories(Paths.get(pathTestDirectories + user1));
			Files.createDirectories(Paths.get(pathTestDirectories + user2));
		} catch (IOException e) {
			e.printStackTrace();
		}

		List<String> listProfile = null;
		try {
			listProfile = localData.getProfileList();
		} catch (InvalidDataException e) {
			e.printStackTrace();
		}
		Iterator<String> it = listProfile.iterator();

		assertEquals(user1, it.next());
		assertEquals(user2, it.next());

		try {
			Files.delete(Paths.get(pathTestDirectories + user1));
			Files.delete(Paths.get(pathTestDirectories + user2));
			Files.delete(Paths.get(pathTestDirectories));
		} catch (IOException e) {
			e.printStackTrace();
		}
	}

	@Test
	void saveAndCloseProfile() {
		Profile newProfile = null;
		try {
			newProfile = localData.createProfileData(user1, password1, bcAddress1);
		} catch (Exception e) {
			e.printStackTrace();
		}

		try {
			localData.saveProfileData(newProfile);
		} catch (IOException e) {
			e.printStackTrace();
		}
	}

	@Test
	void getPublicPrivateKeyInstanceUser() {

		try {
			localData.createProfileData(user1, password1, bcAddress1);
		} catch (Exception e) {
			e.printStackTrace();
		}

		try {
			localData.getPublicPrivateKeyInstanceUser(user1);
		} catch (InvalidKeySpecException e) {
			e.printStackTrace();
		} catch (NoSuchAlgorithmException e) {
			e.printStackTrace();
		} catch (NoSuchPaddingException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		}
	}

	@Test
	void saveInformationInChatHistory() throws IOException {
		Profile newProfile = null;
		try {
			newProfile = localData.createProfileData(user1, password1, bcAddress1);
		} catch (Exception e) {
			e.printStackTrace();
		}

		String friend = "user2";
		String message1 = "hello1";
		String message2 = "hello2";
		ChatMessage chatMsg1 = new ChatMessage("user1", message1);
		ChatMessage chatMsg2 = new ChatMessage(friend, message2);

		TreeMap<LocalDateTime, ChatMessage> chatHistory = new TreeMap<>();
		chatHistory.put(LocalDateTime.now(), chatMsg1);
		chatHistory.put(LocalDateTime.now(), chatMsg2);

		ChatHistory newHistory = new ChatHistory();
		newHistory.setMessageHistory(chatHistory);

		Friend friend2 = new Friend(friend, "");
		friend2.setChatHistory(newHistory);

		newProfile.addFriend(friend2);

		localData.saveProfileData(newProfile);

		Profile loadedProfile = null;

		try {
			loadedProfile = localData.loadProfileData(user1, password1);
		} catch (Exception e) {
			e.printStackTrace();
		}

		assertTrue(loadedProfile.getFriends().contains(friend2));

		TreeMap<LocalDateTime, ChatMessage> messages = loadedProfile.getFriends()
				.get(loadedProfile.getFriends().indexOf(friend2)).getChatHistory().getAllMessageHistory();

		Set<Entry<LocalDateTime, ChatMessage>> set = messages.entrySet();

		Iterator<Entry<LocalDateTime, ChatMessage>> message = set.iterator();

		Map.Entry<LocalDateTime, ChatMessage> ent1 = message.next();
		assertEquals(ent1.getValue().getMessage(), message1);

		Map.Entry<LocalDateTime, ChatMessage> ent2 = message.next();
		assertEquals(ent2.getValue().getMessage(), message2);
	}

	/*
	 * @Test void getPublicPrivateKeyInstanceBlockChain() {
	 * 
	 * try { localData.createProfileData(user1, password1); } catch (Exception e) {
	 * e.printStackTrace(); }
	 * 
	 * try { localData.getPublicPrivateKeyInstanceBlockChain(user1); } catch
	 * (InvalidKeySpecException e) { e.printStackTrace(); } catch
	 * (NoSuchAlgorithmException e) { e.printStackTrace(); } catch
	 * (NoSuchPaddingException e) { e.printStackTrace(); } catch (IOException e) {
	 * e.printStackTrace(); }
	 * 
	 * }
	 */

	@org.junit.jupiter.api.AfterEach
	void tearDown() {
		try {
			FileUtils.deleteDirectory(new File(pathTestDirectories));
		} catch (IOException e) {
			e.printStackTrace();
		}
	}
}
