package handler

import (
	"context"
	"encoding/json"
	"io/ioutil"
	"net/http"
	"net/url"
	"time"

	"github.com/micro/micro/v3/service/config"
	"github.com/micro/micro/v3/service/errors"
	"github.com/micro/micro/v3/service/logger"
	pb "github.com/micro/services/time/proto"
	"github.com/tkuchiki/go-timezone"
)

type Time struct {
	Api string
	Key string
	TZ  *timezone.Timezone
}

func New() *Time {
	// TODO: look for "weather.provider" to determine the handler
	v, err := config.Get("weatherapi.api")
	if err != nil {
		logger.Fatalf("weatherapi.api config not found: %v", err)
	}
	api := v.String("")
	if len(api) == 0 {
		logger.Fatal("weatherapi.api config not found")
	}
	v, err = config.Get("weatherapi.key")
	if err != nil {
		logger.Fatalf("weatherapi.key config not found: %v", err)
	}
	key := v.String("")
	if len(key) == 0 {
		logger.Fatal("weatherapi.key config not found")
	}

	return &Time{
		Api: api,
		Key: key,
		TZ:  timezone.New(),
	}
}

func (t *Time) Now(ctx context.Context, req *pb.NowRequest, rsp *pb.NowResponse) error {
	if len(req.Location) == 0 {
		ti := time.Now().In(time.UTC)
		rsp.Localtime = ti.Format("15:04:05")
		rsp.Timestamp = ti.Format(time.RFC3339Nano)
		rsp.Location = "Prime Meridian"
		rsp.Timezone = "UTC"
		rsp.Unix = ti.Unix()
		return nil
	}

	vals := url.Values{}
	vals.Set("key", t.Key)
	vals.Set("q", req.Location)

	resp, err := http.Get(t.Api + "timezone.json?" + vals.Encode())
	if err != nil {
		logger.Errorf("Failed to get time zone: %v\n", err)
		return errors.InternalServerError("time.now", "failed to get time")
	}
	defer resp.Body.Close()

	b, _ := ioutil.ReadAll(resp.Body)

	if resp.StatusCode != 200 {
		logger.Errorf("Failed to get time zone (non 200): %d %v\n", resp.StatusCode, string(b))
		return errors.InternalServerError("time.now", "failed to get time")
	}

	var respBody map[string]interface{}

	if err := json.Unmarshal(b, &respBody); err != nil {
		logger.Errorf("Failed to unmarshal current: %v\n", err)
		return errors.InternalServerError("time.now", "failed to get time")
	}

	location := respBody["location"].(map[string]interface{})

	loc, _ := time.LoadLocation(location["tz_id"].(string))
	ti := time.Now().In(loc)
	isDST := t.TZ.IsDST(ti)

	rsp.Localtime = ti.Format("15:04:05")
	rsp.Timezone, _ = t.TZ.GetTimezoneAbbreviation(location["tz_id"].(string), isDST)
	rsp.Location = ti.Location().String()
	rsp.Timestamp = ti.Format(time.RFC3339Nano)
	rsp.Unix = ti.Unix()

	return nil
}

func (t *Time) Zone(ctx context.Context, req *pb.ZoneRequest, rsp *pb.ZoneResponse) error {
	if len(req.Location) == 0 {
		return errors.BadRequest("time.zone", "invalid location")
	}

	vals := url.Values{}
	vals.Set("key", t.Key)
	vals.Set("q", req.Location)

	resp, err := http.Get(t.Api + "timezone.json?" + vals.Encode())
	if err != nil {
		logger.Errorf("Failed to get time zone: %v\n", err)
		return errors.InternalServerError("time.zone", "failed to get time zone")
	}
	defer resp.Body.Close()

	b, _ := ioutil.ReadAll(resp.Body)

	if resp.StatusCode != 200 {
		logger.Errorf("Failed to get time zone (non 200): %d %v\n", resp.StatusCode, string(b))
		return errors.InternalServerError("time.zone", "failed to get time zone")
	}

	var respBody map[string]interface{}

	if err := json.Unmarshal(b, &respBody); err != nil {
		logger.Errorf("Failed to unmarshal current: %v\n", err)
		return errors.InternalServerError("time.zone", "failed to get current")
	}

	location := respBody["location"].(map[string]interface{})

	rsp.Location = location["name"].(string)
	rsp.Region = location["region"].(string)
	rsp.Country = location["country"].(string)
	rsp.Latitude = location["lat"].(float64)
	rsp.Longitude = location["lon"].(float64)
	rsp.Timezone = location["tz_id"].(string)
	rsp.Localtime = location["localtime"].(string)

	loc, _ := time.LoadLocation(rsp.Timezone)
	ti := time.Now().In(loc)
	isDST := t.TZ.IsDST(ti)
	rsp.Abbreviation, _ = t.TZ.GetTimezoneAbbreviation(rsp.Timezone, isDST)
	rsp.Dst = isDST

	return nil
}
