#ifndef MINORI_CORE_ANIME_H_
#define MINORI_CORE_ANIME_H_

#include "core/anime_season.h"
#include "core/date.h"

#include <array>
#include <map>
#include <vector>
#include <optional>

namespace Anime {

enum class ListStatus {
	NotInList = 0,
	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 = 0,
	Finished,
	Releasing,
	NotYetReleased,
	Cancelled,
	Hiatus
};

constexpr std::array<SeriesStatus, 6> SeriesStatuses{
	SeriesStatus::Unknown,
	SeriesStatus::Finished,
	SeriesStatus::Releasing,
	SeriesStatus::NotYetReleased,
	SeriesStatus::Cancelled,
	SeriesStatus::Hiatus
};

enum class SeriesFormat {
	Unknown = 0,
	Tv,
	TvShort,
	Movie,
	Special,
	Ova,
	Ona,
	Music
};

constexpr std::array<SeriesFormat, 8> SeriesFormats{
	SeriesFormat::Unknown,
	SeriesFormat::Tv,
	SeriesFormat::TvShort,
	SeriesFormat::Movie,
	SeriesFormat::Special,
	SeriesFormat::Ova,
	SeriesFormat::Ona,
	SeriesFormat::Music
};

enum class TitleLanguage {
	Romaji,
	Native,
	English
};

constexpr std::array<TitleLanguage, 3> TitleLanguages{TitleLanguage::Romaji, TitleLanguage::Native, TitleLanguage::English};

enum class Service {
	None,
	AniList,
	MyAnimeList,
	Kitsu
};

/* this doesn't include MAL and Kitsu because they aren't really
 * "valid" services yet. */
constexpr std::array<Service, 3> Services{
	Service::AniList,
	Service::MyAnimeList,
	Service::Kitsu
};

enum class ScoreFormat {
	Point100,       // 0-100
	Point10Decimal, // 0.0-10.0
	Point10,        // 0-10
	Point5,         // 0-5, should be represented in stars
	Point3          // 1-3, should be represented in smileys
};

constexpr std::array<ScoreFormat, 5> ScoreFormats{ScoreFormat::Point100, ScoreFormat::Point10Decimal,
                                                  ScoreFormat::Point10, ScoreFormat::Point5, ScoreFormat::Point3};

struct ListInformation {
	std::string id;
	int progress = 0;
	int score = 0; // this will ALWAYS be in POINT_100 format internally
	ListStatus status = ListStatus::NotInList;
	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;
	std::map<Service, std::string> ids;
	std::map<TitleLanguage, std::string> titles;
	std::vector<std::string> synonyms;
	int episodes = 0;
	SeriesStatus status = SeriesStatus::Unknown;
	Date started;
	Date completed;
	std::vector<std::string> genres;
	std::vector<std::string> producers;
	SeriesFormat format = SeriesFormat::Unknown;
	double audience_score = 0;
	std::string synopsis;
	int duration = 0;
	std::string poster_url;
};

class Anime {
public:
	/* User list data */
	std::string GetUserId() const;
	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 SetUserId(const std::string& id);
	void SetUserStatus(ListStatus status);
	void SetUserScore(int score);
	void SetUserProgress(int progress);
	void SetUserDateStarted(const Date& started);
	void SetUserDateCompleted(const Date& completed);
	void SetUserIsPrivate(bool is_private);
	void SetUserRewatchedTimes(int rewatched);
	void SetUserIsRewatching(bool rewatching);
	void SetUserTimeUpdated(uint64_t updated);
	void SetUserNotes(const std::string& notes);

	/* Series data */
	int GetId() const;
	std::optional<std::string> GetServiceId(Service service) const;
	std::optional<std::string> GetTitle(TitleLanguage language) const;
	std::vector<std::string> GetTitleSynonyms() const;
	int GetEpisodes() const;
	SeriesStatus GetAiringStatus() const;
	Date GetStartedDate() const;
	Date GetCompletedDate() const;
	std::vector<std::string> GetGenres() const;
	std::vector<std::string> GetProducers() const;
	SeriesFormat GetFormat() const;
	Season GetSeason() const;
	double GetAudienceScore() const;
	std::string GetSynopsis() const;
	int GetDuration() const;
	std::string GetPosterUrl() const;
	std::optional<std::string> GetServiceUrl(Service service) const;

	void SetId(int id);
	void SetServiceId(Service service, const std::string& id);
	void SetTitle(TitleLanguage language, const std::string& title);
	void SetTitleSynonyms(const std::vector<std::string>& synonyms);
	void AddTitleSynonym(const std::string& synonym);
	void SetEpisodes(int episodes);
	void SetAiringStatus(SeriesStatus status);
	void SetStartedDate(const Date& date);
	void SetCompletedDate(const Date& date);
	void SetGenres(const std::vector<std::string>& genres);
	void SetProducers(const std::vector<std::string>& producers);
	void SetFormat(SeriesFormat format);
	void SetAudienceScore(double 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_
