package cms

import (
	"os"
	"path/filepath"
	"strings"
	"testing"
)

func TestCreateSiteArticleAndBuild(t *testing.T) {
	tmp := t.TempDir()
	root, err := CreateSite(tmp, SiteOptions{Name: "my-capsule", Title: "My Capsule", Host: "gemini.example.org", Language: "en"})
	if err != nil {
		t.Fatalf("CreateSite: %v", err)
	}
	if _, err := CreateArticle(root, ArticleOptions{Title: "Hello Gemini", Section: "journal", Tags: []string{"gemini"}}); err != nil {
		t.Fatalf("CreateArticle: %v", err)
	}
	if _, err := CreateSection(root, SectionOptions{Name: "journal", Title: "Journal"}); err != nil {
		t.Fatalf("CreateSection: %v", err)
	}
	report, err := Build(root)
	if err != nil {
		t.Fatalf("Build: %v", err)
	}
	if report.FilesWritten < 3 {
		t.Fatalf("expected at least 3 files written, got %d", report.FilesWritten)
	}
	assertFileContains(t, filepath.Join(root, "public/index.gmi"), "My Capsule")
	assertFileContains(t, filepath.Join(root, "public/journal/hello-gemini.gmi"), "# Hello Gemini")
	assertFileContains(t, filepath.Join(root, "public/journal/index.gmi"), "=> /journal/hello-gemini.gmi")
}

func TestMenuAndWidgetShortcodes(t *testing.T) {
	tmp := t.TempDir()
	root, err := CreateSite(tmp, SiteOptions{Name: "capsule", Title: "Capsule"})
	if err != nil {
		t.Fatalf("CreateSite: %v", err)
	}
	if _, err := CreatePage(root, PageOptions{Slug: "about", Title: "About"}); err != nil {
		t.Fatalf("CreatePage: %v", err)
	}
	if err := AddMenuItem(root, MenuItemOptions{Menu: "top", Kind: "page", Target: "about", Label: "About"}); err != nil {
		t.Fatalf("AddMenuItem: %v", err)
	}
	if _, err := CreateArticle(root, ArticleOptions{Title: "First Post"}); err != nil {
		t.Fatalf("CreateArticle: %v", err)
	}
	if _, err := Build(root); err != nil {
		t.Fatalf("Build: %v", err)
	}
	assertFileContains(t, filepath.Join(root, "public/index.gmi"), "=> /about.gmi About")
	assertFileContains(t, filepath.Join(root, "public/index.gmi"), "=> /articles/first-post.gmi")
}

func TestAddMarkdownArticle(t *testing.T) {
	tmp := t.TempDir()
	root, err := CreateSite(tmp, SiteOptions{Name: "capsule", Title: "Capsule"})
	if err != nil {
		t.Fatalf("CreateSite: %v", err)
	}
	source := filepath.Join(tmp, "article.md")
	if err := os.WriteFile(source, []byte("# Imported Article\n\n- one\n\n[Gemini](gemini://example.org)\n"), 0o644); err != nil {
		t.Fatalf("write source: %v", err)
	}
	doc, err := AddFile(root, AddOptions{
		Path:    source,
		Section: "journal",
		Tags:    []string{"gemini"},
		Draft:   true,
		Menu:    "top",
	})
	if err != nil {
		t.Fatalf("AddFile: %v", err)
	}
	if doc.Type != "article" {
		t.Fatalf("expected article, got %q", doc.Type)
	}
	assertFileContains(t, doc.SourcePath, "section = \"journal\"")
	assertFileContains(t, doc.SourcePath, "tags = [\"gemini\"]")
	assertFileContains(t, doc.SourcePath, "draft = true")
	assertFileContains(t, doc.SourcePath, "* one")
	assertFileContains(t, doc.SourcePath, "=> gemini://example.org Gemini")
}

func TestAddDirectoryImportsContentFiles(t *testing.T) {
	tmp := t.TempDir()
	root, err := CreateSite(tmp, SiteOptions{Name: "capsule", Title: "Capsule"})
	if err != nil {
		t.Fatalf("CreateSite: %v", err)
	}
	sourceDir := filepath.Join(tmp, "source")
	if err := os.MkdirAll(filepath.Join(sourceDir, "pages"), 0o755); err != nil {
		t.Fatalf("mkdir source: %v", err)
	}
	if err := os.WriteFile(filepath.Join(sourceDir, "post.md"), []byte("---\ntitle: YAML Title\ntags: gemini, cms\n---\n\nBody\n"), 0o644); err != nil {
		t.Fatalf("write post: %v", err)
	}
	if err := os.WriteFile(filepath.Join(sourceDir, "pages/about.gmi"), []byte("+++\ntitle = \"About\"\ntype = \"page\"\nslug = \"about\"\n+++\n\n# About\n"), 0o644); err != nil {
		t.Fatalf("write page: %v", err)
	}
	if err := os.WriteFile(filepath.Join(sourceDir, "ignore.txt"), []byte("ignore"), 0o644); err != nil {
		t.Fatalf("write ignored file: %v", err)
	}

	report, err := AddPath(root, AddOptions{Path: sourceDir, Section: "journal"})
	if err != nil {
		t.Fatalf("AddPath: %v", err)
	}
	if len(report.Documents) != 2 {
		t.Fatalf("expected 2 imported documents, got %d", len(report.Documents))
	}
	article := findDocument(t, report.Documents, "YAML Title")
	assertFileContains(t, article.SourcePath, "title = \"YAML Title\"")
	assertFileContains(t, article.SourcePath, "tags = [\"gemini\", \"cms\"]")
	assertFileContains(t, article.SourcePath, "section = \"journal\"")
	assertFileContains(t, filepath.Join(root, "content/pages/about.gmi"), "title = \"About\"")
}

func TestStatusReportCountsContentAndIssues(t *testing.T) {
	tmp := t.TempDir()
	root, err := CreateSite(tmp, SiteOptions{Name: "capsule", Title: "Capsule"})
	if err != nil {
		t.Fatalf("CreateSite: %v", err)
	}
	if _, err := CreateArticle(root, ArticleOptions{Title: "Draft Post", Draft: true}); err != nil {
		t.Fatalf("CreateArticle: %v", err)
	}
	if _, err := Build(root); err != nil {
		t.Fatalf("Build: %v", err)
	}
	status, err := Status(root)
	if err != nil {
		t.Fatalf("Status: %v", err)
	}
	if status.Pages != 1 || status.Articles != 1 || status.Drafts != 1 {
		t.Fatalf("unexpected status counts: %#v", status)
	}
	if status.Health() != "OK" {
		t.Fatalf("expected OK health, got %s with %#v", status.Health(), status.Issues)
	}
	if status.PublicFiles == 0 {
		t.Fatalf("expected public files to be counted")
	}
}

func TestCheckReportsMalformedGemtextLink(t *testing.T) {
	tmp := t.TempDir()
	root, err := CreateSite(tmp, SiteOptions{Name: "capsule", Title: "Capsule"})
	if err != nil {
		t.Fatalf("CreateSite: %v", err)
	}
	pagePath := filepath.Join(root, "content/pages/bad.gmi")
	if err := os.WriteFile(pagePath, []byte("+++\ntitle = \"Bad\"\ntype = \"page\"\nslug = \"bad\"\n+++\n\n=>\n"), 0o644); err != nil {
		t.Fatalf("write bad page: %v", err)
	}
	report, err := Check(root)
	if err != nil {
		t.Fatalf("Check: %v", err)
	}
	if !report.HasErrors() {
		t.Fatalf("expected check errors, got %#v", report.Issues)
	}
}

func assertFileContains(t *testing.T, path, want string) {
	t.Helper()
	data, err := os.ReadFile(path)
	if err != nil {
		t.Fatalf("read %s: %v", path, err)
	}
	if !strings.Contains(string(data), want) {
		t.Fatalf("%s does not contain %q\n%s", path, want, string(data))
	}
}

func findDocument(t *testing.T, docs []Document, title string) Document {
	t.Helper()
	for _, doc := range docs {
		if doc.Title == title {
			return doc
		}
	}
	t.Fatalf("document %q not found in %#v", title, docs)
	return Document{}
}
