package thumbnails

import (
	"errors"
	"io"
	"strconv"

	"github.com/prometheus/client_golang/prometheus"
	"github.com/turt2live/matrix-media-repo/common"
	"github.com/turt2live/matrix-media-repo/common/rcontext"
	"github.com/turt2live/matrix-media-repo/database"
	"github.com/turt2live/matrix-media-repo/datastores"
	"github.com/turt2live/matrix-media-repo/metrics"
	"github.com/turt2live/matrix-media-repo/pipelines/_steps/datastore_op"
	"github.com/turt2live/matrix-media-repo/pipelines/_steps/download"
	"github.com/turt2live/matrix-media-repo/pool"
	"github.com/turt2live/matrix-media-repo/thumbnailing"
	"github.com/turt2live/matrix-media-repo/thumbnailing/m"
	"github.com/turt2live/matrix-media-repo/util"
)

type generateResult struct {
	i   *m.Thumbnail
	err error
}

func Generate(ctx rcontext.RequestContext, mediaRecord *database.DbMedia, width int, height int, method string, animated bool) (*database.DbThumbnail, io.ReadCloser, error) {
	ch := make(chan generateResult)
	defer close(ch)
	fn := func() {
		metric := metrics.ThumbnailsGenerated.With(prometheus.Labels{
			"width":    strconv.Itoa(width),
			"height":   strconv.Itoa(height),
			"method":   method,
			"animated": strconv.FormatBool(animated),
			"origin":   mediaRecord.Origin,
		})

		mediaStream, err := download.OpenStream(ctx, mediaRecord.Locatable)
		if err != nil {
			ch <- generateResult{err: err}
			return
		}
		fixedContentType := util.FixContentType(mediaRecord.ContentType)

		i, err := thumbnailing.GenerateThumbnail(mediaStream, fixedContentType, width, height, method, animated, ctx)
		if err != nil {
			if errors.Is(err, common.ErrMediaDimensionsTooSmall) {
				metric.Inc()
			}
			ch <- generateResult{err: err}
			return
		}

		metric.Inc()
		ch <- generateResult{i: i}
	}

	if err := pool.ThumbnailQueue.Schedule(fn); err != nil {
		return nil, nil, err
	}
	res := <-ch
	if res.err != nil {
		return nil, nil, res.err
	}
	if res.i == nil {
		// Couldn't generate a thumbnail
		return nil, nil, common.ErrMediaNotFound
	}

	// At this point, res.i is our thumbnail

	thumbMediaRecord, thumbStream, err := datastore_op.PutAndReturnStream(ctx, ctx.Request.Host, "", res.i.Reader, res.i.ContentType, "", datastores.ThumbnailsKind)
	if err != nil {
		return nil, nil, err
	}

	// Create a DbThumbnail
	newRecord := &database.DbThumbnail{
		Origin:      mediaRecord.Origin,
		MediaId:     mediaRecord.MediaId,
		ContentType: thumbMediaRecord.ContentType,
		Width:       width,
		Height:      height,
		Method:      method,
		Animated:    res.i.Animated,
		SizeBytes:   thumbMediaRecord.SizeBytes,
		CreationTs:  thumbMediaRecord.CreationTs,
		Locatable: &database.Locatable{
			Sha256Hash:  thumbMediaRecord.Sha256Hash,
			DatastoreId: thumbMediaRecord.DatastoreId,
			Location:    thumbMediaRecord.Location,
		},
	}
	err = database.GetInstance().Thumbnails.Prepare(ctx).Insert(newRecord)
	if err != nil {
		defer thumbStream.Close()
		return nil, nil, err
	}

	return newRecord, thumbStream, nil
}