package hsr.dsa.controller;

import java.io.IOException;
import java.util.List;

import hsr.dsa.blockchain.BlockChainClient;
import hsr.dsa.blockchain.exceptions.BlockChainException;
import hsr.dsa.data.DataManager;
import hsr.dsa.data.DataManagerException;
import hsr.dsa.data.models.Group;
import hsr.dsa.data.models.LocalUser;
import hsr.dsa.data.models.User;
import hsr.dsa.local.InvalidDataException;
import hsr.dsa.network.ChatClient;
import hsr.dsa.utils.Observable;
import hsr.dsa.utils.Observer;

public class ChatController implements Observer {
	private final DataManager dataManager;
	private ChatClient chatClient;
	private BlockChainClient blockChainClient;
	private LocalUser user;

	public ChatController() throws IOException {
		this.dataManager = new DataManager();
	}

	public List<String> getLoginProfiles() {
		return dataManager.getProfiles();
	}

	public LocalUser getLoginProfile(String profile, String password) {
		try {
			return this.dataManager.getProfile(profile, password);
		} catch (Exception e) {
			throw new IllegalStateException(e);
		}
	}

	public LocalUser getProfile() {
		return this.user;
	}

	private String validateName(String profileName) {
		profileName = profileName.trim().replaceAll("[\\s]{2,}", " ");
		if (profileName.length() < 4) {
			throw new IllegalArgumentException("minimum name length is 5 given is " + profileName.length());
		} else if (!profileName.matches("[\\w ]{4,}")) {
			throw new IllegalArgumentException("only letters, numbers and spaces are allowed");
		}
		return profileName;
	}

	public User createProfile(String profileName, String password, String blockChainPrivateKey)
			throws InvalidDataException, Exception {
		profileName = this.validateName(profileName);
		LocalUser user = this.dataManager.createProfile(profileName, password, blockChainPrivateKey);
		this.login(user, password);
		return user;
	}
	
	public void createGroup(String groupName) throws ClassNotFoundException, IOException {
		this.chatClient.createGroup(this.validateName(groupName), (Group group) -> {
			this.user.getGroups().add(group);
		});
		this.dataManager.saveProfile(this.user);
	}

	public void login(LocalUser profile, String password) {
		this.user = profile;
		this.user.addObserver(this);
		this.dataManager.login(profile, password);
		try {
			this.blockChainClient = new BlockChainClient(profile.getBlockChainPrivateKey(), user);
		} catch (BlockChainException e) {
			e.printStackTrace();
			this.blockChainClient = null;
		}
		this.chatClient = new ChatClient(this.dataManager.getNetworkDataClient(), this.user, this.blockChainClient);
	}

	public void logout() throws BlockChainException, IOException, DataManagerException  {
		this.dataManager.logout();
		this.chatClient.close();
		this.chatClient = null;
		this.blockChainClient.close();
		this.blockChainClient = null;
		this.user = null;
	}

	public void stop() {
		this.dataManager.saveProfile(this.user);
		this.dataManager.stop();
	}

	public void sendFriendRequest(String friendName) throws IOException {
		friendName = this.validateName(friendName);
		this.chatClient.createFriendRequest(friendName);
	}
	
	public void sendGroupInvitationRequest(Group group, String username) throws IOException {
		username = this.validateName(username);
		this.chatClient.inviteToGroup(username, group);
	}

	public ChatClient getClient() {
		return this.chatClient;
	}

	public BlockChainClient getBlockChainClient() {
		return blockChainClient;
	}

	public void setBlockChainClient(BlockChainClient blockChainClient) {
		this.blockChainClient = blockChainClient;
	}

	@Override
	public void onChange(Observable observable) {
		this.dataManager.saveProfile(this.user);
	}
}
