package common

import (
	"encoding/xml"
	"fmt"
	"io/ioutil"
	"os"
	"strings"
	"time"
	"log"
)

const (
	// LocationDefaultEndpoint is the default API endpoint of Location services
	locationDefaultEndpoint = "https://location.aliyuncs.com"
	locationAPIVersion      = "2015-06-12"
	HTTP_PROTOCOL           = "http"
	HTTPS_PROTOCOL          = "https"
)

var (
	endpoints = make(map[Region]map[string]string)

	SpecailEnpoints = map[Region]map[string]string{
		APNorthEast1: {
			"ecs": "https://ecs.ap-northeast-1.aliyuncs.com",
			"slb": "https://slb.ap-northeast-1.aliyuncs.com",
			"rds": "https://rds.ap-northeast-1.aliyuncs.com",
			"vpc": "https://vpc.ap-northeast-1.aliyuncs.com",
		},
		APSouthEast2: {
			"ecs": "https://ecs.ap-southeast-2.aliyuncs.com",
			"slb": "https://slb.ap-southeast-2.aliyuncs.com",
			"rds": "https://rds.ap-southeast-2.aliyuncs.com",
			"vpc": "https://vpc.ap-southeast-2.aliyuncs.com",
		},
		APSouthEast3: {
			"ecs": "https://ecs.ap-southeast-3.aliyuncs.com",
			"slb": "https://slb.ap-southeast-3.aliyuncs.com",
			"rds": "https://rds.ap-southeast-3.aliyuncs.com",
			"vpc": "https://vpc.ap-southeast-3.aliyuncs.com",
		},
		MEEast1: {
			"ecs": "https://ecs.me-east-1.aliyuncs.com",
			"slb": "https://slb.me-east-1.aliyuncs.com",
			"rds": "https://rds.me-east-1.aliyuncs.com",
			"vpc": "https://vpc.me-east-1.aliyuncs.com",
		},
		EUCentral1: {
			"ecs": "https://ecs.eu-central-1.aliyuncs.com",
			"slb": "https://slb.eu-central-1.aliyuncs.com",
			"rds": "https://rds.eu-central-1.aliyuncs.com",
			"vpc": "https://vpc.eu-central-1.aliyuncs.com",
		},
		Zhangjiakou: {
			"ecs": "https://ecs.cn-zhangjiakou.aliyuncs.com",
			"slb": "https://slb.cn-zhangjiakou.aliyuncs.com",
			"rds": "https://rds.cn-zhangjiakou.aliyuncs.com",
			"vpc": "https://vpc.cn-zhangjiakou.aliyuncs.com",
		},
		Huhehaote: {
			"ecs": "https://ecs.cn-huhehaote.aliyuncs.com",
			"slb": "https://slb.cn-huhehaote.aliyuncs.com",
			"rds": "https://rds.cn-huhehaote.aliyuncs.com",
			"vpc": "https://vpc.cn-huhehaote.aliyuncs.com",
		},
	}
)

//init endpoints from file
func init() {

}

type LocationClient struct {
	Client
}

func NewLocationClient(accessKeyId, accessKeySecret, securityToken string) *LocationClient {
	endpoint := os.Getenv("LOCATION_ENDPOINT")
	if endpoint == "" {
		endpoint = locationDefaultEndpoint
	}

	client := &LocationClient{}
	client.Init(endpoint, locationAPIVersion, accessKeyId, accessKeySecret)
	client.securityToken = securityToken
	return client
}

func NewLocationClientWithSecurityToken(accessKeyId, accessKeySecret, securityToken string) *LocationClient {
	endpoint := os.Getenv("LOCATION_ENDPOINT")
	if endpoint == "" {
		endpoint = locationDefaultEndpoint
	}

	client := &LocationClient{}
	client.WithEndpoint(endpoint).
		WithVersion(locationAPIVersion).
		WithAccessKeyId(accessKeyId).
		WithAccessKeySecret(accessKeySecret).
		WithSecurityToken(securityToken).
		InitClient()
	return client
}

func (client *LocationClient) DescribeEndpoint(args *DescribeEndpointArgs) (*DescribeEndpointResponse, error) {
	response := &DescribeEndpointResponse{}
	err := client.Invoke("DescribeEndpoint", args, response)
	if err != nil {
		return nil, err
	}
	return response, err
}

func (client *LocationClient) DescribeEndpoints(args *DescribeEndpointsArgs) (*DescribeEndpointsResponse, error) {
	response := &DescribeEndpointsResponse{}
	err := client.Invoke("DescribeEndpoints", args, response)
	if err != nil {
		return nil, err
	}
	return response, err
}

func getProductRegionEndpoint(region Region, serviceCode string) string {
	if sp, ok := endpoints[region]; ok {
		if endpoint, ok := sp[serviceCode]; ok {
			return endpoint
		}
	}

	return ""
}

func setProductRegionEndpoint(region Region, serviceCode string, endpoint string) {
	endpoints[region] = map[string]string{
		serviceCode: endpoint,
	}
}

func (client *LocationClient) DescribeOpenAPIEndpoint(region Region, serviceCode string) string {
	if endpoint := getProductRegionEndpoint(region, serviceCode); endpoint != "" {
		return endpoint
	}
	defaultProtocols := HTTP_PROTOCOL

	args := &DescribeEndpointsArgs{
		Id:          region,
		ServiceCode: serviceCode,
		Type:        "openAPI",
	}

	var endpoint *DescribeEndpointsResponse
	var err error
	for index := 0; index < 5; index++ {
		endpoint, err = client.DescribeEndpoints(args)
		if err == nil && endpoint != nil && len(endpoint.Endpoints.Endpoint) > 0 {
			break
		}
		time.Sleep(500 * time.Millisecond)
	}

	if err != nil || endpoint == nil || len(endpoint.Endpoints.Endpoint) <= 0 {
		log.Printf("aliyungo: can not get endpoint from service, use default. endpoint=[%v], error=[%v]\n",endpoint, err)
		return ""
	}

	for _, protocol := range endpoint.Endpoints.Endpoint[0].Protocols.Protocols {
		if strings.ToLower(protocol) == HTTPS_PROTOCOL {
			defaultProtocols = HTTPS_PROTOCOL
			break
		}
	}

	ep := fmt.Sprintf("%s://%s", defaultProtocols, endpoint.Endpoints.Endpoint[0].Endpoint)

	setProductRegionEndpoint(region, serviceCode, ep)
	return ep
}

func loadEndpointFromFile(region Region, serviceCode string) string {
	data, err := ioutil.ReadFile("./endpoints.xml")
	if err != nil {
		return ""
	}
	var endpoints Endpoints
	err = xml.Unmarshal(data, &endpoints)
	if err != nil {
		return ""
	}
	for _, endpoint := range endpoints.Endpoint {
		if endpoint.RegionIds.RegionId == string(region) {
			for _, product := range endpoint.Products.Product {
				if strings.ToLower(product.ProductName) == serviceCode {
					return fmt.Sprintf("%s://%s", HTTPS_PROTOCOL, product.DomainName)
				}
			}
		}
	}

	return ""
}
