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

#include "arg_func.hpp"
#include "func.hpp"
#include "sys.hpp"
#include "func.hpp"
#include "buffer.hpp"
#include "cryptography.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(" -l                   list all labels\n");
	printf(" -p                   print all passwords\n");
	printf(" -c                   change master password\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 (!strcmp("-h", argv[1]))
	{
		print_args();
		return Arg::Error;
	}

	if (!strcmp("-l", argv[1]))
		return Arg::List;
	if (!strcmp("-p", argv[1]))
		return Arg::Print_all_p;
	if (!strcmp("-c", argv[1]))
		return Arg::Change;

	if (argc < 3)
	{
		*label = argv[1];
		return Arg::Get;
	}

	*label = argv[2];

	if (!strcmp("-g", argv[1]))
		return Arg::Generate;
	if (!strcmp("-d", argv[1]))
		return Arg::Delete;
	if (!strcmp("-i", argv[1]))
		return Arg::Input;
	if (!strcmp("-s", argv[1]))
		return Arg::Show;
	if (!strcmp("-u", argv[1]))
		return Arg::Username;
	if (!strcmp("-n", argv[1]))
		return Arg::Name;
	if (!strcmp("-f", argv[1]))
		return Arg::File;

	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, Cryptography &crypto, 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;
		}

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

	add_logininfo_to_buffer(decrypted_buffer, name.c_str(), username.c_str(), password.c_str());
	crypto.encrypt(&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, Cryptography &crypto)
{
	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(), username.c_str(), password.c_str());
	crypto.encrypt(&decrypted_buffer, &encrypted_buffer);
	encrypted_buffer.save_to_file();
}

void arg_label_name(Buffer &decrypted_buffer, Buffer &encrypted_buffer, const char *label, Cryptography &crypto)
{
	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(), username.c_str(), password.c_str());
	crypto.encrypt(&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, Cryptography &crypto)
{
	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);

	crypto.encrypt(&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: %-10s\t\t", decrypted_buffer.buffer + pass[i].label + index->offset);
		printf("username: %-10s\t\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, Cryptography &crypto)
{
	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;
	}

	crypto.change_pass(new_string);
	crypto.encrypt(&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, Cryptography &crypto, 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;

	crypto.encrypt(&decrypted_buffer, &encrypted_buffer);
	encrypted_buffer.save_to_file(save.string());
}