package internals

import (
	"context"
	"crypto/tls"
	"errors"
	"fmt"
	"log"
	"net/http"

	"github.com/gregjones/httpcache"
	"github.com/gregjones/httpcache/leveldbcache"
	loghttp "github.com/motemen/go-loghttp"
	"github.com/syndtr/goleveldb/leveldb"
	"golang.org/x/oauth2"
)

var (
	googleOauthConfig = &oauth2.Config{
		RedirectURL:  "http://localhost:3000/callback",
		ClientID:     "CLIENTKEY",
		ClientSecret: "SECRETKEY",
		Scopes: []string{
			"esi-skills.read_skillqueue.v1",
			"esi-skills.read_skills.v1",
			"esi-planets.manage_planets.v1",
			"esi-wallet.read_character_wallet.v1",
			"esi-markets.read_character_orders.v1",
			"esi-industry.read_character_jobs.v1",
			"esi-location.read_location.v1",
			"esi-clones.read_clones.v1",
			"esi-universe.read_structures.v1",
		},
		Endpoint: oauth2.Endpoint{
			AuthURL:  "https://login.eveonline.com/oauth/authorize/",
			TokenURL: "https://login.eveonline.com/oauth/token/",
		},
	}
	// Some random string, random for each request
	oauthStateString = "random"
)

// GetTokenURL -
func GetTokenURL(config *HTTPConfiguration) string {
	googleOauthConfig.ClientID = config.ClientID
	googleOauthConfig.ClientSecret = config.ClientSecret

	url := googleOauthConfig.AuthCodeURL(oauthStateString, oauth2.AccessTypeOffline)

	// https://eveonline-third-party-documentation.readthedocs.io/en/latest/sso/authentication.html
	// response_type: Must be set to “code”.
	url = url + "&response_type=code"
	return url
}

// GetTemporaryClient -
func GetTemporaryClient(r *http.Request) (*http.Client, *oauth2.Token, error) {
	state := r.FormValue("state")
	if state != oauthStateString {
		errorTxt := fmt.Sprintf("invalid oauth state, expected '%s', got '%s'\n", oauthStateString, state)
		return nil, nil, errors.New(errorTxt)
	}

	code := r.FormValue("code")
	token, err := googleOauthConfig.Exchange(oauth2.NoContext, code)
	if err != nil {
		errorTxt := fmt.Sprintf("Code exchange failed with '%s'\n", err)
		return nil, nil, errors.New(errorTxt)
	}
	client := googleOauthConfig.Client(oauth2.NoContext, token)

	return client, token, nil
}

// GetDefaultClient - Returns the default client based on the given configuration
func GetDefaultClient(cacheDB *leveldb.DB, config *HTTPConfiguration) *http.Client {

	ldb := leveldbcache.NewWithDB(cacheDB)
	cachingTransport := httpcache.NewTransport(ldb)

	if config.InsecureCalls {
		insecureTransport := &http.Transport{
			TLSClientConfig: &tls.Config{InsecureSkipVerify: true},
			Proxy:           http.ProxyFromEnvironment,
		}
		cachingTransport.Transport = insecureTransport
	}

	if config.LogCalls {
		var CustomLogResponse = func(resp *http.Response) {
			log.Printf("<--- HTTP %d.%d %d %s (expires on %s) %s",
				resp.ProtoMajor, resp.ProtoMinor,
				resp.StatusCode,
				resp.Request.URL,
				resp.Header.Get("expires"),
				resp.Header.Get("content-type"),
			)
		}

		loggingTransport := &loghttp.Transport{
			LogResponse: CustomLogResponse,
		}
		loggingTransport.Transport = cachingTransport.Transport
		cachingTransport.Transport = loggingTransport
	}

	cachingClient := &http.Client{Transport: cachingTransport}

	ctx := context.WithValue(context.TODO(), oauth2.HTTPClient, cachingClient)

	googleOauthConfig.ClientID = config.ClientID
	googleOauthConfig.ClientSecret = config.ClientSecret

	client := googleOauthConfig.Client(ctx, config.ConnectionToken)

	return client
}

// HTTPConfiguration - Configuration for the HTTP swagger client.
type HTTPConfiguration struct {
	ClientID        string
	ClientSecret    string
	LogCalls        bool
	InsecureCalls   bool
	ConnectionToken *oauth2.Token
}
