package service

import (
	"context"
	"io"
	"mime"
	"path/filepath"
	"strings"

	"github.com/gofiber/fiber/v2"
	"github.com/minio/minio-go/v7"
	"github.com/rs/zerolog"

	minioStorage "web-qudo-be/config/config"
)

type CmsMediaService struct {
	Minio *minioStorage.MinioStorage
	Log   zerolog.Logger
}

func NewCmsMediaService(minio *minioStorage.MinioStorage, log zerolog.Logger) *CmsMediaService {
	return &CmsMediaService{Minio: minio, Log: log}
}

// Viewer streams a CMS object from MinIO (same idea as article-files viewer).
func (s *CmsMediaService) Viewer(c *fiber.Ctx) error {
	objectKey := strings.TrimSpace(c.Params("*"))
	objectKey = strings.TrimPrefix(objectKey, "/")
	if objectKey == "" {
		return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{
			"success":  false,
			"messages": []string{"object key required"},
		})
	}
	if !strings.HasPrefix(objectKey, "cms/") || strings.Contains(objectKey, "..") {
		return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{
			"success":  false,
			"messages": []string{"invalid object key"},
		})
	}

	ctx := context.Background()
	bucket := s.Minio.Cfg.ObjectStorage.MinioStorage.BucketName

	client, err := s.Minio.ConnectMinio()
	if err != nil {
		s.Log.Error().Err(err).Msg("cms media viewer: minio connect")
		return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{
			"success":  false,
			"messages": []string{"storage unavailable"},
		})
	}

	obj, err := client.GetObject(ctx, bucket, objectKey, minio.GetObjectOptions{})
	if err != nil {
		s.Log.Error().Err(err).Str("key", objectKey).Msg("cms media viewer: get object")
		return c.Status(fiber.StatusNotFound).SendString("not found")
	}
	defer obj.Close()

	ext := strings.ToLower(filepath.Ext(objectKey))
	contentType := mime.TypeByExtension(ext)
	if contentType == "" {
		contentType = "application/octet-stream"
	}
	c.Set("Content-Type", contentType)
	c.Set("Cache-Control", "public, max-age=86400")

	if _, err := io.Copy(c.Response().BodyWriter(), obj); err != nil {
		s.Log.Error().Err(err).Msg("cms media viewer: stream")
		return err
	}
	return nil
}
