package main

import (
	"bytes"
	"encoding/json"
	"fmt"
	"io"
	"log"
	"net/http"
	"reflect"
	"slices"
	"strconv"
)

var SimklWatchList SimklWatchListType

func SimklHelper(method string, url string, body interface{}) json.RawMessage {
	reader, _ := json.Marshal(body)
	var req *http.Request

	client := &http.Client{}

	if body != nil {
		req, _ = http.NewRequest(method, url, bytes.NewBuffer(reader))
	} else {
		req, _ = http.NewRequest(method, url, nil)
	}

	req.Header.Add("Content-Type", "application/json")
	req.Header.Add("Authorization", "Bearer "+simklJwt.AccessToken)
	req.Header.Add("simkl-api-key", Environment.SIMKL_CLIENT_ID)

	resp, err := client.Do(req)
	if err != nil {
		fmt.Println("Errored when sending request to the server")
		message, _ := json.Marshal(struct {
			Message string `json:"message"`
		}{
			Message: "Errored when sending request to the server" + err.Error(),
		})

		return message
	}

	defer resp.Body.Close()
	respBody, _ := io.ReadAll(resp.Body)

	return respBody
}

func (a *App) SimklGetUserWatchlist() SimklWatchListType {
	method := "GET"
	url := "https://api.simkl.com/sync/all-items/anime"

	respBody := SimklHelper(method, url, nil)

	var errCheck struct {
		Error   string `json:"error"`
		Message string `json:"message"`
	}

	err := json.Unmarshal(respBody, &errCheck)
	if err != nil {
		log.Printf("Failed at unmarshal, %s\n", err)
	}

	if errCheck.Error != "" {
		a.LogoutSimkl()
		return SimklWatchListType{}
	}

	var watchlist SimklWatchListType

	err = json.Unmarshal(respBody, &watchlist)
	if err != nil {
		log.Printf("Failed at unmarshal, %s\n", err)
	}

	SimklWatchList = watchlist

	return watchlist
}

func (a *App) SimklSyncEpisodes(anime SimklAnime, progress int) SimklAnime {
	var episodes []Episode
	var url string
	var shows []SimklPostShow

	if progress >= anime.WatchedEpisodesCount {
		for i := 1; i <= progress; i++ {
			episodes = append(episodes, Episode{Number: i})
		}
		url = "https://api.simkl.com/sync/history"
	} else {
		for i := anime.WatchedEpisodesCount; i > progress; i-- {
			episodes = append(episodes, Episode{Number: i})
		}
		url = "https://api.simkl.com/sync/history/remove"
	}

	formattedShow := SimklPostShow{
		Title: anime.Show.Title,
		Ids: Ids{
			Simkl:   anime.Show.Ids.Simkl,
			Mal:     anime.Show.Ids.Mal,
			Anilist: anime.Show.Ids.AniList,
		},
		Episodes: episodes,
	}

	shows = append(shows, formattedShow)

	simklSync := SimklSyncHistoryType{shows}

	respBody := SimklHelper("POST", url, simklSync)

	var success interface{}

	err := json.Unmarshal(respBody, &success)
	if err != nil {
		log.Printf("Failed at unmarshal, %s\n", err)
	}

	for i, simklAnime := range SimklWatchList.Anime {
		if anime.Show.Ids.Simkl == simklAnime.Show.Ids.Simkl {
			SimklWatchList.Anime[i].WatchedEpisodesCount = progress
		}
	}

	anime.WatchedEpisodesCount = progress

	WatchListUpdate(anime)

	return anime
}

func (a *App) SimklSyncRating(anime SimklAnime, rating int) SimklAnime {
	var url string
	showWithRating := ShowWithRating{
		Title: anime.Show.Title,
		Ids: Ids{
			Simkl:   anime.Show.Ids.Simkl,
			Mal:     anime.Show.Ids.Mal,
			Anilist: anime.Show.Ids.AniList,
		},
		Rating: rating,
	}

	showWithoutRating := ShowWithoutRating{
		Title: anime.Show.Title,
		Ids: Ids{
			Simkl:   anime.Show.Ids.Simkl,
			Mal:     anime.Show.Ids.Mal,
			Anilist: anime.Show.Ids.AniList,
		},
	}

	var shows []interface{}

	if rating > 0 {
		shows = append(shows, showWithRating)
		url = "https://api.simkl.com/sync/ratings"
	} else {
		shows = append(shows, showWithoutRating)
		url = "https://api.simkl.com/sync/ratings/remove"
	}

	simklSync := struct {
		Shows []interface{} `json:"shows" ts_type:"shows"`
	}{shows}

	respBody := SimklHelper("POST", url, simklSync)

	var success interface{}

	err := json.Unmarshal(respBody, &success)
	if err != nil {
		log.Printf("Failed at unmarshal, %s\n", err)
	}

	for i, simklAnime := range SimklWatchList.Anime {
		if anime.Show.Ids.Simkl == simklAnime.Show.Ids.Simkl {
			SimklWatchList.Anime[i].UserRating = rating
		}
	}

	anime.UserRating = rating

	WatchListUpdate(anime)

	return anime
}

func (a *App) SimklSyncStatus(anime SimklAnime, status string) SimklAnime {
	url := "https://api.simkl.com/sync/add-to-list"
	show := SimklShowStatus{
		Title: anime.Show.Title,
		Ids: Ids{
			Simkl:   anime.Show.Ids.Simkl,
			Mal:     anime.Show.Ids.Mal,
			Anilist: anime.Show.Ids.AniList,
		},
		To: status,
	}

	var shows []SimklShowStatus

	shows = append(shows, show)

	simklSync := struct {
		Shows []SimklShowStatus `json:"shows" ts_type:"shows"`
	}{shows}

	respBody := SimklHelper("POST", url, simklSync)

	var success interface{}

	err := json.Unmarshal(respBody, &success)
	if err != nil {
		log.Printf("Failed at unmarshal, %s\n", err)
	}

	for i, simklAnime := range SimklWatchList.Anime {
		if anime.Show.Ids.Simkl == simklAnime.Show.Ids.Simkl {
			SimklWatchList.Anime[i].Status = status
		}
	}

	anime.Status = status

	WatchListUpdate(anime)

	return anime
}

func (a *App) SimklSearch(aniListAnime MediaList) SimklAnime {
	var result SimklAnime

	if reflect.DeepEqual(SimklWatchList, SimklWatchListType{}) {
		fmt.Println("Watchlist empty. Calling...")
		SimklWatchList = a.SimklGetUserWatchlist()
	}

	for _, anime := range SimklWatchList.Anime {
		id, err := strconv.Atoi(anime.Show.Ids.AniList)
		if err != nil {
			fmt.Println("AniList ID does not exist on " + anime.Show.Title)
		}
		if id == aniListAnime.Media.ID {
			result = anime
		}
	}

	if reflect.DeepEqual(result, SimklAnime{}) {
		var anime SimklSearchType
		url := "https://api.simkl.com/search/id?anilist=" + strconv.Itoa(aniListAnime.Media.ID)

		respBody := SimklHelper("GET", url, nil)

		err := json.Unmarshal(respBody, &anime)

		if len(anime) == 0 {
			url = "https://api.simkl.com/search/id?mal=" + strconv.Itoa(aniListAnime.Media.IDMal)
			respBody = SimklHelper("GET", url, nil)
			fmt.Println(string(respBody))
			err = json.Unmarshal(respBody, &anime)
		}

		if err != nil {
			log.Printf("Failed at unmarshal, %s\n", err)
		}

		if len(anime) == 0 {
			return result
		}

		for _, watchListAnime := range SimklWatchList.Anime {
			id := watchListAnime.Show.Ids.Simkl
			if id == anime[0].Ids.Simkl {
				result = watchListAnime
			}
		}

		if reflect.DeepEqual(result, SimklAnime{}) && len(anime) > 0 {
			result.Show.Title = anime[0].Title
			result.Show.Poster = anime[0].Poster
			result.Show.Ids.Simkl = anime[0].Ids.Simkl
			result.Show.Ids.Slug = anime[0].Ids.Slug
		}
	}

	return result
}

func (a *App) SimklSyncRemove(anime SimklAnime) bool {
	url := "https://api.simkl.com/sync/history/remove"
	var showArray []SimklShowStatus

	singleShow := SimklShowStatus{
		Title: anime.Show.Title,
		Ids: Ids{
			Simkl:   anime.Show.Ids.Simkl,
			Mal:     anime.Show.Ids.Mal,
			Anilist: anime.Show.Ids.AniList,
		},
	}

	showArray = append(showArray, singleShow)

	show := struct {
		Shows []SimklShowStatus `json:"shows"`
	}{
		Shows: showArray,
	}

	respBody := SimklHelper("POST", url, show)

	var success SimklDeleteType

	err := json.Unmarshal(respBody, &success)
	if err != nil {
		log.Printf("Failed at unmarshal, %s\n", err)
	}

	if success.Deleted.Shows >= 1 {
		for i, simklAnime := range SimklWatchList.Anime {
			if simklAnime.Show.Ids.Simkl == anime.Show.Ids.Simkl {
				SimklWatchList.Anime = slices.Delete(SimklWatchList.Anime, i, i+1)
			}
		}
		return true
	} else {
		return false
	}
}

func WatchListUpdate(anime SimklAnime) {
	updated := false
	for i, simklAnime := range SimklWatchList.Anime {
		if simklAnime.Show.Ids.Simkl == anime.Show.Ids.Simkl {
			SimklWatchList.Anime[i] = anime
			updated = true
		}
	}
	if !updated {
		SimklWatchList.Anime = append(SimklWatchList.Anime, anime)
	}
}