package messages

import (
	"fmt"
	"time"

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

// Document returns document object if message has a document attachment (video, voice, audio,
// basically every type except photo).
func (e Elem) Document() (*tg.Document, bool) {
	msg, ok := e.Msg.(*tg.Message)
	if !ok {
		return nil, false
	}

	media, ok := msg.Media.(*tg.MessageMediaDocument)
	if !ok {
		return nil, false
	}

	return media.Document.AsNotEmpty()
}

// Photo returns photo object if message has a photo attachment.
func (e Elem) Photo() (*tg.Photo, bool) {
	msg, ok := e.Msg.(*tg.Message)
	if !ok {
		return nil, false
	}

	media, ok := msg.Media.(*tg.MessageMediaPhoto)
	if !ok {
		return nil, false
	}

	return media.Photo.AsNotEmpty()
}

// File represents file attachment.
type File struct {
	Name     string
	MIMEType string
	Location tg.InputFileLocationClass
}

const dateLayout = "2006-01-02_15-04-05"

func getDocFilename(doc *tg.Document) string {
	var filename, ext string
	for _, attr := range doc.Attributes {
		switch v := attr.(type) {
		case *tg.DocumentAttributeImageSize:
			switch doc.MimeType {
			case "image/png":
				ext = ".png"
			case "image/webp":
				ext = ".webp"
			case "image/tiff":
				ext = ".tif"
			default:
				ext = ".jpg"
			}
		case *tg.DocumentAttributeAnimated:
			ext = ".gif"
		case *tg.DocumentAttributeSticker:
			ext = ".webp"
		case *tg.DocumentAttributeVideo:
			switch doc.MimeType {
			case "video/mpeg":
				ext = ".mpeg"
			case "video/webm":
				ext = ".webm"
			case "video/ogg":
				ext = ".ogg"
			default:
				ext = ".mp4"
			}
		case *tg.DocumentAttributeAudio:
			switch doc.MimeType {
			case "audio/webm":
				ext = ".webm"
			case "audio/aac":
				ext = ".aac"
			case "audio/ogg":
				ext = ".ogg"
			default:
				ext = ".mp3"
			}
		case *tg.DocumentAttributeFilename:
			filename = v.FileName
		}
	}

	if filename == "" {
		filename = fmt.Sprintf(
			"doc%d_%s%s", doc.GetID(),
			time.Unix(int64(doc.Date), 0).Format(dateLayout),
			ext,
		)
	}

	return filename
}

type sizedPhoto interface {
	GetW() int
	GetH() int
	GetType() string
}

var (
	_ sizedPhoto = (*tg.PhotoSize)(nil)
	_ sizedPhoto = (*tg.PhotoCachedSize)(nil)
	_ sizedPhoto = (*tg.PhotoSizeProgressive)(nil)
)

// File returns file location if message has a file attachment.
func (e Elem) File() (File, bool) {
	msg, ok := e.Msg.(*tg.Message)
	if !ok {
		return File{}, false
	}

	switch media := msg.Media.(type) {
	case *tg.MessageMediaPhoto:
		photo, ok := media.Photo.AsNotEmpty()
		if !ok {
			return File{}, false
		}

		filename := fmt.Sprintf(
			"photo%d_%s.jpg", photo.GetID(),
			time.Unix(int64(photo.Date), 0).Format(dateLayout),
		)

		var (
			thumbSize  string
			maxW, maxH int
		)
		for _, g := range photo.Sizes {
			// TODO(tdakkota): add helpers to choose photo size.
			if sz, ok := g.(sizedPhoto); ok && maxW < sz.GetW() && maxH < sz.GetH() {
				thumbSize = sz.GetType()
			}
		}

		if thumbSize == "" {
			return File{}, false
		}

		return File{
			Name:     filename,
			MIMEType: "image/jpeg",
			Location: &tg.InputPhotoFileLocation{
				ID:            photo.ID,
				AccessHash:    photo.AccessHash,
				FileReference: photo.FileReference,
				ThumbSize:     thumbSize,
			},
		}, true
	case *tg.MessageMediaDocument:
		doc, ok := media.Document.AsNotEmpty()
		if !ok {
			return File{}, false
		}

		return File{
			Name:     getDocFilename(doc),
			MIMEType: doc.MimeType,
			Location: doc.AsInputDocumentFileLocation(),
		}, true
	default:
		return File{}, false
	}
}
