package tmdb

import (
	"notabug.org/apiote/amuse/utils"

	"notabug.org/apiote/gott"

	"encoding/json"
	"net/http"
	"sort"
	"time"
)

type Collection struct {
	Id    int
	Name  string
	Parts []struct {
		Id               int
		Release_date_str string `json:"release_date"`
		Release_date     time.Time
		Poster_path      string `json:"poster_path"`
		Title            string
	}
}

type Film struct {
	Etag          string
	Backdrop_path string     `json:"backdrop_path"`
	Collection    Collection `json:"belongs_to_collection"`
	Genres        []struct {
		Name string
	}
	Original_title   string `json:"original_title"`
	Overview         string
	Poster_path      string `json:"poster_path"`
	Release_date_str string `json:"release_date"`
	Release_date     time.Time
	Runtime          int
	Status           string
	Tagline          string
	Title            string
	Vote_count       int     `json:"vote_count"`
	Vote_average     float32 `json:"vote_average"`
	Credits          struct {
		Cast []struct {
			Character    string
			Id           int
			Name         string
			Profile_path string `json:"profile_path"`
		}
		Crew []struct {
			Id           int
			Job          string
			Name         string
			Profile_path string `json:"profile_path"`
		}
	}
}

func createFilmRequest(args ...interface{}) (interface{}, error) {
	tmdbRequest := args[0].(TmdbRequest)
	client := &http.Client{}
	request, err := http.NewRequest("GET", "https://api.themoviedb.org/3/movie/"+tmdbRequest.id+"?api_key="+API_KEY+"&language="+tmdbRequest.language+"&append_to_response=credits", nil)
	args[1] = client
	args[2] = request
	return gott.Tuple(args), err
}

func unmarshalFilm(args ...interface{}) (interface{}, error) {
	body := []byte(args[3].(string))
	film := &Film{}
	err := json.Unmarshal(body, film)
	film.Etag = args[4].(string)
	args[3] = film
	return gott.Tuple(args), err
}

func convertFilmDate(args ...interface{}) (interface{}, error) {
	film := args[3].(*Film)
	if film.Release_date_str != "" {
		date, err := time.Parse("2006-01-02", film.Release_date_str)
		film.Release_date = date
		args[3] = film
		return gott.Tuple(args), err
	}
	return gott.Tuple(args), nil
}

func createCollectionRequest(args ...interface{}) (interface{}, error) {
	tmdbRequest := args[0].(TmdbRequest)
	client := &http.Client{}
	request, err := http.NewRequest("GET", "https://api.themoviedb.org/3/collection/"+tmdbRequest.id+"?api_key="+API_KEY+"&language="+tmdbRequest.language, nil)
	args[1] = client
	args[2] = request
	return gott.Tuple(args), err
}

func unmarshalCollection(args ...interface{}) (interface{}, error) {
	body := []byte(args[3].(string))
	collection := &Collection{}
	err := json.Unmarshal(body, collection)
	args[3] = collection
	return gott.Tuple(args), err
}

func convertCollectionDates(args ...interface{}) (interface{}, error) {
	collection := args[3].(*Collection)
	var err error = nil
	for i, part := range collection.Parts {
		if part.Release_date_str != "" {
			date, err2 := time.Parse("2006-01-02", part.Release_date_str)
			part.Release_date = date
			err = utils.Or(err2, err)
			collection.Parts[i] = part
		}
	}
	args[3] = collection
	return gott.Tuple(args), err
}

func sortCollection(args ...interface{}) interface{} {
	collection := args[3].(*Collection)
	sort.Slice(collection.Parts, func(i, j int) bool {
		isBefore := collection.Parts[i].Release_date.Before(collection.Parts[j].Release_date)
		return (isBefore && collection.Parts[i].Release_date.Year() != 1) ||
			(!isBefore && collection.Parts[j].Release_date.Year() == 1)
	})
	args[3] = collection
	return gott.Tuple(args)
}

func GetFilm(id string, language string, etag string) (*Film, error) {
	film, err := gott.
		NewResult(gott.Tuple{TmdbRequest{id, etag, language, ""}, nil, nil, nil, nil}).
		Bind(createFilmRequest).
		Bind(doRequest).
		Bind(handleRequestError).
		Bind(readResponse).
		Bind(unmarshalFilm).
		Bind(convertFilmDate).
		Finish()

	if err != nil {
		return &Film{}, err
	} else {
		return film.(gott.Tuple)[3].(*Film), nil
	}
}

func GetCollection(id string, language string, etag string) (*Collection, error) {
	collection, err := gott.
		NewResult(gott.Tuple{TmdbRequest{id, etag, language, ""}, nil, nil, nil, nil}).
		Bind(createCollectionRequest).
		Bind(doRequest).
		Bind(handleRequestError).
		Bind(readResponse).
		Bind(unmarshalCollection).
		Bind(convertCollectionDates).
		Map(sortCollection).
		Finish()

	if err != nil {
		return &Collection{}, err
	} else {
		return collection.(gott.Tuple)[3].(*Collection), nil
	}
}
