package tdesktop

import (
	"fmt"

	"github.com/go-faster/errors"

	"go.mau.fi/mautrix-telegram/pkg/gotd/bin"
)

// MTPDCOption is a Telegram Desktop storage structure which stores DC info.
//
// See https://github.com/telegramdesktop/tdesktop/blob/v2.9.8/Telegram/SourceFiles/mtproto/mtproto_dc_options.h.
type MTPDCOption struct {
	ID     int32
	Flags  bin.Fields
	Port   int32
	IP     string
	Secret []byte
}

// IPv6 denotes that the specified IP is an IPv6 address.
func (m MTPDCOption) IPv6() bool {
	return m.Flags.Has(0)
}

// MediaOnly denotes that this DC should only be used to download or upload files.
func (m MTPDCOption) MediaOnly() bool {
	return m.Flags.Has(1)
}

// TCPOOnly denotes that this DC only supports connection with transport obfuscation.
func (m MTPDCOption) TCPOOnly() bool {
	return m.Flags.Has(2)
}

// CDN denotes that this is a CDN DC.
func (m MTPDCOption) CDN() bool {
	return m.Flags.Has(3)
}

// Static denotes that this IP should be used when connecting through a proxy.
func (m MTPDCOption) Static() bool {
	return m.Flags.Has(4)
}

func (m *MTPDCOption) deserialize(r *qtReader, version int32) error {
	id, err := r.readInt32()
	if err != nil {
		return errors.Wrap(err, "read id")
	}
	m.ID = id

	flags, err := r.readUint32()
	if err != nil {
		return errors.Wrap(err, "read flags")
	}
	m.Flags = bin.Fields(flags)

	port, err := r.readInt32()
	if err != nil {
		return errors.Wrap(err, "read port")
	}
	m.Port = port

	const maxIPSize = 45
	ip, err := r.readString()
	if err != nil {
		return errors.Wrap(err, "read ip")
	}
	if l := len(ip); l > maxIPSize {
		return errors.Errorf("too big IP string (%d > %d)", l, maxIPSize)
	}
	m.IP = ip

	if version > 0 {
		const maxSecretSize = 32
		secret, err := r.readBytes()
		if err != nil {
			return errors.Wrap(err, "read secret")
		}
		if l := len(secret); l > maxSecretSize {
			return errors.Errorf("too big DC secret (%d > %d)", l, maxSecretSize)
		}
		m.Secret = secret
	}

	return nil
}

// MTPDCOptions is a Telegram Desktop storage structure which stores DCs info.
//
// See https://github.com/telegramdesktop/tdesktop/blob/v2.9.8/Telegram/SourceFiles/mtproto/mtproto_dc_options.cpp#L479.
type MTPDCOptions struct {
	Options []MTPDCOption
}

func (m *MTPDCOptions) deserialize(r *qtReader) error {
	minusVersion, err := r.readInt32()
	if err != nil {
		return errors.Wrap(err, "read version")
	}

	var version int32
	if minusVersion < 0 {
		version = -minusVersion
	}

	var count int32
	if version > 0 {
		c, err := r.readInt32()
		if err != nil {
			return errors.Wrap(err, "read count")
		}
		count = c
	} else {
		count = minusVersion
	}

	for i := 0; i < int(count); i++ {
		var o MTPDCOption
		if err := o.deserialize(r, version); err != nil {
			return fmt.Errorf("read option %d: %w", i, err)
		}
		m.Options = append(m.Options, o)
	}

	// TODO(tdakkota): Read CDN keys.
	return nil
}
