package thumb

import (
	"bytes"
	"context"
	"fmt"
	model "github.com/cloudreve/Cloudreve/v3/models"
	"github.com/cloudreve/Cloudreve/v3/pkg/util"
	"github.com/gofrs/uuid"
	"io"
	"os"
	"os/exec"
	"path/filepath"
	"strings"
)

func init() {
	RegisterGenerator(&LibreOfficeGenerator{})
}

type LibreOfficeGenerator struct {
	exts        []string
	lastRawExts string
}

func (l *LibreOfficeGenerator) Generate(ctx context.Context, file io.Reader, src string, name string, options map[string]string) (*Result, error) {
	sofficeOpts := model.GetSettingByNames("thumb_libreoffice_path", "thumb_libreoffice_exts", "thumb_encode_method", "temp_path")

	if l.lastRawExts != sofficeOpts["thumb_libreoffice_exts"] {
		l.exts = strings.Split(sofficeOpts["thumb_libreoffice_exts"], ",")
	}

	if !util.IsInExtensionList(l.exts, name) {
		return nil, fmt.Errorf("unsupported document format: %w", ErrPassThrough)
	}

	tempOutputPath := filepath.Join(
		util.RelativePath(sofficeOpts["temp_path"]),
		"thumb",
		fmt.Sprintf("soffice_%s", uuid.Must(uuid.NewV4()).String()),
	)

	tempInputPath := src
	if tempInputPath == "" {
		// If not local policy files, download to temp folder
		tempInputPath = filepath.Join(
			util.RelativePath(sofficeOpts["temp_path"]),
			"thumb",
			fmt.Sprintf("soffice_%s%s", uuid.Must(uuid.NewV4()).String(), filepath.Ext(name)),
		)

		// Due to limitations of ffmpeg, we need to write the input file to disk first
		tempInputFile, err := util.CreatNestedFile(tempInputPath)
		if err != nil {
			return nil, fmt.Errorf("failed to create temp file: %w", err)
		}

		defer os.Remove(tempInputPath)
		defer tempInputFile.Close()

		if _, err = io.Copy(tempInputFile, file); err != nil {
			return nil, fmt.Errorf("failed to write input file: %w", err)
		}

		tempInputFile.Close()
	}

	// Convert the document to an image
	cmd := exec.CommandContext(ctx, sofficeOpts["thumb_libreoffice_path"], "--headless",
		"-nologo", "--nofirststartwizard", "--invisible", "--norestore", "--convert-to",
		sofficeOpts["thumb_encode_method"], "--outdir", tempOutputPath, tempInputPath)

	// Redirect IO
	var stdErr bytes.Buffer
	cmd.Stdin = file
	cmd.Stderr = &stdErr

	if err := cmd.Run(); err != nil {
		util.Log().Warning("Failed to invoke LibreOffice: %s", stdErr.String())
		return nil, fmt.Errorf("failed to invoke LibreOffice: %w", err)
	}

	return &Result{
		Path: filepath.Join(
			tempOutputPath,
			strings.TrimSuffix(filepath.Base(tempInputPath), filepath.Ext(tempInputPath))+"."+sofficeOpts["thumb_encode_method"],
		),
		Continue: true,
		Cleanup:  []func(){func() { _ = os.RemoveAll(tempOutputPath) }},
	}, nil
}

func (l *LibreOfficeGenerator) Priority() int {
	return 50
}

func (l *LibreOfficeGenerator) EnableFlag() string {
	return "thumb_libreoffice_enabled"
}
