package s3

import (
	"context"
	"github.com/aws/aws-sdk-go-v2/aws"
	v4 "github.com/aws/aws-sdk-go-v2/aws/signer/v4"
	"github.com/aws/aws-sdk-go-v2/service/s3"
	"github.com/labstack/gommon/log"
	"io"
	"net/http"
	"time"
)

type ObjectInfo struct {
	Bucket      string `json:"bucket"`
	Key         string `json:"key"`
	ContentType string `json:"content_type"`
	Expire      int64  `json:"expire"`
}

func (c *Client) GetObjectInfo(ctx context.Context, bucket string, key string) (*ObjectInfo, error) {
	var (
		err   error
		input = &s3.HeadObjectInput{
			Bucket: aws.String(bucket),
			Key:    aws.String(key),
		}
		output *s3.HeadObjectOutput
	)

	if output, err = c.client.HeadObject(ctx, input); err != nil {
		return nil, err
	}

	return &ObjectInfo{
		Bucket:      bucket,
		Key:         key,
		ContentType: aws.ToString(output.ContentType),
		Expire:      aws.ToTime(output.Expires).UnixMilli(),
	}, nil
}

// GetObject
// https://docs.aws.amazon.com/AmazonS3/latest/userguide/example_s3_Scenario_PresignedUrl_section.html

type Presigner struct {
	PresignClient *s3.PresignClient
}

func (presigner *Presigner) GetObject(ctx context.Context, bucketName string, objectKey string, lifetimeSecs int64) (*v4.PresignedHTTPRequest, error) {
	request, err := presigner.PresignClient.PresignGetObject(ctx, &s3.GetObjectInput{
		Bucket: aws.String(bucketName),
		Key:    aws.String(objectKey),
	}, func(opts *s3.PresignOptions) {
		opts.Expires = time.Duration(lifetimeSecs * int64(time.Second))
	})
	if err != nil {
		log.Error("Presigner: couldn't get a presigned request to get %v:%v. Here's why: %v\n",
			bucketName, objectKey, err)
	}
	return request, err
}

type ObjectEntry struct {
	URL    string      `json:"url"`
	Method string      `json:"method"`
	Header http.Header `json:"header"`
}

func (c *Client) GetObjectEntry(ctx context.Context, bucket string, key string, lifetimes ...int64) (*ObjectEntry, error) {
	var (
		err      error
		lifetime int64 = 5 * 60
		pc             = &Presigner{PresignClient: s3.NewPresignClient(c.client)}
		output   *v4.PresignedHTTPRequest
	)

	if len(lifetimes) > 0 && lifetimes[0] > 0 {
		lifetime = lifetimes[0]
	}

	if output, err = pc.GetObject(ctx, bucket, key, lifetime); err != nil {
		return nil, err
	}

	return &ObjectEntry{
		URL:    output.URL,
		Method: output.Method,
		Header: output.SignedHeader,
	}, nil
}

type ObjectEntity struct {
	ObjectInfo
	Body io.ReadCloser `json:"body"`
}

func (c *Client) GetObject(ctx context.Context, bucket string, key string) (*ObjectEntity, error) {
	var (
		err   error
		input = &s3.GetObjectInput{
			Bucket: aws.String(bucket),
			Key:    aws.String(key),
		}
		output *s3.GetObjectOutput
	)

	if output, err = c.client.GetObject(ctx, input); err != nil {
		return nil, err
	}

	return &ObjectEntity{
		ObjectInfo: ObjectInfo{
			Bucket:      bucket,
			Key:         key,
			ContentType: aws.ToString(output.ContentType),
			Expire:      aws.ToTime(output.Expires).UnixMilli(),
		},
		Body: output.Body,
	}, nil
}