// Package office 生成真实可用的 Word（.docx）文档。
//
// 这里不引第三方 Office 库（UniOffice 为商业授权、且会显著增重依赖），而是直接
// 按 OOXML(WordprocessingML) 规范用标准库 archive/zip + 内联 XML 拼出最小但完整、
// Word / Pages / WPS 均可正常打开的 .docx 包。零额外依赖，契合 clone 即跑的目标。
package office

import (
	"archive/zip"
	"bytes"
	"context"
	"fmt"
	"strings"
)

// Doc 累积段落，最终序列化为一个 .docx 字节流。
type Doc struct {
	body strings.Builder // word/document.xml 的 <w:body> 内部段落串
}

// NewDoc 新建一个空文档。
func NewDoc() *Doc { return &Doc{} }

// Title 加一行大标题（居中、加粗、约 18pt）。
func (d *Doc) Title(text string) *Doc {
	d.para(text, paraOpts{bold: true, sizeHalfPt: 36, center: true, spaceAfter: 240})
	return d
}

// Heading 加一行小节标题（加粗、约 14pt）。
func (d *Doc) Heading(text string) *Doc {
	d.para(text, paraOpts{bold: true, sizeHalfPt: 28, spaceBefore: 240, spaceAfter: 120})
	return d
}

// Para 加一个正文段落（约 11pt）。空串忽略。
func (d *Doc) Para(text string) *Doc {
	text = strings.TrimSpace(text)
	if text == "" {
		return d
	}
	d.para(text, paraOpts{sizeHalfPt: 22, spaceAfter: 120})
	return d
}

// Body 把一段可能含多个换行的正文按行拆成多个段落。
func (d *Doc) Body(text string) *Doc {
	for _, line := range strings.Split(strings.ReplaceAll(text, "\r\n", "\n"), "\n") {
		d.Para(line)
	}
	return d
}

type paraOpts struct {
	bold        bool
	center      bool
	sizeHalfPt  int // OOXML 字号单位为半磅（half-points），22 = 11pt
	spaceBefore int // 段前间距（twentieths of a point）
	spaceAfter  int
}

func (d *Doc) para(text string, o paraOpts) {
	d.body.WriteString("<w:p>")
	// 段落属性：间距 + 居中。
	d.body.WriteString("<w:pPr>")
	if o.spaceBefore > 0 || o.spaceAfter > 0 {
		fmt.Fprintf(&d.body, `<w:spacing w:before="%d" w:after="%d"/>`, o.spaceBefore, o.spaceAfter)
	}
	if o.center {
		d.body.WriteString(`<w:jc w:val="center"/>`)
	}
	d.body.WriteString("</w:pPr>")
	// 文本 run。
	d.body.WriteString("<w:r><w:rPr>")
	if o.bold {
		d.body.WriteString("<w:b/>")
	}
	if o.sizeHalfPt > 0 {
		fmt.Fprintf(&d.body, `<w:sz w:val="%d"/><w:szCs w:val="%d"/>`, o.sizeHalfPt, o.sizeHalfPt)
	}
	d.body.WriteString("</w:rPr>")
	fmt.Fprintf(&d.body, `<w:t xml:space="preserve">%s</w:t>`, escapeXML(text))
	d.body.WriteString("</w:r></w:p>")
}

// Bytes 把累积的段落打包为合规 .docx（zip + 三个核心 XML 部件）。
func (d *Doc) Bytes() ([]byte, error) {
	var buf bytes.Buffer
	zw := zip.NewWriter(&buf)

	parts := map[string]string{
		"[Content_Types].xml": contentTypesXML,
		"_rels/.rels":         relsXML,
		"word/document.xml":   documentXML(d.body.String()),
	}
	for name, content := range parts {
		w, err := zw.Create(name)
		if err != nil {
			return nil, err
		}
		if _, err := w.Write([]byte(content)); err != nil {
			return nil, err
		}
	}
	if err := zw.Close(); err != nil {
		return nil, err
	}
	return buf.Bytes(), nil
}

// ---- Renderer：把结构化报告（标题 + 章节）渲染为 docx ----

// Section 是报告的一章：小节标题 + 正文。
type Section struct {
	Heading string `json:"heading"`
	Body    string `json:"body"`
}

// Renderer 把结构化数据渲染为 docx。
type Renderer struct{}

func NewRenderer() *Renderer { return &Renderer{} }

// RenderReport 渲染「大标题 + 多章节」结构的报告为 .docx 字节流。
func (r *Renderer) RenderReport(_ context.Context, title string, sections []Section) ([]byte, error) {
	doc := NewDoc()
	if title != "" {
		doc.Title(title)
	}
	for _, s := range sections {
		if s.Heading != "" {
			doc.Heading(s.Heading)
		}
		doc.Body(s.Body)
	}
	return doc.Bytes()
}

// ---- OOXML 模板 ----

const contentTypesXML = `<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<Types xmlns="http://schemas.openxmlformats.org/package/2006/content-types">
<Default Extension="rels" ContentType="application/vnd.openxmlformats-package.relationships+xml"/>
<Default Extension="xml" ContentType="application/xml"/>
<Override PartName="/word/document.xml" ContentType="application/vnd.openxmlformats-officedocument.wordprocessingml.document.main+xml"/>
</Types>`

const relsXML = `<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<Relationships xmlns="http://schemas.openxmlformats.org/package/2006/relationships">
<Relationship Id="rId1" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/officeDocument" Target="word/document.xml"/>
</Relationships>`

func documentXML(body string) string {
	return `<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<w:document xmlns:w="http://schemas.openxmlformats.org/wordprocessingml/2006/main"><w:body>` +
		body +
		`<w:sectPr><w:pgSz w:w="11906" w:h="16838"/><w:pgMar w:top="1440" w:right="1440" w:bottom="1440" w:left="1440"/></w:sectPr></w:body></w:document>`
}

func escapeXML(s string) string {
	r := strings.NewReplacer(
		"&", "&amp;",
		"<", "&lt;",
		">", "&gt;",
		`"`, "&quot;",
		"'", "&apos;",
	)
	return r.Replace(s)
}
