package openweathermap

import (
	"encoding/json"
	"errors"
	"fmt"
	"net/http"
	"time"
)

var errInvalidUVIndex = errors.New("invalid UV index value")

// UVDataPoints holds the UV specific data
type UVDataPoints struct {
	DT    int64   `json:"dt"`
	Value float64 `json:"value"`
}

// UV contains the response from the OWM UV API
type UV struct {
	Coord []float64      `json:"coord"`
	Data  []UVDataPoints `json:"data,omitempty"`
	/*Data  []struct {
		DT    int64   `json:"dt"`
		Value float64 `json:"value"`
	} `json:"data,omitempty"`*/
	DT    int64   `json:"dt,omitempty"`
	Value float64 `json:"value,omitempty"`
	Key   string
	*Settings
}

// NewUV creates a new reference to UV
func NewUV(key string, options ...Option) (*UV, error) {
	k, err := setKey(key)
	if err != nil {
		return nil, err
	}
	u := &UV{
		Key:      k,
		Settings: NewSettings(),
	}

	if err := setOptions(u.Settings, options); err != nil {
		return nil, err
	}
	return u, nil
}

// Current gets the current UV data for the given coordinates
func (u *UV) Current(coord *Coordinates) error {
	response, err := u.client.Get(fmt.Sprintf("%suvi?lat=%f&lon=%f&appid=%s", uvURL, coord.Latitude, coord.Longitude, u.Key))
	if err != nil {
		return err
	}

	defer response.Body.Close()

	if response.StatusCode == http.StatusUnauthorized {
		return errInvalidKey
	}

	if err = json.NewDecoder(response.Body).Decode(&u); err != nil {
		return err
	}

	return nil
}

// Historical gets the historical UV data for the coordinates and times
func (u *UV) Historical(coord *Coordinates, start, end time.Time) error {
	response, err := u.client.Get(fmt.Sprintf("%shistory?lat=%f&lon=%f&start=%d&end=%d&appid=%s", uvURL, coord.Latitude, coord.Longitude, start.Unix(), end.Unix(), u.Key))
	if err != nil {
		return err
	}

	defer response.Body.Close()

	if response.StatusCode == http.StatusUnauthorized {
		return errInvalidKey
	}

	if err = json.NewDecoder(response.Body).Decode(&u); err != nil {
		return err
	}

	return nil
}

// UVIndexInfo
type UVIndexInfo struct {
	// UVIndex holds the range of the index
	UVIndex []float64

	// MGC represents the Media graphic color
	MGC string

	// Risk of harm from unprotected sun exposure, for the average adult
	Risk string

	// RecommendedProtection contains information on what a person should
	// do when outside in the associated UVIndex
	RecommendedProtection string
}

// UVData contains data in regards to UV index ranges, rankings, and steps for protection
var UVData = []UVIndexInfo{
	{
		UVIndex:               []float64{0, 2.9},
		MGC:                   "Green",
		Risk:                  "Low",
		RecommendedProtection: "Wear sunglasses on bright days; use sunscreen if there is snow on the ground, which reflects UV radiation, or if you have particularly fair skin.",
	},
	{
		UVIndex:               []float64{3, 5.9},
		MGC:                   "Yellow",
		Risk:                  "Moderate",
		RecommendedProtection: "Take precautions, such as covering up, if you will be outside. Stay in shade near midday when the sun is strongest.",
	},
	{
		UVIndex:               []float64{6, 7.9},
		MGC:                   "Orange",
		Risk:                  "High",
		RecommendedProtection: "Cover the body with sun protective clothing, use SPF 30+ sunscreen, wear a hat, reduce time in the sun within three hours of solar noon, and wear sunglasses.",
	},
	{
		UVIndex:               []float64{8, 10.9},
		MGC:                   "Red",
		Risk:                  "Very high",
		RecommendedProtection: "Wear SPF 30+ sunscreen, a shirt, sunglasses, and a wide-brimmed hat. Do not stay in the sun for too long.",
	},
	{
		UVIndex:               []float64{11},
		MGC:                   "Violet",
		Risk:                  "Extreme",
		RecommendedProtection: "Take all precautions: Wear SPF 30+ sunscreen, a long-sleeved shirt and trousers, sunglasses, and a very broad hat. Avoid the sun within three hours of solar noon.",
	},
}

// UVInformation provides information on the given UV data which includes the severity
// and "Recommended protection"
func (u *UV) UVInformation() ([]UVIndexInfo, error) {
	switch {
	case u.Value != 0:
		switch {
		case u.Value < 2.9:
			return []UVIndexInfo{UVData[0]}, nil
		case u.Value > 3 && u.Value < 5.9:
			return []UVIndexInfo{UVData[1]}, nil
		case u.Value > 6 && u.Value < 7.9:
			return []UVIndexInfo{UVData[2]}, nil
		case u.Value > 8 && u.Value < 10.9:
			return []UVIndexInfo{UVData[3]}, nil
		case u.Value >= 11:
			return []UVIndexInfo{UVData[4]}, nil
		default:
			return nil, errInvalidUVIndex
		}

	case len(u.Data) > 0:
		var uvi []UVIndexInfo
		for _, i := range u.Data {
			switch {
			case i.Value < 2.9:
				uvi = append(uvi, UVData[0])
			case i.Value > 3 && u.Value < 5.9:
				uvi = append(uvi, UVData[1])
			case i.Value > 6 && u.Value < 7.9:
				uvi = append(uvi, UVData[2])
			case i.Value > 8 && u.Value < 10.9:
				uvi = append(uvi, UVData[3])
			case i.Value >= 11:
				uvi = append(uvi, UVData[4])
			default:
				return nil, errInvalidUVIndex
			}
		}
	}

	return nil, nil
}
