package hubrpc

import (
	"fmt"
	"sync"
	"time"

	"gitlink.org.cn/cloudream/common/consts/errorcode"
	"gitlink.org.cn/cloudream/jcs-pub/common/pkgs/rpc"
	grpc "google.golang.org/grpc"
	"google.golang.org/grpc/credentials/insecure"
)

type PoolConfig struct{}

type Pool struct {
	grpcCons map[grpcAddr]*grpcCon
	lock     sync.Mutex
}

type grpcAddr struct {
	IP   string
	Port int
}

type grpcCon struct {
	grpcCon     *grpc.ClientConn
	refCount    int
	stopClosing chan any
}

func NewPool(cfg PoolConfig) *Pool {
	return &Pool{
		grpcCons: make(map[grpcAddr]*grpcCon),
	}
}

func (p *Pool) Get(ip string, port int) *Client {
	p.lock.Lock()
	defer p.lock.Unlock()

	ga := grpcAddr{IP: ip, Port: port}
	con := p.grpcCons[ga]
	if con == nil {
		gcon, err := grpc.NewClient(fmt.Sprintf("%v:%v", ip, port), grpc.WithTransportCredentials(insecure.NewCredentials()))
		if err != nil {
			return &Client{
				addr:     ga,
				con:      nil,
				pool:     p,
				fusedErr: rpc.Failed(errorcode.OperationFailed, err.Error()),
			}
		}

		con = &grpcCon{
			grpcCon:     gcon,
			refCount:    0,
			stopClosing: nil,
		}

		p.grpcCons[ga] = con
	}

	con.refCount++

	return &Client{
		addr: ga,
		con:  con.grpcCon,
		cli:  NewHubClient(con.grpcCon),
		pool: p,
	}
}

func (p *Pool) release(addr grpcAddr) {
	p.lock.Lock()
	defer p.lock.Unlock()

	grpcCon := p.grpcCons[addr]
	if grpcCon == nil {
		return
	}

	grpcCon.refCount--
	grpcCon.refCount = max(grpcCon.refCount, 0)

	if grpcCon.refCount == 0 {
		stopClosing := make(chan any)
		grpcCon.stopClosing = stopClosing

		go func() {
			select {
			case <-stopClosing:
				return

			case <-time.After(time.Minute):
				p.lock.Lock()
				defer p.lock.Unlock()

				if grpcCon.refCount == 0 {
					grpcCon.grpcCon.Close()
					delete(p.grpcCons, addr)
				}
			}
		}()
	}
}
