from pathlib import Path
from docx import Document
from docx.enum.text import WD_BREAK, WD_LINE_SPACING
from docx.oxml import OxmlElement
from docx.oxml.ns import qn
from docx.shared import Pt


ROOT_STORE = Path("/Users/lqt/work/zhct/zhctproject/store")
ROOT_AI = Path("/Users/lqt/work/zhct/zhctproject/ai_api")
OUT = ROOT_STORE / "outputs" / "康比特体重管理系统_后端源代码材料_V2.0_30页.docx"
TEMPLATE = Path("/Users/lqt/Library/Containers/com.tencent.WeWorkMac/Data/Documents/Profiles/2F85AE0B9E074DCE570C7EBCCB22E902/Caches/Files/2026-05/e41f85d9ceef7e2d79384d034ca73627/template.docx")


SOURCE_PLAN = {
    "活动发布": [
        (ROOT_STORE, "application/p/logic/Activity.php"),
        (ROOT_STORE, "application/p/controller/Activity.php"),
        (ROOT_STORE, "application/validate/Activity.php"),
        (ROOT_STORE, "application/model/Activity.php"),
        (ROOT_AI, "app/api/service/Activity.php"),
        (ROOT_AI, "app/api/service/ActivityRegister.php"),
        (ROOT_AI, "app/api/service/ActivityType.php"),
        (ROOT_AI, "app/api/controller/Activity.php"),
        (ROOT_AI, "app/api/controller/ActivityRegister.php"),
        (ROOT_AI, "app/api/controller/ActivityType.php"),
        (ROOT_AI, "app/api/model/Activity.php"),
        (ROOT_AI, "app/api/model/ActivityRegister.php"),
        (ROOT_AI, "app/api/model/ActivityType.php"),
        (ROOT_AI, "app/common/model/Activity.php"),
        (ROOT_AI, "app/common/model/ActivityRegister.php"),
        (ROOT_AI, "app/common/model/ActivityType.php"),
    ],
    "体重管理统计": [
        (ROOT_STORE, "application/p/logic/Weight.php"),
        (ROOT_STORE, "application/model/Weight.php"),
        (ROOT_STORE, "application/p/controller/Weight.php"),
        (ROOT_STORE, "application/model/MemberWeight.php"),
        (ROOT_STORE, "application/model/MemberWeightStatus.php"),
        (ROOT_AI, "app/api/service/user/UserWeight.php"),
        (ROOT_AI, "app/api/controller/user/UserWeight.php"),
        (ROOT_AI, "app/api/model/user/UserWeight.php"),
        (ROOT_AI, "app/api/model/user/UserWeightStatus.php"),
        (ROOT_AI, "app/store/service/weight/StaffWeight.php"),
        (ROOT_AI, "app/store/model/weight/StaffWeight.php"),
        (ROOT_AI, "app/common/model/user/UserWeight.php"),
        (ROOT_AI, "app/common/model/user/UserWeightStatus.php"),
        (ROOT_AI, "app/common/model/weight/StaffWeight.php"),
        (ROOT_AI, "app/common/service/StaffPower.php"),
        (ROOT_AI, "app/common/Algorithm.php"),
    ],
    "打卡排行统计": [
        (ROOT_STORE, "application/p/logic/UserScore.php"),
        (ROOT_STORE, "application/p/controller/UserScore.php"),
        (ROOT_AI, "app/api/model/user/UserScore.php"),
        (ROOT_AI, "app/api/model/user/UserScoreLog.php"),
        (ROOT_AI, "app/api/controller/user/Score.php"),
        (ROOT_AI, "app/api/service/meal/Meal.php"),
        (ROOT_AI, "app/api/service/user/UserCourse.php"),
        (ROOT_AI, "app/api/service/drill/Course.php"),
        (ROOT_AI, "app/api/controller/drill/Course.php"),
        (ROOT_AI, "app/api/model/user/UserCourse.php"),
        (ROOT_AI, "app/api/model/user/CourseSchedule.php"),
    ],
}


def strip_php_comments(raw_lines):
    cleaned = []
    in_block = False
    for raw in raw_lines:
        line = raw.rstrip()
        work = line
        while True:
            if in_block:
                end = work.find("*/")
                if end == -1:
                    work = ""
                    break
                work = work[end + 2:]
                in_block = False
                continue
            start = work.find("/*")
            if start != -1:
                end = work.find("*/", start + 2)
                if end == -1:
                    work = work[:start].rstrip()
                    in_block = True
                    break
                work = (work[:start] + work[end + 2:]).rstrip()
                continue
            break
        stripped = work.strip()
        if not stripped:
            continue
        if stripped.startswith("//") or stripped.startswith("#") or stripped.startswith("*"):
            continue
        for marker in (" //", "\t//"):
            pos = work.find(marker)
            if pos != -1:
                work = work[:pos].rstrip()
        if work.strip():
            cleaned.append(work.rstrip())
    return cleaned


def code_lines_for_module(module):
    lines = []
    for root, rel in SOURCE_PLAN[module]:
        path = root / rel
        text = strip_php_comments(path.read_text(encoding="utf-8", errors="replace").splitlines())
        for raw in text:
            lines.append((module, rel, raw.rstrip()))
    return lines


def clear_body(document):
    body = document._body._element
    sect_pr = body.sectPr
    for child in list(body):
        if child is not sect_pr:
            body.remove(child)


def add_page(doc, page_no, page_lines):
    for _, _, line in page_lines:
        para = doc.add_paragraph()
        para.paragraph_format.space_before = Pt(0)
        para.paragraph_format.space_after = Pt(0)
        para.paragraph_format.line_spacing_rule = WD_LINE_SPACING.EXACTLY
        para.paragraph_format.line_spacing = Pt(13)
        p_pr = para._p.get_or_add_pPr()
        snap = OxmlElement("w:snapToGrid")
        snap.set(qn("w:val"), "0")
        p_pr.append(snap)
        run = para.add_run(line.expandtabs(4))
        run.font.size = Pt(10.5)
    if page_no < 30:
        doc.paragraphs[-1].add_run().add_break(WD_BREAK.PAGE)


def visual_rows(line):
    length = len(line.expandtabs(4))
    if length <= 88:
        return 1
    return (length + 87) // 88


def paginate_module(module, page_count=10, capacity=50):
    source_lines = code_lines_for_module(module)
    pages = []
    cursor = 0
    for page_index in range(page_count):
        page = []
        used = 0
        while cursor < len(source_lines):
            line = source_lines[cursor]
            rows = visual_rows(line[2])
            if page and used + rows > capacity:
                break
            page.append(line)
            used += max(rows, 1)
            cursor += 1
        if not page:
            raise RuntimeError(f"{module} page {page_index + 1} is empty")
        pages.append(page)
    return pages


def final_complete_method_page():
    rel = "app/api/model/user/UserScoreLog.php"
    path = ROOT_AI / rel
    lines = strip_php_comments(path.read_text(encoding="utf-8", errors="replace").splitlines())
    return [("打卡排行统计", rel, line.rstrip()) for line in lines if line.strip()]


def build():
    OUT.parent.mkdir(parents=True, exist_ok=True)
    doc = Document(TEMPLATE)
    clear_body(doc)
    section = doc.sections[0]
    for paragraph in section.header.paragraphs:
        paragraph.clear()
    style = doc.styles["Normal"]
    style.font.size = Pt(10.5)
    style.paragraph_format.space_before = Pt(0)
    style.paragraph_format.space_after = Pt(0)
    style.paragraph_format.line_spacing_rule = WD_LINE_SPACING.EXACTLY
    style.paragraph_format.line_spacing = Pt(13)
    pages = []
    for module in ("活动发布", "体重管理统计", "打卡排行统计"):
        pages.extend(paginate_module(module))
    pages[-1] = final_complete_method_page()
    for page_no, page_lines in enumerate(pages, 1):
        add_page(doc, page_no, page_lines)
    doc.save(OUT)
    total_lines = sum(len(page) for page in pages)
    print(OUT)
    print(f"pages=30 code_lines={total_lines} overflow_policy=move-long-lines-to-next-page")


if __name__ == "__main__":
    build()
