package main

import (
	"bufio"
	"bytes"
	"encoding/json"
	"io"
	"io/ioutil"
	"net/http"
	"os"
)

type OAuth struct {
	Access_token  string `"json:access_token"`
	Created_at    int    `"json:created_at"`
	Expires_in    int64  `"json:Expires_in"`
	Refresh_token string `"json:refresh_token"`
}

type authError struct {
	msg string
}

func (e *authError) Error() string {
	return e.msg
}

func register_client(endpoint string, o *RunningInstance) error {
	requestBodymap, _ := json.Marshal(map[string]string{
		"client_name":   "Tusky", // Hard-coded in for now...
		"scopes":        "read write follow push",
		"redirect_uris": "urn:ietf:wg:oauth:2.0:oob",
	})
	requestBodybytes := bytes.NewBuffer(requestBodymap)

	api_base_apps := "https://" + endpoint + "/api/v1/apps"

	resp, err := o.client.Post(api_base_apps, "application/json", requestBodybytes)
	if err != nil {
		logErr.Print("Unable to connect to "+api_base_apps+" ", err)
		return err
	}

	body, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		logErr.Print("Unable to read HTTP response: ", err)
		o.client_id = ""
		o.client_secret = ""
		return err
	}
	defer resp.Body.Close()

	bodymap := make(map[string]string)
	err = json.Unmarshal(body, &bodymap)
	if err != nil {
		logErr.Print("Unable to parse response from "+endpoint+": ", err)
		o.client_id = ""
		o.client_secret = ""
		return err
	}

	client_file := "clients/" + endpoint

	f, err := os.Create("clients/" + endpoint)
	if err != nil {
		logErr.Print("Unable to create "+client_file+": ", err)
		o.client_id = ""
		o.client_secret = ""
		return err
	}
	defer f.Close()

	_, err = io.WriteString(f, bodymap["client_id"]+"\n")
	if err != nil {
		logErr.Print("Unable to write client_id line to file "+client_file+": ", err)
		o.client_id = bodymap["client_id"]
		o.client_secret = bodymap["client_secret"]
		return nil
	}
	_, err = io.WriteString(f, bodymap["client_secret"]+"\n")
	if err != nil {
		logErr.Print("Unable to write client_secret to file "+client_file+": ", err)
		o.client_id = bodymap["client_id"]
		o.client_secret = bodymap["client_secret"]
		return nil
	}

	o.client_id = bodymap["client_id"]
	o.client_secret = bodymap["client_secret"]
	return nil
}

func get_client(endpoint string, o *RunningInstance) error {
	var err error
	client_file := "clients/" + endpoint
	_, err = os.Stat(client_file)
	if os.IsNotExist(err) == false { // The file exists
		f, err := os.Open(client_file)
		if err != nil {
			logErr.Print("Unable to open " + client_file + ", creating new client")
			return err
		}
		defer f.Close()

		rd := bufio.NewReader(f)

		client_id_bin, _, err := rd.ReadLine()
		o.client_id = string(client_id_bin)
		if err != nil {
			logErr.Print("Unable to read client_id line of " + client_file + ", building new client")
			return err
		}
		client_secret_bin, _, err := rd.ReadLine()
		o.client_secret = string(client_secret_bin)
		if err != nil {
			logErr.Print("Unable to read client_secret line of " + client_file + ", building new client")
			return err
		}

		return nil
	} else {
		return register_client(endpoint, o)
	}

	return nil
}

func oauth_login(endpoint string, o *RunningInstance, username string, password string) (OAuth, error) {
	authMap, err := json.Marshal(map[string]string{
		"username":      username,
		"password":      password,
		"redirect_uri":  "urn:ietf:wg:oauth:2.0:oob",
		"grant_type":    "password",
		"client_name":   "Tusky",
		"scope":         "read write follow push",
		"client_id":     o.client_id,
		"client_secret": o.client_secret,
	})

	if err != nil {
		logErr.Print("Unable to create Authentication map for "+endpoint)
		return OAuth{}, err
	}

	authMapbytes := bytes.NewBuffer(authMap)

	resp, err := http.Post("https://"+endpoint+"/oauth/token", "application/json", authMapbytes)
	if err != nil {
		logErr.Print("Cannot connect to "+endpoint+": ", err)
		return OAuth{}, err
	}

	body, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		logErr.Print("Unable to read response data for "+endpoint+": ", err)
		return OAuth{}, err
	}

	if resp.StatusCode == 400 {
		logErr.Print("Unable to authenticate to " + endpoint)
		return OAuth{}, &authError{"Authentication error"}
	}

	oauthData := OAuth{}
	err = json.Unmarshal(body, &oauthData)
	if err != nil {
		logErr.Print("Unable to parse json data for "+endpoint+": ", err)
		return OAuth{}, err
	}

	return oauthData, nil
}

func oauth_refresh(endpoint string, client_id string, client_secret string, refresh_token string) (OAuth, error) {
	authMap, _ := json.Marshal(map[string]string{
		"redirect_uri":  "urn:ietf:wg:oauth:2.0:oob",
		"grant_type":    "refresh_token",
		"scope":         "read write follow push",
		"refresh_token": refresh_token,
		"client_id":     client_id,
		"client_secret": client_secret,
	})

	authMapbytes := bytes.NewBuffer(authMap)

	resp, err := http.Post("https://"+endpoint+"/oauth/token", "application/json", authMapbytes)
	if err != nil {
		logErr.Print("Unable to connect to "+endpoint+": ", err)
		return OAuth{}, err
	}

	body, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		logErr.Print("Unable to read response data for "+endpoint+": ", err)
		return OAuth{}, err
	}

	oauthData := OAuth{}
	err = json.Unmarshal(body, &oauthData)
	if err != nil {
		logErr.Print("Unable to parse json data for "+endpoint+": ", err)
		return oauthData, err
	}

	return oauthData, nil
}