package tmdb

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

	"notabug.org/apiote/gott"

	"encoding/json"
	"fmt"
	"net/http"
	"strconv"
	"time"
)

type Episode struct {
	Air_date_str   string `json:"air_date"`
	Air_date       time.Time
	Episode_number int `json:"episode_number"`
	Name           string
	Overview       string
	Season_number  int     `json:"season_number"`
	Still_path     string  `json:"still_path"`
	Vote_count     int     `json:"vote_count"`
	Vote_average   float32 `json:"vote_average"`
	Episode_code   string
}

type Season struct {
	Season_number int `json:"season_number"`
	Episodes      []Episode
}

type Serie struct {
	Id                 string `json:"-"`
	Etag               string
	Backdrop_path      string `json:"backdrop_path"`
	Episode_run_time   []int  `json:"episode_run_time"`
	First_air_date_str string `json:"first_air_date"`
	First_air_date     time.Time
	Genres             []struct {
		Name string
	}
	Last_air_date_str   string `json:"last_air_date"`
	Last_air_date       time.Time
	Last_episode_to_air Episode `json:"last_episode_to_air"`
	Name                string
	Number_of_episodes  int    `json:"number_of_episodes"`
	Original_name       string `json:"original_name"`
	Overview            string
	Poster_path         string `json:"poster_path"`
	Seasons             []Season
	Status              string
	Type                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 createSerieRequest(args ...interface{}) (interface{}, error) {
	tmdbRequest := args[0].(TmdbRequest)
	client := &http.Client{}
	request, err := http.NewRequest("GET", "https://api.themoviedb.org/3/tv/"+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 unmarshalSerie(args ...interface{}) (interface{}, error) {
	tmdbRequest := args[0].(TmdbRequest)
	body := []byte(args[3].(string))
	serie := &Serie{}
	err := json.Unmarshal(body, serie)
	serie.Etag = args[4].(string)
	serie.Id = tmdbRequest.id
	args[3] = serie
	return gott.Tuple(args), err
}

func convertSerieDates(args ...interface{}) (interface{}, error) {
	serie := args[3].(*Serie)
	var err error
	if serie.First_air_date_str != "" {
		date, err2 := time.Parse("2006-01-02", serie.First_air_date_str)
		serie.First_air_date = date
		err = utils.Or(err, err2)
	}
	if serie.Last_air_date_str != "" {
		date, err2 := time.Parse("2006-01-02", serie.Last_air_date_str)
		serie.Last_air_date = date
		err = utils.Or(err, err2)
	}
	if serie.Last_episode_to_air.Air_date_str != "" {
		date, err2 := time.Parse("2006-01-02", serie.Last_episode_to_air.Air_date_str)
		serie.Last_episode_to_air.Air_date = date
		err = utils.Or(err, err2)
	}
	args[3] = serie
	return gott.Tuple(args), err
}

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

func unmarshalSeason(args ...interface{}) (interface{}, error) {
	body := []byte(args[3].(string))
	season := &Season{}
	err := json.Unmarshal(body, season)
	for i, episode := range season.Episodes {
		episode.Episode_code = fmt.Sprintf("S%02dE%02d", season.Season_number, episode.Episode_number)
		season.Episodes[i] = episode
	}
	args[3] = season
	return gott.Tuple(args), err
}

func convertSeasonDates(args ...interface{}) (interface{}, error) {
	season := args[3].(*Season)
	var err error
	for i, episode := range season.Episodes {
		if episode.Air_date_str != "" {
			date, err2 := time.Parse("2006-01-02", episode.Air_date_str)
			episode.Air_date = date
			err = utils.Or(err, err2)
			season.Episodes[i] = episode
		}
	}
	args[3] = season
	return gott.Tuple(args), err
}

func GetSerie(id, language, etag string) (*Serie, error) {
	serie, err := gott.
		NewResult(gott.Tuple{TmdbRequest{id, etag, language, ""}, nil, nil, nil, nil}).
		Bind(createSerieRequest).
		Bind(doRequest).
		Bind(handleRequestError).
		Bind(readResponse).
		Bind(unmarshalSerie).
		Bind(convertSerieDates).
		Finish()

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

func GetSeasons(serie *Serie, language, etag string) ([]Season, error) {
	var err error
	var seasons []Season
	for _, season := range serie.Seasons {
		seasonNumber := strconv.FormatInt(int64(season.Season_number), 10)
		s, err2 := gott.
			NewResult(gott.Tuple{TmdbRequest{serie.Id, etag, language, seasonNumber}, nil, nil, nil, nil}).
			Bind(createSeasonRequest).
			Bind(doRequest).
			Bind(handleRequestError).
			Bind(readResponse).
			Bind(unmarshalSeason).
			Bind(convertSeasonDates).
			Finish()
		err = utils.Or(err, err2)
		seasons = append(seasons, *s.(gott.Tuple)[3].(*Season))
	}
	return seasons, err
}
