package main

import (
	"errors"
	"fmt"
	"log"
	"reflect"
	"sync"

	"github.com/thrasher-corp/gocryptotrader/common"
	"github.com/thrasher-corp/gocryptotrader/engine"
	exchange "github.com/thrasher-corp/gocryptotrader/exchanges"
)

func main() {
	var err error
	engine.Bot, err = engine.New()
	if err != nil {
		log.Fatalf("Failed to initialise engine. Err: %s", err)
	}

	engine.Bot.Settings = engine.Settings{
		DisableExchangeAutoPairUpdates: true,
		EnableDryRun:                   true,
	}

	engine.Bot.Config.PurgeExchangeAPICredentials()
	engine.Bot.ExchangeManager = engine.SetupExchangeManager()

	log.Printf("Loading exchanges..")
	var wg sync.WaitGroup
	for x := range exchange.Exchanges {
		err = engine.Bot.LoadExchange(exchange.Exchanges[x], &wg)
		if err != nil {
			log.Printf("Failed to load exchange %s. Err: %s",
				exchange.Exchanges[x],
				err)
			continue
		}
	}
	wg.Wait()
	log.Println("Done.")

	log.Printf("Testing exchange wrappers..")
	results := make(map[string][]string)
	wg = sync.WaitGroup{}
	exchanges := engine.Bot.GetExchanges()
	for x := range exchanges {
		exch := exchanges[x]
		wg.Add(1)
		go func(e exchange.IBotExchange) {
			results[e.GetName()], err = testWrappers(e)
			if err != nil {
				fmt.Printf("failed to test wrappers for %s %s", e.GetName(), err)
			}
			wg.Done()
		}(exch)
	}
	wg.Wait()
	log.Println("Done.")

	var dummyInterface exchange.IBotExchange
	totalWrappers := reflect.TypeOf(&dummyInterface).Elem().NumMethod()

	log.Println()
	for name, funcs := range results {
		pct := float64(totalWrappers-len(funcs)) / float64(totalWrappers) * 100
		log.Printf("Exchange %s wrapper coverage [%d/%d - %.2f%%] | Total missing: %d",
			name,
			totalWrappers-len(funcs),
			totalWrappers,
			pct,
			len(funcs))
		log.Printf("\t Wrappers not implemented:")

		for x := range funcs {
			log.Printf("\t - %s", funcs[x])
		}
		log.Println()
	}
}

// testWrappers executes and checks each IBotExchange's function return for the
// error common.ErrNotYetImplemented to verify whether the wrapper function has
// been implemented yet.
func testWrappers(e exchange.IBotExchange) ([]string, error) {
	iExchange := reflect.TypeOf(&e).Elem()
	actualExchange := reflect.ValueOf(e)
	errType := reflect.TypeOf(common.ErrNotYetImplemented)

	var funcs []string
	for x := 0; x < iExchange.NumMethod(); x++ {
		name := iExchange.Method(x).Name
		method := actualExchange.MethodByName(name)
		inputs := make([]reflect.Value, method.Type().NumIn())
		for y := 0; y < method.Type().NumIn(); y++ {
			input := method.Type().In(y)
			inputs[y] = reflect.Zero(input)
		}

		outputs := method.Call(inputs)
		for y := range outputs {
			incoming := outputs[y].Interface()
			if reflect.TypeOf(incoming) == errType {
				err, ok := incoming.(error)
				if !ok {
					return nil, fmt.Errorf("%s type assertion failure for %v", name, incoming)
				}
				if errors.Is(err, common.ErrNotYetImplemented) {
					funcs = append(funcs, name)
				}
				// found error; there should not be another error in this slice.
				break
			}
		}
	}
	return funcs, nil
}
