package loadsim

import (
	"errors"
	"flag"
	"fmt"
	"strings"
	"time"
)

type Protocol string

const (
	ProtocolGB32960    Protocol = "gb32960"
	ProtocolJT808      Protocol = "jt808"
	ProtocolYutongMQTT Protocol = "yutong-mqtt"

	DefaultJT808PhoneBase int64 = 139000000000
	maxJT808Phone         int64 = 999999999999
)

type Config struct {
	Protocol             Protocol
	Addr                 string
	Connections          int
	ConnectRatePerSecond int
	SendInterval         time.Duration
	Duration             time.Duration
	Template             string
	SendFrames           bool
	JT808PhoneBase       int64
	DrainResponses       bool
}

type FlagConfig struct {
	protocol       string
	addr           string
	connections    int
	connectRate    int
	sendInterval   time.Duration
	duration       time.Duration
	template       string
	sendFrames     bool
	jt808PhoneBase int64
	drainResponses bool
}

func RegisterFlags(fs *flag.FlagSet) *FlagConfig {
	cfg := &FlagConfig{}
	fs.StringVar(&cfg.protocol, "protocol", string(ProtocolJT808), "protocol: jt808, gb32960, yutong-mqtt")
	fs.StringVar(&cfg.addr, "addr", "", "target host:port")
	fs.IntVar(&cfg.connections, "connections", 1, "target concurrent connections")
	fs.IntVar(&cfg.connectRate, "connect-rate", 100, "new connections per second")
	fs.DurationVar(&cfg.sendInterval, "send-interval", 10*time.Second, "frame send interval per connection")
	fs.DurationVar(&cfg.duration, "duration", 10*time.Minute, "load test duration")
	fs.StringVar(&cfg.template, "template", "", "frame template name")
	fs.BoolVar(&cfg.sendFrames, "send", true, "send protocol frames while connections are open")
	fs.Int64Var(&cfg.jt808PhoneBase, "jt808-phone-base", DefaultJT808PhoneBase, "first synthetic JT808 phone; one consecutive phone is assigned per connection")
	fs.BoolVar(&cfg.drainResponses, "drain-responses", true, "continuously read protocol responses while frames are being sent")
	return cfg
}

func (c *FlagConfig) Build() (Config, error) {
	protocol := Protocol(strings.ToLower(strings.TrimSpace(c.protocol)))
	switch protocol {
	case ProtocolGB32960, ProtocolJT808, ProtocolYutongMQTT:
	default:
		return Config{}, fmt.Errorf("unsupported protocol %q", c.protocol)
	}
	if strings.TrimSpace(c.addr) == "" {
		return Config{}, errors.New("addr is required")
	}
	if c.connections <= 0 {
		return Config{}, errors.New("connections must be positive")
	}
	if c.connectRate <= 0 {
		return Config{}, errors.New("connect-rate must be positive")
	}
	if c.sendInterval <= 0 {
		return Config{}, errors.New("send-interval must be positive")
	}
	if c.duration <= 0 {
		return Config{}, errors.New("duration must be positive")
	}
	if protocol == ProtocolJT808 {
		if c.jt808PhoneBase <= 0 || c.jt808PhoneBase > maxJT808Phone {
			return Config{}, errors.New("jt808-phone-base must be a positive 12-digit-or-shorter number")
		}
		if int64(c.connections-1) > maxJT808Phone-c.jt808PhoneBase {
			return Config{}, errors.New("jt808 synthetic phone range exceeds 12 digits")
		}
	}
	return Config{
		Protocol:             protocol,
		Addr:                 strings.TrimSpace(c.addr),
		Connections:          c.connections,
		ConnectRatePerSecond: c.connectRate,
		SendInterval:         c.sendInterval,
		Duration:             c.duration,
		Template:             strings.TrimSpace(c.template),
		SendFrames:           c.sendFrames,
		JT808PhoneBase:       c.jt808PhoneBase,
		DrainResponses:       c.drainResponses,
	}, nil
}
