/*
 * Mirai. Copyright (C) 2024 Vyn
 * This file is licensed under version 3 of the GNU General Public License (GPL-3.0-only)
 * The license can be found in the LICENSE file or at https://www.gnu.org/licenses/gpl-3.0.txt
 */

#include "MarkdownDataProvider.h"
#include "cpp-utils/debug.h"
#include "utils.h"
#include <regex>

namespace mirai
{

/*Tags TodoMdFormat::extractTagsFromMetadata(std::string metadata)*/
/*{*/
/*Tags tags;*/

/*std::regex regex("(#([a-zA-Z0-1]+))");*/
/*std::smatch matches;*/

/*while (std::regex_search(metadata, matches, regex)) {*/
/*if (!vectorUtils::contains(tags, matches[2].str())) {*/
/*tags.push_back(matches[2]);*/
/*}*/
/*metadata = matches.suffix();*/
/*}*/
/*return tags;*/
/*}*/

std::string fieldWithSpace(const std::string &field)
{
	if (field.length() == 0) {
		return "";
	}
	return (field + " ");
}

TaskData stringToTask(const std::string &str, const std::string &date)
{
	std::smatch matches;
	std::regex regex("- \\[(\\s|X)\\] (([0-9]{2}:[0-9]{2})-([0-9]{2}:[0-9]{2}) > )?(.*?)( -- (.*))?"
	);
	std::regex_match(str, matches, regex);

	/*std::cout << "line " << str << std::endl;*/
	/*std::cout << "M 0 " << matches[0] << std::endl;*/
	/*std::cout << "M 1 " << matches[1] << std::endl;*/
	/*std::cout << "M 2 " << matches[2] << std::endl;*/
	/*std::cout << "M 3 " << matches[3] << std::endl;*/
	/*std::cout << "M 4 " << matches[4] << std::endl;*/
	/*std::cout << "M 5 " << matches[5] << std::endl;*/
	/*std::cout << "M 6 " << matches[6] << std::endl;*/
	/*std::cout << "M 7 " << matches[7] << std::endl;*/

	std::string text = stringUtils::trim(matches[5]);

	TaskData taskItem{
		.title = text,
		.state = str.substr(0, 5) == "- [X]" ? DONE : TODO,
	};
	return taskItem;
}

Time stringToTime(const std::string &str)
{
	if (str.length() != 5) {
		throw std::runtime_error("Str time length must be 5 (got '" + str + "')");
	}
	if (str[2] != 'h') {
		throw std::runtime_error("Malformated time range");
	}
	Time time{};
	std::from_chars(str.data(), str.data() + 2, time.hour);
	std::from_chars(str.data() + 3, str.data() + 5, time.minute);

	return time;
}

EventData stringToEvent(const std::string &str, const std::string &dateString)
{
	std::smatch matches;
	std::regex regex("> (([0-9]{2}h[0-9]{2})-([0-9]{2}h[0-9]{2}) )(.*?)( -- (.*))?");
	std::regex_match(str, matches, regex);

	/*std::cout << "line " << str << std::endl;*/
	/*std::cout << "M 0 " << matches[0] << std::endl;*/
	/*std::cout << "M 1 " << matches[1] << std::endl;*/
	/*std::cout << "M 2 " << matches[2] << std::endl;*/
	/*std::cout << "M 3 " << matches[3] << std::endl;*/
	/*std::cout << "M 4 " << matches[4] << std::endl;*/
	/*std::cout << "M 5 " << matches[5] << std::endl;*/
	/*std::cout << "M 6 " << matches[6] << std::endl;*/

	std::string text = stringUtils::trim(matches[4]);

	auto date = stringToDate(dateString);

	if (!date.has_value()) {
		throw std::runtime_error("Malformated date");
	}

	EventData eventData{
		.title = text, .startsAt = stringToTime(matches[2]), .endsAt = stringToTime(matches[3]),
		//.tags = extractTagsFromMetadata(matches[6])
	};
	return eventData;
}

std::string taskToString(const TaskData &task)
{
	std::string str = task.title;
	/*if (task.getTags().size() > 0) {*/
	/*str += " --";*/
	/*for (const std::string &tag : task.getTags()) {*/
	/*str += " #" + tag;*/
	/*}*/
	/*}*/
	str = (task.state == DONE ? "- [X] " : "- [ ] ") + str;
	return str;
}

std::string MarkdownDataProvider::toMarkdown()
{
	const std::vector<DayData> days = getDays();
	std::string result = "# " + name() + "\n\n";
	std::string currentDate = "";

	for (const auto &day : days) {
		auto &date = day.date;
		auto tasks = getTasksByDayId(day.id);
		auto events = getEventsByDate(date);
		if (tasks.size() == 0 && events.size() == 0) {
			continue;
		}
		result +=
			"## " + std::format("{:02d}-{:02d}-{:02d}", date.year, date.month, date.day) + "\n\n";
		for (auto event : events) {
			auto &start = event.startsAt;
			auto &end = event.endsAt;
			result += "> " +
					  std::format(
						  "{:02d}h{:02d}-{:02d}h{:02d} {}", start.hour, start.minute, end.hour,
						  end.minute, event.title
					  ) +
					  "\n";
			for (const auto &task : getTasksByEventId(event.id)) {
				result += taskToString(task) + '\n';
			}
			result += '\n';
		}
		for (const auto &task : tasks) {
			result += taskToString(task) + '\n';
		}
		result += '\n';
	}

	auto unscheduledTasks = getTasksWithoutDate();
	if (unscheduledTasks.size() > 0) {
		result += "## Unscheduled\n\n";
		for (const auto &task : unscheduledTasks) {
			result += taskToString(task) + '\n';
		}
		result += '\n';
	}
	return result;
};

MarkdownData MarkdownDataProvider::parseMarkdown(const std::string &content)
{
	cpputils::debug::Timer readMdFormatDuration;

	// std::vector<TaskItem> taskItems;
	std::string line;

	std::stringstream contentStream(content);

	if (!std::getline(contentStream, line) || line.substr(0, 2) != "# ") {
		std::cerr << "Couldn't find the task list name" << std::endl;
	}

	data.name = line.substr(2);
	std::string currentDateString = "";

	std::vector<mirai::DayData> daysData;
	std::vector<mirai::TaskData> unscheduledTasks;
	std::optional<Date> currentDate = std::nullopt;
	std::optional<EventData> currentEvent = std::nullopt;

	cpputils::debug::Timer stringToTaskDuration;
	stringToTaskDuration.reset();
	cpputils::debug::Timer gelinesDuration;
	while (std::getline(contentStream, line)) {
		if (std::string_view{line.data(), 3} == "## ") {
			currentDateString = line.substr(3);
			if (currentDateString == "Unscheduled") {
				currentDate = std::nullopt;
				continue;
			}
			currentDate = mirai::stringToDate(currentDateString);
			if (!currentDate.has_value()) {
				throw std::runtime_error("Malformated date (1)");
			}
			insertDay({.id = generateUniqueId(), .date = currentDate.value()});
		} else if (line.starts_with("> ")) {
			auto event = stringToEvent(line, currentDateString);
			if (currentDate.has_value()) {
				auto day = getDayByDate(currentDate.value());
				currentEvent = insertEvent(
					{.id = generateUniqueId(),
					 .dayId = day->id,
					 .title = event.title,
					 .startsAt = event.startsAt,
					 .endsAt = event.endsAt}
				);
			}
		} else if (line.starts_with("- [ ]") || line.starts_with("- [X]")) {
			stringToTaskDuration.start();
			TaskData taskItemData = stringToTask(line, currentDateString);
			stringToTaskDuration.stop();
			if (currentEvent.has_value()) {
				insertTask({
					.id = generateUniqueId(),
					.title = taskItemData.title,
					.state = taskItemData.state,
					.dayId = std::nullopt,
					.eventId = currentEvent.value().id,
				});
			} else if (currentDate.has_value()) {
				auto day = getDayByDate(currentDate.value());
				insertTask({
					.id = generateUniqueId(),
					.title = taskItemData.title,
					.state = taskItemData.state,
					.dayId = day->id,
					.eventId = std::nullopt,
				});
			} else {
				insertTask({
					.id = generateUniqueId(),
					.title = taskItemData.title,
					.state = taskItemData.state,
					.dayId = std::nullopt,
					.eventId = std::nullopt,
				});
			}
		} else if (cpputils::string::trim(line) == "") {
			currentEvent = std::nullopt;
		}
	}

	gelinesDuration.printTimeElapsed("getlinesDuration");
	stringToTaskDuration.printTimeElapsed("stringToTaskDuration");
	readMdFormatDuration.printTimeElapsed("Reading MD File duration");
	return data;
}
} // namespace mirai
