// Copyright 2022 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT

package console

import (
	"bytes"
	"io"
	"path/filepath"
	"regexp"
	"strings"

	"forgejo.org/modules/markup"
	"forgejo.org/modules/setting"

	trend "github.com/buildkite/terminal-to-html/v3"
	"github.com/go-enry/go-enry/v2"
)

// MarkupName describes markup's name
var MarkupName = "console"

func init() {
	markup.RegisterRenderer(Renderer{})
}

// Renderer implements markup.Renderer
type Renderer struct{}

// Name implements markup.Renderer
func (Renderer) Name() string {
	return MarkupName
}

// Extensions implements markup.Renderer
func (Renderer) Extensions() []string {
	return []string{".sh-session"}
}

// SanitizerRules implements markup.Renderer
func (Renderer) SanitizerRules() []setting.MarkupSanitizerRule {
	return []setting.MarkupSanitizerRule{
		{Element: "span", AllowAttr: "class", Regexp: regexp.MustCompile(`^term-((fg[ix]?|bg)\d+|container)$`)},
	}
}

// CanRender implements markup.RendererContentDetector
func (Renderer) CanRender(filename string, input io.Reader) bool {
	buf, err := io.ReadAll(input)
	if err != nil {
		return false
	}
	if enry.GetLanguage(filepath.Base(filename), buf) != enry.OtherLanguage {
		return false
	}
	return bytes.ContainsRune(buf, '\x1b')
}

// Render renders terminal colors to HTML with all specific handling stuff.
func (Renderer) Render(ctx *markup.RenderContext, input io.Reader, output io.Writer) error {
	screen, err := trend.NewScreen()
	if err != nil {
		return err
	}
	if _, err := io.Copy(screen, input); err != nil {
		return err
	}
	buf := screen.AsHTML()
	buf = strings.ReplaceAll(buf, "\n", `<br>`)
	_, err = output.Write([]byte(buf))
	return err
}

// Render renders terminal colors to HTML with all specific handling stuff.
func Render(ctx *markup.RenderContext, input io.Reader, output io.Writer) error {
	if ctx.Type == "" {
		ctx.Type = MarkupName
	}
	return markup.Render(ctx, input, output)
}

// RenderString renders terminal colors in string to HTML with all specific handling stuff and return string
func RenderString(ctx *markup.RenderContext, content string) (string, error) {
	var buf strings.Builder
	if err := Render(ctx, strings.NewReader(content), &buf); err != nil {
		return "", err
	}
	return buf.String(), nil
}
