package markdown

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

	chromahtml "github.com/alecthomas/chroma/v2/formatters/html"
	"github.com/yuin/goldmark"
	highlighting "github.com/yuin/goldmark-highlighting/v2"
	"github.com/yuin/goldmark/extension"
	"github.com/yuin/goldmark/parser"
	"github.com/yuin/goldmark/renderer/html"

	"gobsidian/internal/config"
)

type Renderer struct {
	md     goldmark.Markdown
	config *config.Config
}

func NewRenderer(cfg *config.Config) *Renderer {
	md := goldmark.New(
		goldmark.WithExtensions(
			extension.GFM,
			extension.Table,
			extension.Strikethrough,
			extension.TaskList,
			highlighting.NewHighlighting(
				highlighting.WithStyle("github-dark"),
				highlighting.WithFormatOptions(
					// Disable line numbers in code blocks for cleaner display
					chromahtml.WithLineNumbers(false),
					chromahtml.WithClasses(true),
				),
			),
		),
		goldmark.WithParserOptions(
			parser.WithAutoHeadingID(),
		),
		goldmark.WithRendererOptions(
			html.WithHardWraps(),
			html.WithXHTML(),
			html.WithUnsafe(),
		),
	)

	return &Renderer{
		md:     md,
		config: cfg,
	}
}

func (r *Renderer) RenderMarkdown(content string, notePath string) (string, error) {
	// Process Obsidian image syntax
	content = r.processObsidianImages(content, notePath)

	// Process Obsidian links
	content = r.processObsidianLinks(content)

	var buf bytes.Buffer
	if err := r.md.Convert([]byte(content), &buf); err != nil {
		return "", err
	}

	return buf.String(), nil
}

func (r *Renderer) processObsidianImages(content string, notePath string) string {
	// Regex to match Obsidian image syntax: ![[image.png]] or ![[folder/image.png]]
	obsidianImageRegex := regexp.MustCompile(`!\[\[([^\]]+\.(png|jpg|jpeg|gif|webp))\]\]`)

	return obsidianImageRegex.ReplaceAllStringFunc(content, func(match string) string {
		// Extract the image path from the match
		submatch := obsidianImageRegex.FindStringSubmatch(match)
		if len(submatch) < 2 {
			return match
		}

		imagePath := submatch[1]

		// Get storage info based on current note path
		// storageInfo := utils.GetImageStorageInfo(notePath, r.config)

		// Clean up the path
		cleanPath := filepath.Clean(imagePath)
		cleanPath = strings.ReplaceAll(cleanPath, "\\", "/")
		cleanPath = strings.Trim(cleanPath, "/")

		// Generate the appropriate URL based on storage mode
		var imageURL string

		switch r.config.ImageStorageMode {
		case 2: // Mode 2: Specific storage folder - just the filename
			imageURL = fmt.Sprintf("/serve_stored_image/%s", filepath.Base(cleanPath))
		default: // For modes 1, 3, and 4
			imageURL = fmt.Sprintf("/serve_attached_image/%s", cleanPath)
		}

		// Prefix with configured URL base
		if bp := r.config.URLPrefix; bp != "" {
			imageURL = bp + imageURL
		}

		// Convert to standard markdown image syntax
		alt := filepath.Base(imagePath)
		return fmt.Sprintf("![%s](%s)", alt, imageURL)
	})
}

func (r *Renderer) processObsidianLinks(content string) string {
	// Regex to match Obsidian wiki-links: [[Note Name]] or [[Note Name|Display Text]]
	obsidianLinkRegex := regexp.MustCompile(`\[\[([^\]|]+)(\|([^\]]+))?\]\]`)

	return obsidianLinkRegex.ReplaceAllStringFunc(content, func(match string) string {
		submatch := obsidianLinkRegex.FindStringSubmatch(match)
		if len(submatch) < 2 {
			return match
		}

		noteName := strings.TrimSpace(submatch[1])
		displayText := noteName

		// If there's custom display text (after |), use it
		if len(submatch) >= 4 && submatch[3] != "" {
			displayText = strings.TrimSpace(submatch[3])
		}

		// Convert note name to URL-friendly format
		noteURL := strings.ReplaceAll(noteName, " ", "%20")
		noteURL = fmt.Sprintf("/note/%s.md", noteURL)
		if bp := r.config.URLPrefix; bp != "" {
			noteURL = bp + noteURL
		}

		// Convert to standard markdown link syntax
		return fmt.Sprintf("[%s](%s)", displayText, noteURL)
	})
}
