package desktop

import (
	"encoding/json"
	"fmt"
	"io/ioutil"
	"net/url"
	"os"
	"os/user"
	"path/filepath"

	"github.com/cloudfoundry/stratos/src/jetstream/api"
)

// CFConfigFile represents the data we need for CF config file
type CFConfigFile struct {
	APIEndpoint            string `json:"Target"`
	AuthorizationEndpoint  string `json:"AuthorizationEndpoint"`
	TokenEndpoint          string `json:"UaaEndpoint"`
	DopplerLoggingEndpoint string `json:"DopplerEndPoint"`
	SkipSSLValidation      bool   `json:"SSLDisabled"`
	ClientID               string `json:"UAAOAuthClient"`
	ClientSecret           string `json:"UAAOAuthClientSecret"`
	AccessToken            string `json:"AccessToken"`
	RefreshToken           string `json:"RefreshToken"`
}

// ListCloudFoundry will list Cloud Foundry endpoints configured locally (can be only one)
func ListCloudFoundry() ([]*api.CNSIRecord, error) {
	cfg, apiEndpoint, err := readCFFile()
	if err != nil {
		return nil, err
	}

	// Ignore if the api endpoint is empty
	if len(cfg.APIEndpoint) == 0 {
		return nil, nil
	}

	eps := make([]*api.CNSIRecord, 1)
	eps[0] = &api.CNSIRecord{
		GUID:                   getEndpointGUID(cfg.APIEndpoint),
		Name:                   "CF",
		CNSIType:               "cf",
		APIEndpoint:            apiEndpoint,
		AuthorizationEndpoint:  cfg.AuthorizationEndpoint,
		DopplerLoggingEndpoint: cfg.DopplerLoggingEndpoint,
		TokenEndpoint:          cfg.TokenEndpoint,
		SkipSSLValidation:      cfg.SkipSSLValidation,
		SSOAllowed:             false,
		ClientId:               cfg.ClientID,
		ClientSecret:           cfg.ClientSecret,
		Local:                  true,
	}
	return eps, nil
}

// ListConnectedCloudFoundry will list Cloud Foundry endpoints configured locally (can be only one)
func ListConnectedCloudFoundry() ([]*api.ConnectedEndpoint, error) {
	cfg, apiEndpoint, err := readCFFile()
	if err != nil {
		return nil, err
	}

	// Ignore if the api endpoint is empty
	if len(cfg.APIEndpoint) == 0 {
		return nil, nil
	}

	//TODO: Token expiry
	eps := make([]*api.ConnectedEndpoint, 1)
	eps[0] = &api.ConnectedEndpoint{
		GUID:                   getEndpointGUID(cfg.APIEndpoint),
		Name:                   "CF",
		CNSIType:               "cf",
		APIEndpoint:            apiEndpoint,
		Account:                "local",
		TokenExpiry:            20000,
		AuthorizationEndpoint:  cfg.AuthorizationEndpoint,
		DopplerLoggingEndpoint: cfg.DopplerLoggingEndpoint,
		SkipSSLValidation:      cfg.SkipSSLValidation,
		Local:                  true,
	}
	return eps, nil
}

func readCFFile() (*CFConfigFile, *url.URL, error) {

	var url *url.URL
	usr, err := user.Current()
	if err != nil {
		return nil, url, err
	}

	cfFile := filepath.Join(usr.HomeDir, ".cf", "config.json")

	// Check we can unmarshall the request
	data, err := ioutil.ReadFile(cfFile)
	if err != nil {
		return nil, url, fmt.Errorf("Can not read Cloud Foundry config file: %s", err)
	}

	config := &CFConfigFile{}
	if err = json.Unmarshal(data, config); err != nil {
		return nil, url, fmt.Errorf("Can not parse Cloud Foundry config file: %s", err)
	}

	if len(config.APIEndpoint) > 0 {
		url, err = url.Parse(config.APIEndpoint)
		if err != nil {
			return nil, url, err
		}
	}
	return config, url, nil
}

func updateCFFIle(updates map[string]string) error {
	usr, err := user.Current()
	if err != nil {
		return err
	}

	cfFile := filepath.Join(usr.HomeDir, ".cf", "config.json")

	// Check we can unmarshall the request
	data, err := ioutil.ReadFile(cfFile)
	if err != nil {
		return fmt.Errorf("Can not read Cloud Foundry config file: %s", err)
	}

	file, err := os.Open(cfFile)
	if err != nil {
		return err
	}
	defer file.Close()
	stats, err := file.Stat()
	if err != nil {
		return err
	}

	var config map[string]interface{}
	if err = json.Unmarshal(data, &config); err != nil {
		return fmt.Errorf("Can not parse Cloud Foundry config file: %s", err)
	}

	for k, v := range updates {
		config[k] = v
	}

	data, err = json.Marshal(config)
	if err != nil {
		return err
	}

	ioutil.WriteFile(cfFile, data, stats.Mode())
	return nil
}

func IsLocalCloudFoundry(cnsiGUID string) bool {
	record, _ := FindLocalCloudFoundry(cnsiGUID)
	return record != nil
}

func FindLocalCloudFoundry(cnsiGUID string) (*api.CNSIRecord, error) {

	local, err := ListCloudFoundry()
	if err != nil {
		return nil, err
	}

	if len(local) == 1 && local[0].GUID == cnsiGUID {
		return local[0], nil
	}

	return nil, nil
}
