package manga

import (
	"context"
	"errors"
	"seanime/internal/api/anilist"
	"seanime/internal/hook"
	"seanime/internal/platforms/anilist_platform"
	"seanime/internal/platforms/platform"
	"seanime/internal/util/filecache"

	"github.com/rs/zerolog"
)

type (
	// Entry is fetched when the user goes to the manga entry page.
	Entry struct {
		MediaId       int                `json:"mediaId"`
		Media         *anilist.BaseManga `json:"media"`
		EntryListData *EntryListData     `json:"listData,omitempty"`
	}

	EntryListData struct {
		Progress    int                      `json:"progress,omitempty"`
		Score       float64                  `json:"score,omitempty"`
		Status      *anilist.MediaListStatus `json:"status,omitempty"`
		Repeat      int                      `json:"repeat,omitempty"`
		StartedAt   string                   `json:"startedAt,omitempty"`
		CompletedAt string                   `json:"completedAt,omitempty"`
	}
)

type (
	// NewEntryOptions is the options for creating a new manga entry.
	NewEntryOptions struct {
		MediaId         int
		Logger          *zerolog.Logger
		FileCacher      *filecache.Cacher
		MangaCollection *anilist.MangaCollection
		Platform        platform.Platform
	}
)

// NewEntry creates a new manga entry.
func NewEntry(ctx context.Context, opts *NewEntryOptions) (entry *Entry, err error) {
	entry = &Entry{
		MediaId: opts.MediaId,
	}

	reqEvent := new(MangaEntryRequestedEvent)
	reqEvent.MediaId = opts.MediaId
	reqEvent.MangaCollection = opts.MangaCollection
	reqEvent.Entry = entry

	err = hook.GlobalHookManager.OnMangaEntryRequested().Trigger(reqEvent)
	if err != nil {
		return nil, err
	}
	opts.MediaId = reqEvent.MediaId                 // Override the media ID
	opts.MangaCollection = reqEvent.MangaCollection // Override the manga collection
	entry = reqEvent.Entry                          // Override the entry

	if reqEvent.DefaultPrevented {
		mangaEvent := new(MangaEntryEvent)
		mangaEvent.Entry = reqEvent.Entry
		err = hook.GlobalHookManager.OnMangaEntry().Trigger(mangaEvent)
		if err != nil {
			return nil, err
		}

		if mangaEvent.Entry == nil {
			return nil, errors.New("no entry was returned")
		}
		return mangaEvent.Entry, nil
	}

	anilistEntry, found := opts.MangaCollection.GetListEntryFromMangaId(opts.MediaId)

	// If the entry is not found, we fetch the manga from the Anilist API.
	if !found {
		media, err := opts.Platform.GetManga(ctx, opts.MediaId)
		if err != nil {
			return nil, err
		}
		entry.Media = media

	} else {
		// If the entry is found, we use the entry from the collection.
		mangaEvent := new(anilist_platform.GetMangaEvent)
		mangaEvent.Manga = anilistEntry.GetMedia()
		err := hook.GlobalHookManager.OnGetManga().Trigger(mangaEvent)
		if err != nil {
			return nil, err
		}
		entry.Media = mangaEvent.Manga
		entry.EntryListData = &EntryListData{
			Progress:    *anilistEntry.Progress,
			Score:       *anilistEntry.Score,
			Status:      anilistEntry.Status,
			Repeat:      anilistEntry.GetRepeatSafe(),
			StartedAt:   anilist.FuzzyDateToString(anilistEntry.StartedAt),
			CompletedAt: anilist.FuzzyDateToString(anilistEntry.CompletedAt),
		}
	}

	mangaEvent := new(MangaEntryEvent)
	mangaEvent.Entry = entry
	err = hook.GlobalHookManager.OnMangaEntry().Trigger(mangaEvent)
	if err != nil {
		return nil, err
	}

	return mangaEvent.Entry, nil
}
