package arn

import (
	"errors"
	"fmt"
	"reflect"
	"time"

	"github.com/aerogo/aero"
	"github.com/aerogo/aero/event"
	"github.com/aerogo/api"
)

// Force interface implementations
var (
	_ api.CustomEditable = (*AnimeListItem)(nil)
	_ api.AfterEditable  = (*AnimeListItem)(nil)
)

// Edit ...
func (item *AnimeListItem) Edit(ctx aero.Context, key string, value reflect.Value, newValue reflect.Value) (bool, error) {
	user := GetUserFromContext(ctx)

	if user == nil {
		return true, errors.New("Not logged in")
	}

	switch key {
	case "Episodes":
		if !newValue.IsValid() {
			return true, errors.New("Invalid episode number")
		}

		oldEpisodes := item.Episodes
		newEpisodes := int(newValue.Float())

		// Fetch last activity
		lastActivity := user.LastActivityConsumeAnime(item.AnimeID)

		if lastActivity == nil || time.Since(lastActivity.GetCreatedTime()) > 1*time.Hour {
			// If there is no last activity for the given anime,
			// or if the last activity happened more than an hour ago,
			// create a new activity.
			if newEpisodes > oldEpisodes {
				activity := NewActivityConsumeAnime(item.AnimeID, newEpisodes, newEpisodes, user.ID)
				activity.Save()

				// Broadcast event to all users so they can reload the activity page if needed.
				for receiver := range StreamUsers() {
					activityEvent := event.New("watch activity", receiver.IsFollowing(user.ID))
					receiver.BroadcastEvent(activityEvent)
				}
			}
		} else if newEpisodes >= lastActivity.FromEpisode {
			// Otherwise, update the last activity.
			lastActivity.ToEpisode = newEpisodes
			lastActivity.Created = DateTimeUTC()
			lastActivity.Save()
		}

		item.Episodes = newEpisodes

		if item.Episodes < 0 {
			item.Episodes = 0
		}

		item.OnEpisodesChange()
		return true, nil

	case "Status":
		newStatus := newValue.String()

		switch newStatus {
		case AnimeListStatusWatching, AnimeListStatusCompleted, AnimeListStatusPlanned, AnimeListStatusHold, AnimeListStatusDropped:
			item.Status = newStatus
			item.OnStatusChange()
			return true, nil

		default:
			return true, fmt.Errorf("Invalid anime list item status: %s", newStatus)
		}
	}

	return false, nil
}

// AfterEdit is called after the item is edited.
func (item *AnimeListItem) AfterEdit(ctx aero.Context) error {
	item.Rating.Clamp()
	item.Edited = DateTimeUTC()
	return nil
}
