// The usual utils files with some functions
// I do plan to change the markdownToHTML and addCopyButtonsToCode
// I will take example on openai and gemini and put a header on top of a code part with the button instead of inside
package main

import (
	"bytes"
	"fmt"
	"regexp"

	"github.com/gofiber/fiber/v2"
	"github.com/yuin/goldmark"
	highlighting "github.com/yuin/goldmark-highlighting"
	"github.com/yuin/goldmark/parser"
	"github.com/yuin/goldmark/renderer/html"
)

var md goldmark.Markdown

func init() {
	md = goldmark.New(goldmark.WithExtensions(highlighting.NewHighlighting(highlighting.WithStyle("monokai"))),
		goldmark.WithParserOptions(
			parser.WithAutoHeadingID(),
		),
		goldmark.WithRendererOptions(
			html.WithHardWraps(),
			html.WithXHTML(),
		))
}

func markdownToHTML(markdownText string) string {
	var buf bytes.Buffer

	if err := md.Convert([]byte(markdownText), &buf); err != nil {
		fmt.Println("failed to convert markdown to HTML")
		panic(err)
	}

	return addCodeHeader(buf.String(), extractLanguages(markdownText))
}

func extractLanguages(markdownText string) []string {
	// Regular expression to match code blocks and capture the language identifier
	re := regexp.MustCompile("(?s)```([a-zA-Z0-9]*)\n.*?```")

	matches := re.FindAllStringSubmatch(markdownText, -1)
	languages := make([]string, 0, len(matches))

	for _, match := range matches {
		if len(match) > 1 {
			languages = append(languages, match[1])
		} else {
			languages = append(languages, "")
		}
	}

	return languages
}

func addCodeHeader(htmlContent string, languages []string) string {
	// Regular expression pattern to match <pre> elements
	pattern := `(<pre[^>]*>)`

	// Compile the regular expression
	re := regexp.MustCompile(pattern)

	// Replace each matched <pre> element with the updated HTML
	updatedHTML := re.ReplaceAllStringFunc(htmlContent, func(match string) string {
		var language string
		if len(languages) > 0 {
			language = languages[0]
			languages = languages[1:]
		}
		headerHTML := fmt.Sprintf(`<div class="code-header"><span>%s</span><button class="copy-button" onclick="copyToClipboardCode(this)"><i class="fa-solid fa-copy"></i></button></div>`, language)
		return headerHTML + match
	})

	return updatedHTML
}

func getExistingKeys(c *fiber.Ctx) (bool, bool, bool, bool, bool, bool, bool, bool) {
	if edgeGlobalClient.WithGlobals(map[string]interface{}{"ext::auth::client_token": c.Cookies("jade-edgedb-auth-token")}) == nil {
		return false, false, false, false, false, false, false, false
	}

	var (
		openaiExists     bool
		anthropicExists  bool
		mistralExists    bool
		groqExists       bool
		gooseaiExists    bool
		googleExists     bool
		perplexityExists bool
		fireworksExists  bool
	)

	err := edgeGlobalClient.WithGlobals(map[string]interface{}{"ext::auth::client_token": c.Cookies("jade-edgedb-auth-token")}).QuerySingle(edgeCtx, `
	select exists (
		select global currentUser.setting.keys
		filter .company.name = "openai"
	  );
	`, &openaiExists)
	if err != nil {
		fmt.Println("Error checking if OpenAI key exists")
		panic(err)
	}

	err = edgeGlobalClient.WithGlobals(map[string]interface{}{"ext::auth::client_token": c.Cookies("jade-edgedb-auth-token")}).QuerySingle(edgeCtx, `
	select exists (
		select global currentUser.setting.keys
		filter .company.name = "anthropic"
	);
	`, &anthropicExists)
	if err != nil {
		fmt.Println("Error checking if Anthropic key exists")
		panic(err)
	}

	err = edgeGlobalClient.WithGlobals(map[string]interface{}{"ext::auth::client_token": c.Cookies("jade-edgedb-auth-token")}).QuerySingle(edgeCtx, `
	select exists (
		select global currentUser.setting.keys
		filter .company.name = "mistral"
	);
	`, &mistralExists)
	if err != nil {
		fmt.Println("Error checking if Mistral key exists")
		panic(err)
	}

	err = edgeGlobalClient.WithGlobals(map[string]interface{}{"ext::auth::client_token": c.Cookies("jade-edgedb-auth-token")}).QuerySingle(edgeCtx, `
	select exists (
		select global currentUser.setting.keys
		filter .company.name = "groq"
	);
	`, &groqExists)
	if err != nil {
		fmt.Println("Error checking if Groq key exists")
		panic(err)
	}

	err = edgeGlobalClient.WithGlobals(map[string]interface{}{"ext::auth::client_token": c.Cookies("jade-edgedb-auth-token")}).QuerySingle(edgeCtx, `
	select exists (
		select global currentUser.setting.keys
		filter .company.name = "gooseai"
	);
	`, &gooseaiExists)
	if err != nil {
		fmt.Println("Error checking if GooseAI key exists")
		panic(err)
	}

	err = edgeGlobalClient.WithGlobals(map[string]interface{}{"ext::auth::client_token": c.Cookies("jade-edgedb-auth-token")}).QuerySingle(edgeCtx, `
	select exists (
		select global currentUser.setting.keys
		filter .company.name = "google"
	);
	`, &googleExists)
	if err != nil {
		fmt.Println("Error checking if Google key exists")
		panic(err)
	}

	err = edgeGlobalClient.WithGlobals(map[string]interface{}{"ext::auth::client_token": c.Cookies("jade-edgedb-auth-token")}).QuerySingle(edgeCtx, `
	select exists (
		select global currentUser.setting.keys
		filter .company.name = "perplexity"
	);
	`, &perplexityExists)
	if err != nil {
		fmt.Println("Error checking if Perplexity key exists")
		panic(err)
	}

	err = edgeGlobalClient.WithGlobals(map[string]interface{}{"ext::auth::client_token": c.Cookies("jade-edgedb-auth-token")}).QuerySingle(edgeCtx, `
	select exists (
		select global currentUser.setting.keys
		filter .company.name = "fireworks"
	);
	`, &fireworksExists)
	if err != nil {
		fmt.Println("Error checking if Fireworks key exists")
		panic(err)
	}

	return openaiExists, anthropicExists, mistralExists, groqExists, gooseaiExists, googleExists, perplexityExists, fireworksExists
}

func Message2RequestMessage(messages []Message, context string) []RequestMessage {
	// Add context if it exists
	if context != "" {
		m := make([]RequestMessage, len(messages)+1)
		m[0] = RequestMessage{
			Role:    "system",
			Content: context,
		}

		for i, msg := range messages {
			var role string
			switch msg.Role {
			case "user":
				role = "user"
			case "bot":
				role = "assistant"
			default:
				role = "system"
			}
			m[i+1] = RequestMessage{
				Role:    role,
				Content: msg.Content,
			}
		}
		return m
	} else {
		m := make([]RequestMessage, len(messages))

		for i, msg := range messages {
			var role string
			switch msg.Role {
			case "user":
				role = "user"
			case "bot":
				role = "assistant"
			default:
				role = "system"
			}
			m[i] = RequestMessage{
				Role:    role,
				Content: msg.Content,
			}
		}
		return m
	}
}
