//go:build debug || dev || sdk

package commons

import (
	"agent/api"
	"agent/client"
	"agent/commons/app"
	"agent/commons/debug"
	"agent/commons/logger"
	"agent/commons/utils"
	"agent/commons/utime"
	"agent/defines/derrs"
	"context"
	"fmt"
	"os"
	"path/filepath"
	"time"
)

type UserClientArgs struct {
	Login string
	Passw string

	ServerSchema string
	ServerHost   string
	ServerPort   int
	ClientKey    string
	ClientSchema string

	HandshakeValue string
	HandshakeData  string

	LogFile string
}

func NewUserClient(args UserClientArgs) (ctx context.Context, cl client.IClient, err error) {
	if args.LogFile != "" {
		var logOpts []logger.Option
		if utils.FileIsExist(args.LogFile) {
			// При каждом запуске лог-файл очищается. Если запуск происходит не из VSCode, то делаем ротацию лог-файла,
			// чтобы сохранять предыдущие логи.
			if !utils.FileIsExist("./main.go") {
				logDir := filepath.Dir(args.LogFile)
				filename := utils.FileName(args.LogFile)
				ext := filepath.Ext(args.LogFile)
				date := time.Now().Format("2006-01-02_15-04-05")
				utils.FileCopy(fmt.Sprintf("%s/%s_%s%s", logDir, filename, date, ext), args.LogFile)
			}
			// Очищаем лог-файл при каждом запуске
			logOpts = append(logOpts, logger.WithOpenFlags(os.O_CREATE|os.O_WRONLY|os.O_TRUNC))
		}

		l, err := logger.New(args.LogFile, logOpts...)
		if err != nil {
			fmt.Printf("failed to initialize logger: %v\n", err)
			os.Exit(1)
		}

		debug.SetLogger(l)
		debug.Logf("Using log file: \"%s\"\n", args.LogFile)
	}

	config := NewUserConfig(args)

	opts := app.AppOpts{
		Login:              userLogin,
		Ping:               userPing,
		DownloadConfig:     userDownloadConfig,
		CheckServiceHealth: userCheckServiceHealth,
	}

	ctx, err = app.New(config, opts)
	if err != nil {
		return nil, nil, err
	}
	cl = client.ExtractClient(ctx)

	if len(args.Login) == 0 || len(args.Passw) == 0 {
		return ctx, cl, derrs.NewIncorrectParamsError(`login and password must not be empty`)
	}

	if err = cl.Login(ctx, DefaultUserStartupInfo(args.Login, args.Passw)); err != nil {
		return nil, nil, err
	}

	return ctx, cl, nil
}

func DefaultUserStartupInfo(login string, passw string) map[string]any {
	return map[string]any{
		"login": login,
		"passw": passw,
	}
}

func userLogin(ctx context.Context, startupInfo map[string]any) (res client.LoginResult, err error) {
	cl := client.ExtractClient(ctx)

	loginResult, err := api.UserLogin(ctx, cl, startupInfo)
	if err != nil {
		return res, err
	}

	res.Config = nil
	res.Token.Value = loginResult.Token.Value
	res.SrvTime = loginResult.SrvTime

	return res, nil
}

func userPing(ctx context.Context) (srvTime int64, err error) {
	cl := client.ExtractClient(ctx)
	res, err := api.UserPing(ctx, cl, utime.NanoNow())
	if err != nil {
		return 0, err
	}
	return res.Utime, nil
}

func userDownloadConfig(ctx context.Context) (res map[string]any, err error) {
	cl := client.ExtractClient(ctx)
	return api.UserConfig(ctx, cl)
}

func userCheckServiceHealth(ctx context.Context) error {
	cl := client.ExtractClient(ctx)
	return api.ServiceCheckHealth(ctx, cl, true)
}
