package roborock

import (
	"crypto/hmac"
	"crypto/md5"
	"crypto/sha256"
	"encoding/base64"
	"encoding/json"
	"fmt"
	"net/http"
	"net/url"
	"strconv"
	"time"

	"github.com/AlexxIT/go2rtc/pkg/core"
)

type UserInfo struct {
	Token string `json:"token"`
	IoT   struct {
		User   string `json:"u"`
		Pass   string `json:"s"`
		Hash   string `json:"h"`
		Domain string `json:"k"`
		URL    struct {
			API  string `json:"a"`
			MQTT string `json:"m"`
		} `json:"r"`
	} `json:"rriot"`
}

func GetBaseURL(username string) (string, error) {
	u := "https://euiot.roborock.com/api/v1/getUrlByEmail?email=" + url.QueryEscape(username)
	req, err := http.NewRequest("POST", u, nil)
	if err != nil {
		return "", err
	}

	client := http.Client{Timeout: time.Second * 5000}
	res, err := client.Do(req)

	var v struct {
		Msg  string `json:"msg"`
		Code int    `json:"code"`
		Data struct {
			URL string `json:"url"`
		} `json:"data"`
	}
	if err = json.NewDecoder(res.Body).Decode(&v); err != nil {
		return "", err
	}

	if v.Code != 200 {
		return "", fmt.Errorf("%d: %s", v.Code, v.Msg)
	}

	return v.Data.URL, nil
}

func Login(baseURL, username, password string) (*UserInfo, error) {
	u := baseURL + "/api/v1/login?username=" + url.QueryEscape(username) +
		"&password=" + url.QueryEscape(password) + "&needtwostepauth=false"
	req, err := http.NewRequest("POST", u, nil)
	if err != nil {
		return nil, err
	}

	clientID := core.RandString(16, 64)
	clientID = base64.StdEncoding.EncodeToString([]byte(clientID))
	req.Header.Set("header_clientid", clientID)

	client := http.Client{Timeout: time.Second * 5000}
	res, err := client.Do(req)

	var v struct {
		Msg  string   `json:"msg"`
		Code int      `json:"code"`
		Data UserInfo `json:"data"`
	}
	if err = json.NewDecoder(res.Body).Decode(&v); err != nil {
		return nil, err
	}

	if v.Code != 200 {
		return nil, fmt.Errorf("%d: %s", v.Code, v.Msg)
	}

	return &v.Data, nil
}

func GetHomeID(baseURL, token string) (int, error) {
	req, err := http.NewRequest("GET", baseURL+"/api/v1/getHomeDetail", nil)
	if err != nil {
		return 0, err
	}
	req.Header.Set("Authorization", token)

	client := http.Client{Timeout: time.Second * 5000}
	res, err := client.Do(req)
	if err != nil {
		return 0, err
	}

	var v struct {
		Msg  string `json:"msg"`
		Code int    `json:"code"`
		Data struct {
			HomeID int `json:"rrHomeId"`
		} `json:"data"`
	}
	if err = json.NewDecoder(res.Body).Decode(&v); err != nil {
		return 0, err
	}

	if v.Code != 200 {
		return 0, fmt.Errorf("%d: %s", v.Code, v.Msg)
	}

	return v.Data.HomeID, nil
}

type DeviceInfo struct {
	DID  string `json:"duid"`
	Name string `json:"name"`
	Key  string `json:"localKey"`
}

func GetDevices(ui *UserInfo, homeID int) ([]DeviceInfo, error) {
	nonce := core.RandString(6, 64)
	ts := time.Now().Unix()
	path := "/user/homes/" + strconv.Itoa(homeID)

	mac := fmt.Sprintf(
		"%s:%s:%s:%d:%x::", ui.IoT.User, ui.IoT.Pass, nonce, ts, md5.Sum([]byte(path)),
	)
	hash := hmac.New(sha256.New, []byte(ui.IoT.Hash))
	hash.Write([]byte(mac))
	mac = base64.StdEncoding.EncodeToString(hash.Sum(nil))

	auth := fmt.Sprintf(
		`Hawk id="%s", s="%s", ts="%d", nonce="%s", mac="%s"`,
		ui.IoT.User, ui.IoT.Pass, ts, nonce, mac,
	)

	req, err := http.NewRequest("GET", ui.IoT.URL.API+path, nil)
	if err != nil {
		return nil, err
	}
	req.Header.Set("Authorization", auth)

	client := http.Client{Timeout: time.Second * 5000}
	res, err := client.Do(req)
	if err != nil {
		return nil, err
	}

	var v struct {
		Result struct {
			Devices []DeviceInfo `json:"devices"`
		} `json:"result"`
	}
	if err = json.NewDecoder(res.Body).Decode(&v); err != nil {
		return nil, err
	}

	return v.Result.Devices, nil
}
