package fileid

import (
	"github.com/go-faster/errors"

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

// PhotoSizeSource represents photo metadata stored in file_id.
type PhotoSizeSource struct {
	Type      PhotoSizeSourceType
	VolumeID  int64
	LocalID   int
	Secret    int64
	PhotoSize string

	FileType      Type
	ThumbnailType rune

	DialogID         constant.TDLibPeerID
	DialogAccessHash int64

	StickerSetID         int64
	StickerSetAccessHash int64
	StickerVersion       int32
}

func (p *PhotoSizeSource) stickerSet() tg.InputStickerSetClass {
	return &tg.InputStickerSetID{
		ID:         p.StickerSetID,
		AccessHash: p.StickerSetAccessHash,
	}
}

func (p *PhotoSizeSource) dialogPeer() tg.InputPeerClass {
	switch id := p.DialogID; {
	case id.IsUser():
		return &tg.InputPeerUser{
			UserID:     id.ToPlain(),
			AccessHash: p.DialogAccessHash,
		}
	case id.IsChat():
		return &tg.InputPeerChat{
			ChatID: id.ToPlain(),
		}
	case id.IsChannel():
		return &tg.InputPeerChannel{
			ChannelID:  id.ToPlain(),
			AccessHash: p.DialogAccessHash,
		}
	}
	return &tg.InputPeerEmpty{}
}

func (p *PhotoSizeSource) readLocalIDVolumeID(b *bin.Buffer) error {
	{
		v, err := b.Long()
		if err != nil {
			return errors.Wrap(err, "read volume_id")
		}
		p.VolumeID = v
	}
	{
		v, err := b.Int()
		if err != nil {
			return errors.Wrap(err, "read local_id")
		}
		p.LocalID = v
	}
	return nil
}

func (p *PhotoSizeSource) readDialog(b *bin.Buffer) error {
	{
		v, err := b.Long()
		if err != nil {
			return errors.Wrap(err, "read dialog_id")
		}
		p.DialogID = constant.TDLibPeerID(v)
	}
	{
		v, err := b.Long()
		if err != nil {
			return errors.Wrap(err, "read dialog_access_hash")
		}
		p.DialogAccessHash = v
	}
	return nil
}

func (p *PhotoSizeSource) readStickerSet(b *bin.Buffer) error {
	{
		v, err := b.Long()
		if err != nil {
			return errors.Wrap(err, "read sticker_set_id")
		}
		p.StickerSetID = v
	}
	{
		v, err := b.Long()
		if err != nil {
			return errors.Wrap(err, "read sticker_set_access_hash")
		}
		p.StickerSetAccessHash = v
	}
	return nil
}

const latestSubVersion = 34

func (p *PhotoSizeSource) decode(b *bin.Buffer, subVersion byte) error {
	if subVersion < 32 {
		{
			v, err := b.Long()
			if err != nil {
				return errors.Wrap(err, "read volume_id")
			}
			p.VolumeID = v
		}

		if subVersion < 22 {
			{
				v, err := b.Long()
				if err != nil {
					return errors.Wrap(err, "read secret")
				}
				p.Secret = v
			}
			{
				v, err := b.Int()
				if err != nil {
					return errors.Wrap(err, "read local_id")
				}
				p.LocalID = v
			}

			return nil
		}
	}

	var photoSizeType PhotoSizeSourceType
	if subVersion >= 4 {
		v, err := b.Int()
		if err != nil {
			return errors.Wrap(err, "read photo_size_type")
		}
		photoSizeType = PhotoSizeSourceType(v)
	}
	if photoSizeType < 0 || photoSizeType >= lastPhotoSizeSourceType {
		return errors.Errorf("unknown photo size source type %d", photoSizeType)
	}
	p.Type = photoSizeType

	switch photoSizeType {
	case PhotoSizeSourceLegacy:
		v, err := b.Long()
		if err != nil {
			return errors.Wrap(err, "read secret")
		}
		p.Secret = v
	case PhotoSizeSourceThumbnail:
		{
			v, err := b.Uint32()
			if err != nil {
				return errors.Wrap(err, "read file_type")
			}
			p.FileType = Type(v)
		}
		{
			v, err := b.Int32()
			if err != nil {
				return errors.Wrap(err, "read thumbnail_type")
			}
			p.ThumbnailType = v
		}
	case PhotoSizeSourceDialogPhotoBig, PhotoSizeSourceDialogPhotoSmall:
		if err := p.readDialog(b); err != nil {
			return errors.Wrap(err, "read dialog")
		}
	case PhotoSizeSourceStickerSetThumbnail:
		if err := p.readStickerSet(b); err != nil {
			return errors.Wrap(err, "read sticker_set")
		}
	case PhotoSizeSourceFullLegacy:
		{
			v, err := b.Long()
			if err != nil {
				return errors.Wrap(err, "read volume_id")
			}
			p.VolumeID = v
		}
		{
			v, err := b.Long()
			if err != nil {
				return errors.Wrap(err, "read secret")
			}
			p.Secret = v
		}
		{
			v, err := b.Int()
			if err != nil {
				return errors.Wrap(err, "read local_id")
			}
			p.LocalID = v
		}
	case PhotoSizeSourceDialogPhotoBigLegacy, PhotoSizeSourceDialogPhotoSmallLegacy:
		if err := p.readDialog(b); err != nil {
			return errors.Wrap(err, "read dialog")
		}
		if err := p.readLocalIDVolumeID(b); err != nil {
			return errors.Wrap(err, "read legacy photo")
		}

	case PhotoSizeSourceStickerSetThumbnailLegacy:
		if err := p.readStickerSet(b); err != nil {
			return errors.Wrap(err, "read sticker_set")
		}
		if err := p.readLocalIDVolumeID(b); err != nil {
			return errors.Wrap(err, "read legacy photo")
		}

	case PhotoSizeSourceStickerSetThumbnailVersion:
		if err := p.readStickerSet(b); err != nil {
			return errors.Wrap(err, "read sticker_set")
		}
		{
			v, err := b.Int32()
			if err != nil {
				return errors.Wrap(err, "read sticker_version")
			}
			p.StickerVersion = v
		}
	}

	if subVersion < 32 && subVersion >= 22 {
		v, err := b.Int()
		if err != nil {
			return errors.Wrap(err, "read local_id")
		}
		p.LocalID = v
	}
	return nil
}

func (p *PhotoSizeSource) writeLocalIDVolumeID(b *bin.Buffer) {
	b.PutLong(p.VolumeID)
	b.PutInt(p.LocalID)
}

func (p *PhotoSizeSource) writeDialog(b *bin.Buffer) {
	b.PutLong(int64(p.DialogID))
	b.PutLong(p.DialogAccessHash)
}

func (p *PhotoSizeSource) writeStickerSet(b *bin.Buffer) {
	b.PutLong(p.StickerSetID)
	b.PutLong(p.StickerSetAccessHash)
}

func (p *PhotoSizeSource) encode(b *bin.Buffer) {
	b.PutInt(int(p.Type))
	switch p.Type {
	case PhotoSizeSourceLegacy:
		b.PutLong(p.Secret)
	case PhotoSizeSourceThumbnail:
		b.PutUint32(uint32(p.FileType))
		b.PutInt32(p.ThumbnailType)
	case PhotoSizeSourceDialogPhotoBig, PhotoSizeSourceDialogPhotoSmall:
		p.writeDialog(b)
	case PhotoSizeSourceStickerSetThumbnail:
		p.writeStickerSet(b)
	case PhotoSizeSourceFullLegacy:
		b.PutLong(p.VolumeID)
		b.PutLong(p.Secret)
		b.PutInt(p.LocalID)
	case PhotoSizeSourceDialogPhotoBigLegacy, PhotoSizeSourceDialogPhotoSmallLegacy:
		p.writeDialog(b)
		p.writeLocalIDVolumeID(b)
	case PhotoSizeSourceStickerSetThumbnailLegacy:
		p.writeStickerSet(b)
		p.writeLocalIDVolumeID(b)
	case PhotoSizeSourceStickerSetThumbnailVersion:
		p.writeStickerSet(b)
		b.PutInt32(p.StickerVersion)
	}
}
