package client

import (
	"net"
	"sync"
	"time"

	"github.com/adamweixuan/getty"
	"github.com/coocood/freecache"

	"virjar.com/majora-go/infra"
	"virjar.com/majora-go/log"
	"virjar.com/majora-go/model"
	"virjar.com/majora-go/safe"
)

type Client struct {
	config *model.Configure

	localAddr    net.Addr
	natTunnel    getty.Client
	session      getty.Session
	redial       *infra.PPPRedial
	connStore    sync.Map
	sessionStore sync.Map
	dnsCache     *freecache.Cache
}

func NewClientWithConf(cfg *model.Configure) *Client {
	return NewCli(cfg)
}

func NewCli(cfg *model.Configure) *Client {
	var localAddr net.Addr
	if len(cfg.LocalAddr) > 0 {
		localAddr = &net.TCPAddr{
			IP:   net.ParseIP(cfg.LocalAddr),
			Port: 0,
		}
	}
	client := &Client{
		config:       cfg,
		localAddr:    localAddr,
		connStore:    sync.Map{},
		sessionStore: sync.Map{},
		dnsCache:     freecache.NewCache(1024),
		redial:       infra.NewPPPRedial(),
	}

	return client
}

func (client *Client) StartUp() {
	client.check()
	client.connect()
}

func (client *Client) check() {
	cfg := client.config

	if !cfg.Redial.Valid() {
		return
	}
	interval := cfg.NetCheckInterval
	if interval <= 0 {
		interval = time.Second * 5
	}

	url := infra.RandUrl()
	if len(cfg.NetCheckUrl) > 0 {
		url = cfg.NetCheckUrl
	}

	safe.SageGo(func() {
		var timer = time.NewTimer(interval)
		for {
			timer.Reset(interval)
			<-timer.C
			success := false
			for i := 0; i < 3; i++ {
				success = infra.Ping(url)
				if success {
					break
				}
			}
			if success {
				continue
			}
			log.Run().Warnf("Redial net check fail, redial...")
			client.redial.RedialByCheck(cfg)
			if client.session != nil {
				client.session.Close()
			}
		}
	})
}
