package scdbclient

import (
	"sync"
	"sync/atomic"
	"time"

	"google.golang.org/grpc"
	"google.golang.org/grpc/credentials/insecure"
)

type proxyClient struct {
	*gRpcClientPool
}

type Options struct {
	Host           string
	Port           int
	MingRpcChannl  int32
	MaxgRpcChannel int32
}

type gRpcClientPool struct {
	mu             sync.RWMutex
	pool           []*connectionpool
	addr           string
	minchannelsize int32
	maxchannelsize int32
	channelSize    *atomic.Int32
}

type connectionpool struct {
	connection *grpc.ClientConn
	lastCall   time.Time
	status     bool
	sessions   *atomic.Int32
}

func (proxy *proxyClient) getPool() *connectionpool {
	maxRetries := 100
	retryInterval := time.Millisecond
	for i := 0; i < maxRetries; i++ {
		getconn := proxy.getConnection()
		if getconn == nil {
			if proxy.channelSize.Load() <= proxy.maxchannelsize {
				newconn := proxy.addConnection()
				if newconn == nil {
					return nil
				}
				newconn.lastCall = time.Now()
				newconn.status = true
				newconn.sessions.Add(1)
				return newconn
			}
			time.Sleep(retryInterval)
		} else if getconn != nil {
			getconn.lastCall = time.Now()
			getconn.sessions.Add(1)
			return getconn
		}
	}
	return nil
}

func (proxy *proxyClient) getConnection() *connectionpool {
	proxy.mu.RLock()
	defer proxy.mu.RUnlock()
	for index, connection := range proxy.pool {
		if connection.sessions.Load() <= 100 && connection.status {
			proxy.pool[index].lastCall = time.Now()
			return proxy.pool[index]
		}
	}
	return nil
}

func (proxy *proxyClient) addConnection() *connectionpool {
	proxy.mu.Lock()
	defer proxy.mu.Unlock()
	newconn := &connectionpool{}
	conn, err := grpc.Dial(proxy.addr, grpc.WithTransportCredentials(insecure.NewCredentials()))
	if err != nil {
		return nil
	}
	newconn.connection = conn
	newconn.status = true
	newconn.lastCall = time.Now()
	proxy.pool = append(proxy.pool, newconn)
	proxy.channelSize.Add(1)
	return newconn
}

func (proxy *proxyClient) removeConnection(remover *connectionpool) {
	proxy.mu.Lock()
	defer proxy.mu.Unlock()
	for index, wrapper := range proxy.pool {
		if wrapper == remover {
			if index > int(proxy.minchannelsize) {
				proxy.pool[index].connection.Close()
				proxy.pool = append(proxy.pool[:index], proxy.pool[index+1:]...)
				proxy.channelSize.Add(-1)
			} else {
				proxy.pool[index].connection.Close()
				proxy.pool[index] = proxy.addConnection()
				// already add connection, channel size+1 but this logic purpose is maintain channel size
				// because only channel connection change healthy
				proxy.channelSize.Add(-1)
			}
		}
	}
}
