// Copyright 2021 The Erigon Authors
// This file is part of Erigon.
//
// Erigon is free software: you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Erigon is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public License
// along with Erigon. If not, see <http://www.gnu.org/licenses/>.

package downloadergrpc

import (
	"context"
	"encoding/hex"
	"fmt"
	"time"

	"github.com/anacrolix/torrent/metainfo"
	"github.com/c2h5oh/datasize"
	"google.golang.org/grpc"
	"google.golang.org/grpc/backoff"
	"google.golang.org/grpc/credentials/insecure"
	"google.golang.org/grpc/keepalive"

	"github.com/erigontech/erigon/node/gointerfaces"
	"github.com/erigontech/erigon/node/gointerfaces/downloaderproto"
	"github.com/erigontech/erigon/node/gointerfaces/typesproto"
)

func NewClient(ctx context.Context, downloaderAddr string) (downloaderproto.DownloaderClient, error) {
	// creating grpc client connection
	backoffCfg := backoff.DefaultConfig
	backoffCfg.BaseDelay = 500 * time.Millisecond
	backoffCfg.MaxDelay = 10 * time.Second
	dialOpts := []grpc.DialOption{
		grpc.WithConnectParams(grpc.ConnectParams{Backoff: backoffCfg, MinConnectTimeout: 10 * time.Minute}),
		grpc.WithDefaultCallOptions(grpc.MaxCallRecvMsgSize(int(16 * datasize.MB))),
		grpc.WithKeepaliveParams(keepalive.ClientParameters{}),
		grpc.WithTransportCredentials(insecure.NewCredentials()),
	}
	conn, err := grpc.DialContext(ctx, downloaderAddr, dialOpts...)
	if err != nil {
		return nil, fmt.Errorf("creating client connection to sentry P2P: %w", err)
	}
	return downloaderproto.NewDownloaderClient(conn), nil
}

func InfoHashes2Proto(in []metainfo.Hash) []*typesproto.H160 {
	infoHashes := make([]*typesproto.H160, len(in))
	i := 0
	for _, h := range in {
		infoHashes[i] = gointerfaces.ConvertAddressToH160(h)
		i++
	}
	return infoHashes
}

func Strings2Proto(in []string) []*typesproto.H160 {
	infoHashes := make([]*typesproto.H160, len(in))
	i := 0
	for _, h := range in {
		infoHashes[i] = String2Proto(h)
		i++
	}
	return infoHashes
}

func String2Proto(in string) *typesproto.H160 {
	var infoHash [20]byte
	inHex, _ := hex.DecodeString(in)
	copy(infoHash[:], inHex)
	return gointerfaces.ConvertAddressToH160(infoHash)
}

func Proto2String(in *typesproto.H160) string {
	addr := gointerfaces.ConvertH160toAddress(in)
	return hex.EncodeToString(addr[:])
}
