package main

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

var (
	tagSplitRe  = regexp.MustCompile(`(<\/(?:p|h[1-6]|ul|ol|table|blockquote)>)`)
	tagStartRe  = regexp.MustCompile(`^<(p|h[1-6]|ul|ol|table|blockquote)[\s>]`)
	h2Re        = regexp.MustCompile(`<h2[\s>]`)
	imgPositions = map[int]bool{1: true, 2: true, 4: true, 6: true, 9: true, 12: true}
)

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": renderCompleteArticle,
		"renderParagraph":       renderParagraph,
		"renderImageWithCaption": renderImageWithCaption,
		"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
	SchemaMap        map[string]interface{}
}

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

func renderCompleteArticle(aiBlocks []string, images []Image, injectAds string, templateType string) string {
	var blocks []string

	for _, aiBlock := range aiBlocks {
		if aiBlock == "" || strings.TrimSpace(stripTags(aiBlock)) == "" {
			continue
		}
		parts := tagSplitRe.Split(aiBlock, -1)
		matches := tagSplitRe.FindAllString(aiBlock, -1)

		current := ""
		for i, part := range parts {
			current += part
			if i < len(matches) {
				current += matches[i]
			}

			if i < len(matches) {
				trimmed := strings.TrimSpace(current)
				if trimmed != "" && strings.TrimSpace(stripTags(trimmed)) != "" {
					if tagStartRe.MatchString(trimmed) {
						blocks = append(blocks, `<div class="mb-4">`+trimmed+`</div>`)
					} else {
						blocks = append(blocks, `<div class="mb-4"><p>`+trimmed+`</p></div>`)
					}
				}
				current = ""
			}
		}

		if strings.TrimSpace(current) != "" && strings.TrimSpace(stripTags(strings.TrimSpace(current))) != "" {
			trimmed := strings.TrimSpace(current)
			if tagStartRe.MatchString(trimmed) {
				blocks = append(blocks, `<div class="mb-4">`+trimmed+`</div>`)
			} else {
				blocks = append(blocks, `<div class="mb-4"><p>`+trimmed+`</p></div>`)
			}
		}
	}

	var result strings.Builder
	imgCount := len(images)
	imgIdx := 0
	firstH2AdDone := false

	for i, block := range blocks {
		result.WriteString(renderParagraphHTML(block, templateType))

		if !firstH2AdDone && h2Re.MatchString(block) {
			if injectAds != "" {
				result.WriteString(injectAds)
			}
			firstH2AdDone = true
		}

		pNum := i + 1
		if imgIdx < imgCount && imgPositions[pNum] {
			result.WriteString(renderImageHTML(images[imgIdx], templateType))
			imgIdx++
		} else if pNum == 14 && imgIdx < imgCount {
			for imgIdx < imgCount {
				result.WriteString(renderImageHTML(images[imgIdx], templateType))
				imgIdx++
			}
		}

		if pNum == 3 || pNum == 5 {
			if injectAds != "" {
				result.WriteString(injectAds)
			}
		}
	}

	for imgIdx < imgCount {
		result.WriteString(renderImageHTML(images[imgIdx], templateType))
		imgIdx++
	}

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

	return result.String()
}

func renderParagraph(content string, templateType string) string {
	return renderParagraphHTML(content, templateType)
}

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 renderImageWithCaption(img Image, templateType string) string {
	return renderImageHTML(img, templateType)
}

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)
	}
}

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

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