package service

import (
	"archive/zip"
	"bytes"
	"strings"
	"testing"
)

func TestParseDramaSourceRejectsDisguisedAndExecutableContent(t *testing.T) {
	cases := []struct {
		name     string
		filename string
		data     []byte
		raw      string
	}{
		{name: "pdf renamed txt", filename: "novel.txt", data: []byte("%PDF-1.7\ncontent")},
		{name: "zip renamed txt", filename: "novel.txt", data: []byte{'P', 'K', 3, 4, 1, 2, 3}},
		{name: "html after long prefix", filename: "novel.txt", data: []byte(strings.Repeat("正文", 3000) + "<script>alert(1)</script>")},
		{name: "script pasted", raw: "小说正文\njavascript:alert(1)"},
		{name: "binary pasted", raw: "正文\x00内容"},
	}
	for _, test := range cases {
		t.Run(test.name, func(t *testing.T) {
			if _, _, err := parseDramaSource(test.filename, test.data, test.raw); err == nil {
				t.Fatal("expected unsafe import to be rejected")
			}
		})
	}
}

func TestExtractDOCXReadsParagraphsAndValidatesWordPackage(t *testing.T) {
	valid := makeTestDOCX(t, `<?xml version="1.0"?><Types xmlns="http://schemas.openxmlformats.org/package/2006/content-types"><Override PartName="/word/document.xml" ContentType="application/vnd.openxmlformats-officedocument.wordprocessingml.document.main+xml"/></Types>`, nil)
	text, err := extractDOCX(valid)
	if err != nil {
		t.Fatalf("extractDOCX returned error: %v", err)
	}
	if !strings.Contains(text, "第一章 开始") || !strings.Contains(text, "这是正文") {
		t.Fatalf("unexpected extracted text: %q", text)
	}

	fake := makeTestDOCX(t, `<?xml version="1.0"?><Types/>`, nil)
	if _, err := extractDOCX(fake); err == nil {
		t.Fatal("expected ZIP with fake DOCX structure to be rejected")
	}

	macro := makeTestDOCX(t, `<?xml version="1.0"?><Types xmlns="http://schemas.openxmlformats.org/package/2006/content-types"><Override PartName="/word/document.xml" ContentType="application/vnd.openxmlformats-officedocument.wordprocessingml.document.main+xml"/></Types>`, map[string]string{"word/vbaProject.bin": "macro"})
	if _, err := extractDOCX(macro); err == nil {
		t.Fatal("expected macro-enabled package to be rejected")
	}
}

func TestSplitDramaChaptersPreservesOriginalText(t *testing.T) {
	source := "作品说明\n\n第一章 初见\n正文一\n\n第二章 重逢\n正文二"
	chapters := splitDramaChapters(source)
	if len(chapters) != 2 {
		t.Fatalf("expected 2 chapters, got %d", len(chapters))
	}
	joined := strings.TrimSpace(chapters[0].Content + "\n\n" + chapters[1].Content)
	if joined != source {
		t.Fatalf("chapter splitting lost or duplicated text:\nwant: %q\n got: %q", source, joined)
	}
	if chapters[0].Title != "第一章 初见" || chapters[1].Title != "第二章 重逢" {
		t.Fatalf("unexpected chapter titles: %#v", chapters)
	}
	if chapters := splitDramaChapters("没有章节标题的完整正文"); chapters != nil {
		t.Fatalf("text without chapter titles must require single-episode confirmation: %#v", chapters)
	}
}

func makeTestDOCX(t *testing.T, contentTypes string, extras map[string]string) []byte {
	t.Helper()
	var buffer bytes.Buffer
	writer := zip.NewWriter(&buffer)
	files := map[string]string{
		"[Content_Types].xml": contentTypes,
		"word/document.xml":   `<?xml version="1.0"?><w:document xmlns:w="http://schemas.openxmlformats.org/wordprocessingml/2006/main"><w:body><w:p><w:r><w:t>第一章 开始</w:t></w:r></w:p><w:p><w:r><w:t>这是正文</w:t></w:r></w:p></w:body></w:document>`,
	}
	for name, content := range extras {
		files[name] = content
	}
	for name, content := range files {
		entry, err := writer.Create(name)
		if err != nil {
			t.Fatal(err)
		}
		if _, err = entry.Write([]byte(content)); err != nil {
			t.Fatal(err)
		}
	}
	if err := writer.Close(); err != nil {
		t.Fatal(err)
	}
	return buffer.Bytes()
}
