package main

import (
	"bytes"
	"fmt"
	"html"
	"math/rand"
	"path/filepath"
	"regexp"
	"strings"
	"text/template"
)

type TemplateEngine struct {
	templates *template.Template
}

func NewTemplateEngine(templateDir string) (*TemplateEngine, error) {
	funcMap := template.FuncMap{
		"safe": func(s string) string {
			return s
		},
		"htmlEscape": func(s string) string {
			return html.EscapeString(s)
		},
		"renderCompleteArticle": func(aiBlocks []string, images []Image, injectAds string, templateType string) string {
			var result strings.Builder
			for _, block := range aiBlocks {
				if block != "" {
					result.WriteString(block)
				}
			}
			return result.String()
		},
		"renderParagraph": func(content string, templateType string) string {
			return content
		},
		"renderImageWithCaption": func(img Image, templateType string) string {
			return renderImageHTML(img, templateType)
		},
		"slice": func(s []Image, start int) []Image {
			if start >= len(s) {
				return []Image{}
			}
			return s[start:]
		},
		"sliceArticles": func(s []ArticleMeta, start int) []ArticleMeta {
			if start >= len(s) {
				return []ArticleMeta{}
			}
			return s[start:]
		},
		"truncateImages": func(s []Image, limit int) []Image {
			if len(s) <= limit {
				return s
			}
			return s[:limit]
		},
		"truncateArticles": func(s []ArticleMeta, limit int) []ArticleMeta {
			if len(s) <= limit {
				return s
			}
			return s[:limit]
		},
		"add": func(a, b int) int {
			return a + b
		},
		"formatDate": func(t string) string {
			parsed := parseTime(t)
			return parsed.Format("Jan 02, 2006")
		},
	}

	pattern := filepath.Join(templateDir, "*.gohtml")
	tmpl, err := template.New("").Funcs(funcMap).ParseGlob(pattern)
	if err != nil {
		return nil, fmt.Errorf("error parsing templates: %w", err)
	}

	return &TemplateEngine{templates: tmpl}, nil
}

func (e *TemplateEngine) Render(name string, data interface{}) (string, error) {
	var buf bytes.Buffer
	writer := newBufferedWriter(&buf)
	if err := e.templates.ExecuteTemplate(writer, name, data); err != nil {
		return "", fmt.Errorf("error rendering template %s: %w", name, err)
	}
	writer.Flush()
	return buf.String(), nil
}

type ArticleMeta struct {
	Slug    string
	Title   string
	Keyword string
}

type ArticleData struct {
	Title            string
	Description      string
	Canonical        string
	Schema           string
	SiteName         string
	AuthorName       string
	Date             string
	CreatedAt        string
	AIBlocks         []string
	Images           []Image
	RelatedArticles  []ArticleMeta
	InjectHeader     string
	InjectFooter     string
	InjectAds        string
	HasAIBlocks      bool
	HasRelated       bool
	ContentBody      string
	MainImage        *Image
	AdditionalImages []Image
	HeaderInject     string
	FooterInject     string
}

type IndexData struct {
	SiteName     string
	AuthorName   string
	Description  string
	Canonical    string
	Schema       string
	Articles     []ArticleMeta
	InjectHeader string
	InjectFooter string
}

func pickRandomRelated(relatedKW []string, used map[int]bool) string {
	if len(relatedKW) <= 1 {
		return ""
	}
	candidates := make([]int, 0)
	for i := 1; i < len(relatedKW); i++ {
		if !used[i] {
			candidates = append(candidates, i)
		}
	}
	if len(candidates) == 0 {
		for i := 1; i < len(relatedKW); i++ {
			candidates = append(candidates, i)
		}
	}
	if len(candidates) == 0 {
		return ""
	}
	idx := candidates[rand.Intn(len(candidates))]
	used[idx] = true
	return Ucwords(relatedKW[idx])
}

func renderSnippetContent(descriptions []string, images []Image, relatedKW []string, injectAds string, templateType string) string {
	var result strings.Builder
	usedRelated := make(map[int]bool)
	dIdx := 0
	iIdx := 0
	imgCount := len(images)
	descCount := len(descriptions)

	writeAds := func() {
		if injectAds != "" {
			result.WriteString(injectAds)
		}
	}

	writeParagraph := func() bool {
		if dIdx >= descCount {
			return false
		}
		text := strings.TrimSpace(descriptions[dIdx])
		dIdx++
		if text == "" {
			return true
		}
		result.WriteString(renderParagraphHTML("<p>"+html.EscapeString(text)+"</p>", templateType))
		return true
	}

	writeImage := func() bool {
		if iIdx >= imgCount {
			return false
		}
		result.WriteString(renderImageHTML(images[iIdx], templateType))
		iIdx++
		return true
	}

	writeH2 := func() {
		kw := pickRandomRelated(relatedKW, usedRelated)
		if kw != "" {
			result.WriteString(fmt.Sprintf("<h2>%s</h2>", html.EscapeString(kw)))
		}
	}

	writeAds()
	writeParagraph()
	writeParagraph()
	writeImage()

	writeAds()
	writeH2()
	writeParagraph()
	writeParagraph()

	writeAds()
	writeImage()
	writeImage()
	writeH2()

	writeAds()
	writeParagraph()
	writeImage()
	writeImage()
	writeParagraph()
	writeH2()

	writeAds()
	writeParagraph()
	writeParagraph()

	for iIdx < imgCount {
		writeImage()
	}

	for dIdx < descCount {
		writeParagraph()
	}

	writeAds()

	return result.String()
}

var tagStripRe = regexp.MustCompile(`<[^>]*>`)

func stripTags(s string) string {
	return tagStripRe.ReplaceAllString(s, "")
}

func renderParagraphHTML(content string, templateType string) string {
	switch templateType {
	case "monochrome":
		return `<div class="mb-6 leading-relaxed text-lg">` + content + `</div>`
	case "high-contrast":
		return `<div class="mb-8 leading-tight font-medium text-gray-800">` + content + `</div>`
	case "cyberpunk":
		return `<div class="mb-6 opacity-90 leading-relaxed">` + content + `</div>`
	default:
		return content
	}
}

func renderImageHTML(img Image, templateType string) string {
	url := html.EscapeString(img.ImageURL)
	alt := html.EscapeString(img.Title)

	switch templateType {
	case "monochrome":
		return fmt.Sprintf(`<figure class="my-12 p-1 bg-white"><img src="%s" alt="%s" class="w-full"><figcaption class="text-sm text-gray-400 mt-2 text-center italic">%s</figcaption></figure>`, url, alt, alt)
	case "high-contrast":
		return fmt.Sprintf(`<figure class="my-16 img-zoom"><img src="%s" alt="%s" class="w-full h-auto"><figcaption class="text-sm text-gray-500 mt-3 font-medium">%s</figcaption></figure>`, url, alt, alt)
	case "glassmorphism":
		return fmt.Sprintf(`<figure class="my-6"><img src="%s" alt="%s" class="w-full rounded-2xl shadow-xl"><figcaption class="text-sm text-white/60 mt-3 text-center">%s</figcaption></figure>`, url, alt, alt)
	case "cyberpunk":
		return fmt.Sprintf(`<figure class="my-8 relative"><img src="%s" alt="%s" class="w-full grayscale hover:grayscale-0 transition-all duration-500 cyber-border"><figcaption class="text-xs text-cyan-400/60 mt-2 font-mono">%s</figcaption></figure>`, url, alt, alt)
	case "pinterest":
		return fmt.Sprintf(`<figure><img src="%s" style="width: 100%%; border-radius: 10px; margin: 20px 0;" alt="%s"><figcaption style="font-size:0.85rem; color:#666; text-align:center;">%s</figcaption></figure>`, url, alt, alt)
	case "gallery":
		return fmt.Sprintf(`<figure class="image-section" style="margin: 30px 0;"><img src="%s" style="width: 100%%; border-radius: 12px;" alt="%s"><figcaption style="font-size:0.8rem; color:#888; margin-top:8px; text-align:center;">%s</figcaption></figure>`, url, alt, alt)
	case "bento":
		return fmt.Sprintf(`<figure class="my-4"><img src="%s" alt="%s" class="w-full rounded-xl shadow-sm"><figcaption class="text-sm text-gray-500 mt-2">%s</figcaption></figure>`, url, alt, alt)
	default:
		return fmt.Sprintf(`<figure class="image-section"><img src="%s" class="section-image" style="width:100%%; height:auto; border-radius:8px; margin: 20px 0;" alt="%s"><figcaption style="font-size:0.85rem; color:#666; margin-top:8px;">%s</figcaption></figure>`, url, alt, alt)
	}
}
