package login

import (
	"context"
	"errors"
	"net/http"
	"sync"
	"time"

	"github.com/Mmx233/BackoffCli/backoff"
	"github.com/Mmx233/BitSrunLoginGo/internal/config"
	"github.com/Mmx233/BitSrunLoginGo/internal/config/flags"
	"github.com/Mmx233/BitSrunLoginGo/internal/config/keys"
	dns2 "github.com/Mmx233/BitSrunLoginGo/internal/dns"
	"github.com/Mmx233/BitSrunLoginGo/internal/http_client"
	"github.com/Mmx233/BitSrunLoginGo/pkg/srun"
	"github.com/Mmx233/BitSrunLoginGo/tools"
	log "github.com/sirupsen/logrus"
)

var ipLast string
var debugTip sync.Once

type Conf struct {
	Logger                      log.FieldLogger
	IsOnlineDetectLogDebugLevel bool
}

func Login(conf Conf) error {
	logger := conf.Logger
	if config.Settings.Basic.Interfaces == "" { //单网卡
		err := Single(SingleConf{
			Conf: conf,
			Eth:  nil,
		})
		if err != nil {
			logger.Errorln("登录出错: ", err)
			debugTip.Do(func() {
				if !config.Settings.Log.DebugLevel {
					logger.Infoln("开启调试日志 (debug_level) 获取详细信息")
				}
			})
		}
		return err
	} else { //多网卡
		return Interfaces(conf)
	}
}

func ddns(logger log.FieldLogger, ip string, httpClient *http.Client) error {
	return dns2.Run(&dns2.Config{
		Logger:   logger.WithField(keys.LogLoginModule, "ddns"),
		Provider: config.Settings.DDNS.Provider,
		IP:       ip,
		Domain:   config.Settings.DDNS.Domain,
		TTL:      config.Settings.DDNS.TTL,
		Conf:     config.Settings.DDNS.Config,
		Http:     httpClient,
	})
}

func Interfaces(conf Conf) error {
	logger := conf.Logger
	interfaces, err := tools.GetInterfaceAddr(logger, config.Settings.Basic.Interfaces)
	if err != nil {
		return err
	}
	var interval = time.Duration(config.Settings.Basic.InterfacesInterval) * time.Second
	var errCount int
	for i, eth := range interfaces {
		logger.Infoln("使用网卡: ", eth.Name)
		if err := Single(SingleConf{
			Conf: conf,
			Eth:  &eth,
		}); err != nil {
			logger.Errorf("网卡 %s 登录出错: %v", eth.Name, err)
			errCount++
		}
		if i != len(interfaces)-1 {
			time.Sleep(interval)
		}
	}
	if errCount > 0 {
		return errors.New("multi interface login not completely succeed")
	}
	return nil
}

type SingleConf struct {
	Conf
	Eth *tools.Eth
}

func Single(conf SingleConf) error {
	if config.Settings.Backoff.Enable {
		return backoff.NewInstance(func(ctx context.Context) error {
			return doLogin(conf)
		}, config.BackoffConfig).Run(context.TODO())
	} else {
		return doLogin(conf)
	}
}

func doLogin(conf SingleConf) error {
	logger := conf.Logger

	// 登录配置初始化
	httpClient := http_client.ClientSelect(conf.Eth)
	srunClient := srun.New(&srun.Conf{
		Logger: logger,
		Https:  config.Settings.Basic.Https,
		LoginInfo: srun.LoginInfo{
			Form: *config.Form,
			Meta: *config.Meta,
		},
		Client:       httpClient,
		CustomHeader: config.Settings.CustomHeader,
	})

	srunDetector := srunClient.Api.NewDetector()

	// Reality 与 Acid
	var acidOnReality bool
	if config.Settings.Reality.Enable {
		logger := logger.WithField(keys.LogLoginModule, "reality")

		logger.Debugln("开始 Reality 流程")
		acid, _, err := srunDetector.Reality(config.Settings.Reality.Addr, flags.AutoAcid)
		if err != nil {
			logger.Warnln("Reality 请求异常:", err)
		} else if flags.AutoAcid && acid != "" {
			acidOnReality = true
			logger.Debugf("使用嗅探 acid: %s", acid)
			srunClient.LoginInfo.Meta.Acid = acid
		}
	}
	if !acidOnReality && flags.AutoAcid {
		logger := logger.WithField(keys.LogLoginModule, "acid")

		logger.Debugln("开始嗅探")
		acid, err := srunDetector.DetectAcid()
		if err != nil {
			if errors.Is(err, srun.ErrAcidCannotFound) {
				logger.Warnln("找不到 acid，使用配置 acid")
			} else {
				logger.Warnf("嗅探失败，使用配置 acid: %v", err)
			}
		} else {
			logger.Debugf("使用嗅探 acid: %s", acid)
			srunClient.LoginInfo.Meta.Acid = acid
		}
	}

	if flags.AutoEnc {
		logger := logger.WithField(keys.LogLoginModule, "enc")

		logger.Debugln("开始嗅探")
		enc, err := srunDetector.DetectEnc()
		if err != nil {
			if errors.Is(err, srun.ErrEnvCannotFound) {
				logger.Warnln("找不到 enc，使用配置 enc")
			} else {
				logger.Warnf("嗅探失败，使用配置 enc: %v", err)
			}
		} else {
			logger.Debugf("使用嗅探 enc: %s", enc)
			srunClient.LoginInfo.Meta.Enc = enc
		}
	}

	// 选择输出函数
	var _Println func(args ...interface{})
	if conf.IsOnlineDetectLogDebugLevel {
		_Println = logger.Debugln
	} else {
		_Println = logger.Infoln
	}

	_Println("正在获取登录状态")

	online, ip, err := srunClient.LoginStatus()
	if err != nil {
		return err
	}

	var loginIp string

	if config.Meta.DoubleStack {
		logger.Debugln("使用双栈网络时认证 ip 为空")
	} else {
		loginIp = ip
		logger.Debugln("认证客户端 ip: ", ip)
	}

	// 登录执行

	if online {
		_Println("已登录~")

		if config.Settings.DDNS.Enable && ipLast != ip {
			if ddns(logger, ip, httpClient) == nil {
				ipLast = ip
			}
		}

		return nil
	} else {
		logger.Infoln("检测到用户未登录，开始尝试登录...")

		if err = srunClient.DoLogin(loginIp); err != nil {
			return err
		}

		logger.Infoln("登录成功~")

		if config.Settings.DDNS.Enable {
			_ = ddns(logger, ip, httpClient)
		}
	}

	return nil
}
