#include <string>
#include <iostream>
#include <sstream>
#include <fstream>
#include <cstring>
#include <filesystem>
#include <vector>
#include <algorithm>

#include "arg_func.hpp"
#include "func.hpp"
#include "environment.hpp"
#include "func.hpp"
#include "buffer.hpp"
#include "aes256.hpp"
#include "editor.hpp"

void print_args()
{
	printf(" Usage:\n\n");
	printf(" password_manager.exe [flags]\n\n");
	printf(" Flags:\n\n");
	printf(" -h                   print this message\n");
	printf(" <label>              get password of this label can use GLOB\n");
	printf(" -g <label>           generate password of this label (or update if exists)\n");
	printf(" -i <label>           input new password for this label (or update if exists)\n");
	printf(" -d <label>           delete password of this label\n");
	printf(" -s <label>           show password for this label\n");
	printf(" -u <label>           update username for this label\n");
	printf(" -n <label>           update label name\n");
	printf(" -e <label>           open editor, password as note\n");
	printf(" -l                   list all labels\n");
	printf(" -p                   print all passwords\n");
	printf(" -c                   change master password\n");
	printf(" -z                   print size of file\n");
	printf(" -f <folder path>     select save folder\n");
}

Arg get_args(int argc, char **argv, char **label)
{
	if (argc < 2)
	{
		print_args();
		return Arg::Error;
	}

	if (argv[1][0] != '-')
	{
		*label = argv[1];
		return Arg::Get;
	}

	switch (argv[1][1])
	{
	case 'h':
		print_args();
		return Arg::Error;
	case 'l':
		return Arg::List;
	case 'p':
		return Arg::Print_all_p;
	case 'c':
		return Arg::Change;
	case 'z':
		return Arg::Size;
	}

	if (argc != 3)
		return Arg::Error;

	*label = argv[2];

	switch (argv[1][1])
	{
	case 'g':
		return Arg::Generate;
	case 'd':
		return Arg::Delete;
	case 'i':
		return Arg::Input;
	case 's':
		return Arg::Show;
	case 'u':
		return Arg::Username;
	case 'n':
		return Arg::Name;
	case 'f':
		return Arg::File;
	case 'e':
		return Arg::Editor;

	default:
		return Arg::Error;
		break;
	}

	return Arg::Error;
}

std::optional<LoginInfoPointer> arg_get(Buffer &decrypted_buffer, const char *label)
{
	int pass = find_logininfo_in_buffer(decrypted_buffer, label);
	if (pass < 0)
	{
		printf("Password not found\n");
		return {};
	}
	return get_logininfo_pointer_from_buffer(decrypted_buffer, pass);
}

std::optional<LoginInfoPointer> arg_new_password(Buffer &decrypted_buffer, Buffer &encrypted_buffer, const char *label, std::string &key, bool generate)
{
	int pass = find_logininfo_in_buffer(decrypted_buffer, label);
	std::string username = "";
	std::string name = label;
	std::string password = "";

	if (pass >= 0)
	{
		LoginInfoPointer lip = get_logininfo_pointer_from_buffer(decrypted_buffer, pass);
		username = lip.username;
		name = lip.label;
		delete_logininfo_from_buffer(decrypted_buffer, pass);
	}

	printf("New password for %s: \n", name.c_str());

	if (pass < 0)
	{
		printf("Input username: ");
		std::getline(std::cin, username);
	}

	if (generate)
	{
		int default_length = 15;
		printf("Input password length [%d]: ", default_length);
		std::string input;
		int length = default_length;

		std::getline(std::cin, input);
		if (!input.empty())
		{
			std::istringstream iss(input);
			if (!(iss >> length))
				length = default_length;
		}

		if (!generate_password(password, length))
		{
			printf("error generating password\n");
			return {};
		}
	}
	else
	{
		printf("Input new password: ");
		password = get_user_password();
		if (password.empty())
		{
			printf("error getting password\n");
			return {};
		}
	}

	add_logininfo_to_buffer(decrypted_buffer, name.c_str(), name.size(), username.c_str(), username.size(), password.c_str(), password.size());
	Aes256::encrypt(key, decrypted_buffer, encrypted_buffer);
	encrypted_buffer.save_to_file();
	Index *index = (Index *)decrypted_buffer.buffer;
	return get_logininfo_pointer_from_buffer(decrypted_buffer, index->count - 1);
}

void arg_username(Buffer &decrypted_buffer, Buffer &encrypted_buffer, const char *label, std::string &key)
{
	int pass = find_logininfo_in_buffer(decrypted_buffer, label);
	if (pass < 0)
	{
		printf("LoginInfo not found\n");
		return;
	}

	LoginInfoPointer lip = get_logininfo_pointer_from_buffer(decrypted_buffer, pass);
	std::string password = lip.password;
	std::string name = lip.label;

	printf("Input username for %s: ", name.c_str());
	std::string username;
	std::getline(std::cin, username);

	delete_logininfo_from_buffer(decrypted_buffer, pass);
	add_logininfo_to_buffer(decrypted_buffer, name.c_str(), name.size(), username.c_str(), username.size(), password.c_str(), password.size());
	Aes256::encrypt(key, decrypted_buffer, encrypted_buffer);
	encrypted_buffer.save_to_file();
}

void arg_label_name(Buffer &decrypted_buffer, Buffer &encrypted_buffer, const char *label, std::string &key)
{
	int pass = find_logininfo_in_buffer(decrypted_buffer, label);

	if (pass < 0)
	{
		printf("LoginInfo not found\n");
		return;
	}

	LoginInfoPointer lip = get_logininfo_pointer_from_buffer(decrypted_buffer, pass);
	std::string password = lip.password;
	std::string username = lip.username;

	printf("Input new label name for %s:", lip.label);
	std::string name;
	std::getline(std::cin, name);

	delete_logininfo_from_buffer(decrypted_buffer, pass);
	add_logininfo_to_buffer(decrypted_buffer, name.c_str(), name.size(), username.c_str(), username.size(), password.c_str(), password.size());
	Aes256::encrypt(key, decrypted_buffer, encrypted_buffer);
	encrypted_buffer.save_to_file();
}

void arg_list(Buffer &decrypted_buffer)
{
	Index *index = (Index *)decrypted_buffer.buffer;
	LoginInfo *pass = (LoginInfo *)(decrypted_buffer.buffer + sizeof(Index));

	std::vector<std::string> list;

	for (size_t i = 0; i < index->count; i++)
	{
		list.emplace_back(std::string((char *)decrypted_buffer.buffer + pass[i].label + index->offset));
	}

	std::sort(list.begin(), list.end());

	for (auto &&str : list)
	{
		printf("%s\n", str.c_str());
	}
}

void arg_delete(Buffer &decrypted_buffer, Buffer &encrypted_buffer, const char *label_to_del, std::string &key)
{
	printf("Deleting password for %s\n", label_to_del);
	int pass = find_logininfo_in_buffer(decrypted_buffer, label_to_del);
	if (pass < 0)
	{
		printf("Password not found\n");
		return;
	}

	delete_logininfo_from_buffer(decrypted_buffer, pass);

	Aes256::encrypt(key, decrypted_buffer, encrypted_buffer);
	encrypted_buffer.save_to_file();
	printf("Password deleted\n");
}

void arg_print_all_p(Buffer &decrypted_buffer, std::string &user_pass)
{
	printf("Input main password for confirmation:");
	std::string new_string = get_user_password();
	if (new_string.empty())
	{
		printf("Error getting password\n");
		return;
	}
	if (new_string != user_pass)
	{
		printf("Wrong password\n");
		return;
	}

	Index *index = (Index *)decrypted_buffer.buffer;
	LoginInfo *pass = (LoginInfo *)(decrypted_buffer.buffer + sizeof(Index));

	printf("\n");
	for (size_t i = 0; i < index->count; i++)
	{
		printf("label: %-20s\t", decrypted_buffer.buffer + pass[i].label + index->offset);
		printf("username: %-30s\t", decrypted_buffer.buffer + pass[i].username + index->offset);
		printf("password: %s\n", decrypted_buffer.buffer + pass[i].password + index->offset);
	}
}

void arg_change(Buffer &decrypted_buffer, Buffer &encrypted_buffer, std::string &user_pass)
{
	printf("Input main password for confirmation:");

	std::string new_string = get_user_password();
	if (new_string.empty())
	{
		printf("Error getting password\n");
		return;
	}

	if (new_string != user_pass)
	{
		printf("Passwords don't match\n");
		return;
	}

	printf("Input new password:");
	new_string = get_user_password();
	if (new_string.empty())
	{
		printf("Error getting password\n");
		return;
	}

	printf("Input new password again:");
	user_pass = get_user_password();
	if (user_pass.empty())
	{
		printf("Error getting password\n");
		return;
	}

	if (new_string != user_pass)
	{
		printf("Passwords don't match\n");
		return;
	}

	Aes256::encrypt(new_string, decrypted_buffer, encrypted_buffer);
	encrypted_buffer.save_to_file();
	printf("Password changed\n");
}

void arg_show(Buffer &decrypted_buffer, const char *label)
{

	int pass = find_logininfo_in_buffer(decrypted_buffer, label);

	if (pass < 0)
		printf("Password not found\n");
	else
	{
		LoginInfoPointer lip = get_logininfo_pointer_from_buffer(decrypted_buffer, pass);
		printf("Username: %s\n", lip.username);
		printf("Password: %s\n", lip.password);
	}
}

void arg_file(Buffer &decrypted_buffer, Buffer &encrypted_buffer, const char *label, std::string &key, std::string &save_location_path)
{

	namespace fs = std::filesystem;

	fs::path save(label);
	save /= "passwords.bin";

	std::ofstream file(save_location_path, std::ios::binary);
	file << save.string() << std::endl;
	file.close();

	if (decrypted_buffer.taken <= sizeof(Index))
		return;

	Aes256::encrypt(key, decrypted_buffer, encrypted_buffer);
	encrypted_buffer.save_to_file(save.string());
}

void arg_size(Buffer &encrypted_buffer)
{
	printf("File size is: %zuB\n", encrypted_buffer.taken);
}

void arg_editor(Buffer &decrypted_buffer, Buffer &encrypted_buffer, const char *label_to_del, std::string &key)
{
	int pass = find_logininfo_in_buffer(decrypted_buffer, label_to_del);
	if (pass < 0)
	{
		printf("Password not found\n");
		return;
	}

	LoginInfoPointer lip = get_logininfo_pointer_from_buffer(decrypted_buffer, pass);
	int len = 0;
	char *new_buff = edit_file(lip.password, len);
	if (new_buff == nullptr)
	{
		return;
	}

	std::string lable = lip.label;
	std::string username = lip.username;

	delete_logininfo_from_buffer(decrypted_buffer, pass);
	add_logininfo_to_buffer(decrypted_buffer, lable.c_str(), lable.size(), username.c_str(), username.size(), new_buff, len);

	Aes256::encrypt(key, decrypted_buffer, encrypted_buffer);
	encrypted_buffer.save_to_file();
}