/*
 * Licensed to the Apache Software Foundation (ASF) under one or more
 * contributor license agreements.  See the NOTICE file distributed with
 * this work for additional information regarding copyright ownership.
 * The ASF licenses this file to You under the Apache License, Version 2.0
 * (the "License"); you may not use this file except in compliance with
 * the License.  You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package resolver

import (
	"errors"
	"log"
	"net/url"

	"fmt"
	"github.com/apache/servicecomb-mesher/proxy/config"
	"github.com/go-chassis/openlog"
)

var drMap = make(map[string]DestinationResolver)

//DestinationResolverPlugins is a map
var DestinationResolverPlugins map[string]func() DestinationResolver

//SelfEndpoint is a string
var SelfEndpoint = "#To be init#"

//DefaultPlugin is a constant which stores default plugin name
const DefaultPlugin = "host"

//ErrUnknownResolver is of type error
var ErrUnknownResolver = errors.New("unknown Destination Resolver")

//DestinationResolver is a interface with Resolve method
type DestinationResolver interface {
	Resolve(remoteIP, host, rawURI string, header map[string]string) (string, string, error)
}

//DefaultDestinationResolver is a struct
//mesher as sidecar must use DefaultDestinationResolver
type DefaultDestinationResolver struct {
}

//Resolve resolves service's endpoint
//service may have multiple port for same protocol
func (dr *DefaultDestinationResolver) Resolve(remoteIP, host, rawURI string, header map[string]string) (string, string, error) {
	u, err := url.Parse(rawURI)
	if err != nil {
		openlog.Error("Can not parse url: " + err.Error())
		return "", "", err
	}

	if u.Host == "" {
		return "", "", errors.New(`Invalid uri, please check:
1, For provider, mesher listens on external ip
2, Set http_proxy as mesher address, before sending request`)
	}

	if u.Host == SelfEndpoint {
		return "", "", errors.New(`uri format must be: http://serviceName/api`)
	}

	return u.Hostname(), u.Port(), nil
}

//New function returns new DefaultDestinationResolver struct object
func New() DestinationResolver {
	return &DefaultDestinationResolver{}
}

//GetDestinationResolver returns destinationResolver pointer
func GetDestinationResolver(name string) DestinationResolver {
	return drMap[name]
}

//InstallDestinationResolverPlugin function installs new plugin
func InstallDestinationResolverPlugin(name string, newFunc func() DestinationResolver) {
	DestinationResolverPlugins[name] = newFunc
	log.Printf("Installed DestinationResolver Plugin, name=%s", name)
}

//SetDefaultDestinationResolver set the a default implementation for a protocol, so that you don't need to set config file
func SetDefaultDestinationResolver(name string, dr DestinationResolver) {
	drMap[name] = dr
	log.Printf("Installed default DestinationResolver for [%s]", name)
}
func init() {
	DestinationResolverPlugins = make(map[string]func() DestinationResolver)
	InstallDestinationResolverPlugin(DefaultPlugin, New)
	SetDefaultDestinationResolver("http", &DefaultDestinationResolver{})
}

//Init function reads config and initiates it
func Init() error {
	if config.GetConfig().Plugin != nil {
		for name, v := range config.GetConfig().Plugin.DestinationResolver {
			if v == "" {
				v = DefaultPlugin
			}
			f, ok := DestinationResolverPlugins[v]
			if !ok {
				return fmt.Errorf("unknown destination resolver [%s]", v)
			}
			drMap[name] = f()
		}
	}

	return nil
}
