/*
 * 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 "Source.h"
#include "DataProvider.h"
#include "utils.h"
#include <algorithm>
#include <iterator>
#include <optional>
#include <vector>

namespace mirai
{

void Source::save()
{
	data->save();
}

void Source::createTask(const createTaskParams &task)
{
	std::optional<int> dayId = std::nullopt;
	std::optional<int> eventId = std::nullopt;

	if (!task.event.has_value() && task.date.has_value()) {
		auto day = data->getDayByDate(task.date.value());
		if (!day.has_value() && task.date.has_value()) {
			day = data->insertDay({.id = generateUniqueId(), .date = task.date.value()});
		}
		dayId = day->id;
	}

	if (task.event.has_value() && task.event->id()) {
		eventId = task.event.value().id();
	}

	data->insertTask({
		.id = generateUniqueId(),
		.title = task.title,
		.state = TODO,
		.dayId = dayId,
		.eventId = eventId,
	});
};

std::vector<Day> Source::getDays()
{
	auto daysData = data->getDays();
	std::vector<Day> days;
	std::transform(
		daysData.begin(), daysData.end(), std::back_inserter(days),
		[&](const DayData &dayData) {
			return Day{data, dayData};
		}
	);
	return days;
}

std::optional<Day> Source::getDayByDate(Date date)
{
	auto day = data->getDayByDate(date);
	if (!day.has_value()) {
		return std::nullopt;
	}
	return Day{data, day.value()};
}

void Source::createEvent(const createEventParams &eventToCreate)
{
	std::optional<int> dayId = std::nullopt;

	auto day = data->getDayByDate(eventToCreate.date);
	if (!day.has_value()) {
		day = data->insertDay({.id = generateUniqueId(), .date = eventToCreate.date});
		dayId = day->id;
	}

	data->insertEvent({
		.id = generateUniqueId(),
		.dayId = day.value().id,
		.title = eventToCreate.title,
	});
};

std::optional<Event> Source::getEventById(int evendId)
{
	auto event = data->getEventById(evendId);
	if (!event.has_value()) {
		return std::nullopt;
	}
	return Event{data, event.value()};
}

void Source::removeEvent(const Event &event)
{
	data->removeEventById(event.id());
}

void Source::removeTask(const Task &task)
{
	data->removeTaskById(task.id());
}

std::optional<Task> Source::getTaskById(int taskId)
{
	auto taskData = data->getTaskById(taskId);
	if (!taskData.has_value()) {
		return std::nullopt;
	}
	Task task{data, taskData.value()};
	return task;
}

std::vector<Task> Source::getTasks()
{
	auto tasksData = data->getTasks();
	std::vector<Task> tasks;
	std::transform(
		tasksData.begin(), tasksData.end(), std::back_inserter(tasks),
		[&](const TaskData &taskData) {
			return Task{data, taskData};
		}
	);
	return tasks;
}

std::vector<Task> Source::getUnscheduledTasks()
{
	auto tasksData = data->getTasksWithoutDate();
	std::vector<Task> tasks;
	std::transform(
		tasksData.begin(), tasksData.end(), std::back_inserter(tasks),
		[&](const TaskData &taskData) {
			return Task{data, taskData};
		}
	);
	return tasks;
}

std::string Source::name() const
{
	return data->name();
}
} // namespace mirai
