#ifndef MINORI_CORE_ANIME_H_
#define MINORI_CORE_ANIME_H_

#include "core/date.h"
#include <array>
#include <map>
#include <vector>
#include <optional>

namespace Anime {

enum class ListStatus {
	NOT_IN_LIST,
	CURRENT,
	COMPLETED,
	PAUSED,
	DROPPED,
	PLANNING
};

constexpr std::array<ListStatus, 5> ListStatuses{ListStatus::CURRENT, ListStatus::COMPLETED, ListStatus::PAUSED,
                                                 ListStatus::DROPPED, ListStatus::PLANNING};

enum class SeriesStatus {
	UNKNOWN,
	FINISHED,
	RELEASING,
	NOT_YET_RELEASED,
	CANCELLED,
	HIATUS
};

enum class SeriesFormat {
	UNKNOWN,
	TV,
	TV_SHORT,
	MOVIE,
	SPECIAL,
	OVA,
	ONA,
	MUSIC,
	MANGA,
	NOVEL,
	ONE_SHOT
};

enum class SeriesSeason {
	UNKNOWN,
	WINTER,
	SPRING,
	SUMMER,
	FALL
};

constexpr std::array<SeriesSeason, 4> SeriesSeasons{
	SeriesSeason::WINTER, SeriesSeason::SPRING,
	SeriesSeason::SUMMER, SeriesSeason::FALL
};

enum class TitleLanguage {
	ROMAJI,
	NATIVE,
	ENGLISH
};

enum class Services {
	NONE,
	ANILIST,
	NB_SERVICES
};

enum class ScoreFormat {
	POINT_100,        // 0-100
	POINT_10_DECIMAL, // 0.0-10.0
	POINT_10,         // 0-10
	POINT_5,          // 0-5, should be represented in stars
	POINT_3           // 1-3, should be represented in smileys
};

constexpr std::array<ScoreFormat, 5> ScoreFormats{ScoreFormat::POINT_100, ScoreFormat::POINT_10_DECIMAL,
                                                  ScoreFormat::POINT_10, ScoreFormat::POINT_5, ScoreFormat::POINT_3};

struct ListInformation {
	int id = 0;
	int progress = 0;
	int score = 0; // note that this will ALWAYS be in POINT_100 format and must be converted
	ListStatus status = ListStatus::NOT_IN_LIST;
	Date started;
	Date completed;
	bool is_private = false;
	unsigned int rewatched_times = 0;
	bool rewatching = false;
	uint64_t updated = 0;
	std::string notes;
};

struct SeriesInformation {
	int id;
	struct {
		std::string romaji;
		std::string english;
		std::string native;
	} title;
	std::vector<std::string> synonyms;
	int episodes = 0;
	SeriesStatus status = SeriesStatus::UNKNOWN;
	Date air_date;
	std::vector<std::string> genres;
	std::vector<std::string> producers;
	SeriesFormat format = SeriesFormat::UNKNOWN;
	SeriesSeason season = SeriesSeason::UNKNOWN;
	int audience_score = 0;
	std::string synopsis;
	int duration = 0;
	std::string poster_url;
};

class Anime {
public:
	/* User list data */
	ListStatus GetUserStatus() const;
	int GetUserProgress() const;
	int GetUserScore() const;
	std::string GetUserPresentableScore() const;
	Date GetUserDateStarted() const;
	Date GetUserDateCompleted() const;
	bool GetUserIsPrivate() const;
	unsigned int GetUserRewatchedTimes() const;
	bool GetUserIsRewatching() const;
	uint64_t GetUserTimeUpdated() const;
	std::string GetUserNotes() const;

	void SetUserStatus(ListStatus status);
	void SetUserScore(int score);
	void SetUserProgress(int progress);
	void SetUserDateStarted(Date const& started);
	void SetUserDateCompleted(Date const& completed);
	void SetUserIsPrivate(bool is_private);
	void SetUserRewatchedTimes(int rewatched);
	void SetUserIsRewatching(bool rewatching);
	void SetUserTimeUpdated(uint64_t updated);
	void SetUserNotes(std::string const& notes);

	/* Series data */
	int GetId() const;
	std::string GetRomajiTitle() const;
	std::string GetEnglishTitle() const;
	std::string GetNativeTitle() const;
	std::vector<std::string> GetTitleSynonyms() const;
	int GetEpisodes() const;
	SeriesStatus GetAiringStatus() const;
	Date GetAirDate() const;
	std::vector<std::string> GetGenres() const;
	std::vector<std::string> GetProducers() const;
	SeriesFormat GetFormat() const;
	SeriesSeason GetSeason() const;
	int GetAudienceScore() const;
	std::string GetSynopsis() const;
	int GetDuration() const;
	std::string GetPosterUrl() const;
	std::string GetServiceUrl() const;

	void SetId(int id);
	void SetRomajiTitle(std::string const& title);
	void SetEnglishTitle(std::string const& title);
	void SetNativeTitle(std::string const& title);
	void SetTitleSynonyms(std::vector<std::string> const& synonyms);
	void AddTitleSynonym(std::string const& synonym);
	void SetEpisodes(int episodes);
	void SetAiringStatus(SeriesStatus status);
	void SetAirDate(Date const& date);
	void SetGenres(std::vector<std::string> const& genres);
	void SetProducers(std::vector<std::string> const& producers);
	void SetFormat(SeriesFormat format);
	void SetSeason(SeriesSeason season);
	void SetAudienceScore(int audience_score);
	void SetSynopsis(std::string synopsis);
	void SetDuration(int duration);
	void SetPosterUrl(std::string poster);

	std::string GetUserPreferredTitle() const;

	/* User stuff */
	void AddToUserList();
	bool IsInUserList() const;
	void RemoveFromUserList();

private:
	SeriesInformation info_;
	std::optional<struct ListInformation> list_info_ = std::nullopt;
};

} // namespace Anime

#endif // MINORI_CORE_ANIME_H_
