#!/usr/bin/env python3
"""
generate_sitemap.py
====================
study.topeduprep.uk 의 실제 .html 파일들을 폴더째로 스캔해서
sitemap.xml 을 자동으로 만들어주는 스크립트입니다.

[사용법]
1. 이 파일을 study.topeduprep.uk 의 루트 폴더
   (act/, sat/, ap/ 등 카테고리 폴더들이 들어있는 그 폴더)에 넣으세요.
2. 터미널에서 실행:
     python3 generate_sitemap.py
3. 같은 폴더에 sitemap.xml 이 생성됩니다.
4. Cloudflare Pages / Wrangler로 배포하면 자동으로
   https://study.topeduprep.uk/sitemap.xml 로 서비스됩니다.

[왜 이 방식인가]
- 2,299개 URL을 사람이 손으로 입력하면 100% 누락/오타가 생깁니다.
- 폴더를 스캔하면 실제 존재하는 파일과 sitemap이 항상 100% 일치합니다.
- 새 학습지를 추가할 때마다 이 스크립트만 다시 실행하면 끝입니다.
  (배포 파이프라인에 넣어서 자동화하는 것을 추천합니다 — 아래 'CI 자동화' 참고)

[GSC 최적화를 위해 적용한 규칙]
1. lastmod  : 파일의 실제 수정 시각(mtime)을 ISO8601로 기록 (정직한 lastmod가 크롤 신뢰도를 높입니다)
2. priority : 카테고리별 비중(수학 982개, ACT 447개 등 실제 트래픽 가치)에 맞춰 차등 부여
3. 동일 제목 중복본(-2, -3, -4 ... 접미사) 페이지는 priority를 낮춰서
   Google에게 "원본보다 중요도가 낮은 변형"이라는 신호를 줍니다.
   ⚠️ 단, 이건 임시방편이고 근본적으로는 아래 '중복 콘텐츠 경고' 섹션을 꼭 읽어주세요.
4. 50,000 URL 제한에 안전하게 걸리도록 1개 파일로 충분하지만,
   향후 5만 개 근접 시 자동으로 sitemap-index.xml + 분할 파일로 전환되도록 만들어뒀습니다.
"""

import os
import re
import datetime
from pathlib import Path
from xml.sax.saxutils import escape

# ── 설정 ────────────────────────────────────────────────────────────
DOMAIN = "https://study.topeduprep.uk"
ROOT_DIR = Path(__file__).parent  # 이 스크립트가 있는 폴더를 site root로 간주
OUTPUT_FILE = ROOT_DIR / "sitemap.xml"
MAX_URLS_PER_SITEMAP = 45000  # 50,000 제한에 여유를 둠

# 카테고리(최상위 폴더명) -> 기본 priority
# study.topeduprep.uk 메인 페이지에 표시된 항목 수 기준으로 가중치 부여
CATEGORY_PRIORITY = {
    "math": 0.8,     # 수학 982개 - 최대 카테고리
    "act": 0.7,      # ACT 447개
    "sat": 0.8,      # SAT 336개 - 메인 타겟 시험
    "ap": 0.7,       # AP 178개
    "exam": 0.6,     # 수능/모의고사 67개
    "ssat": 0.6,     # SSAT 61개
    "etc": 0.5,      # 기타 205개
    "naesin": 0.5,   # 내신 11개
    "english": 0.5,  # 영어 9개
    "science": 0.4,  # 과학 3개
}
DEFAULT_PRIORITY = 0.6

# 제목이 거의 동일한 변형본임을 나타내는 패턴 (al1-2.html, al1-3.html, quiz-2.html 등)
DUPLICATE_SUFFIX_RE = re.compile(r"-(\d+)\.html?$", re.IGNORECASE)

EXCLUDE_DIRS = {".git", "node_modules", ".wrangler", "dist", "_worker.js"}
EXCLUDE_FILES = {"404.html", "index.html"}  # index.html은 카테고리 목록 페이지면 따로 처리하세요


def iso_date(timestamp: float) -> str:
    return datetime.datetime.fromtimestamp(timestamp, tz=datetime.timezone.utc).strftime("%Y-%m-%d")


def get_priority(rel_path: Path) -> str:
    category = rel_path.parts[0].lower() if rel_path.parts else ""
    base = CATEGORY_PRIORITY.get(category, DEFAULT_PRIORITY)

    # 동일 시리즈의 2번째, 3번째... 변형본은 약간 낮춤 (al1-2.html -> 0.05 감점, 최대 -0.2)
    m = DUPLICATE_SUFFIX_RE.search(rel_path.name)
    if m:
        seq = int(m.group(1))
        base = max(0.3, base - min(0.2, 0.03 * seq))

    return f"{base:.1f}"


def collect_urls():
    urls = []
    for dirpath, dirnames, filenames in os.walk(ROOT_DIR):
        dirnames[:] = [d for d in dirnames if d not in EXCLUDE_DIRS and not d.startswith(".")]
        for fname in filenames:
            if not fname.lower().endswith((".html", ".htm")):
                continue
            if fname in EXCLUDE_FILES:
                continue
            full_path = Path(dirpath) / fname
            rel_path = full_path.relative_to(ROOT_DIR)

            url_path = "/" + str(rel_path).replace(os.sep, "/")
            loc = DOMAIN + url_path
            lastmod = iso_date(full_path.stat().st_mtime)
            priority = get_priority(rel_path)

            urls.append({"loc": loc, "lastmod": lastmod, "priority": priority})

    urls.sort(key=lambda u: u["loc"])
    return urls


def write_sitemap(urls, output_path):
    lines = ['<?xml version="1.0" encoding="UTF-8"?>',
             '<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">']
    for u in urls:
        lines.append("  <url>")
        lines.append(f"    <loc>{escape(u['loc'])}</loc>")
        lines.append(f"    <lastmod>{u['lastmod']}</lastmod>")
        lines.append("    <changefreq>monthly</changefreq>")
        lines.append(f"    <priority>{u['priority']}</priority>")
        lines.append("  </url>")
    lines.append("</urlset>")
    output_path.write_text("\n".join(lines), encoding="utf-8")


def write_sitemap_index(chunks, index_path):
    lines = ['<?xml version="1.0" encoding="UTF-8"?>',
             '<sitemapindex xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">']
    today = datetime.date.today().isoformat()
    for i in range(len(chunks)):
        lines.append("  <sitemap>")
        lines.append(f"    <loc>{DOMAIN}/sitemap-{i+1}.xml</loc>")
        lines.append(f"    <lastmod>{today}</lastmod>")
        lines.append("  </sitemap>")
    lines.append("</sitemapindex>")
    index_path.write_text("\n".join(lines), encoding="utf-8")


def main():
    urls = collect_urls()
    print(f"총 {len(urls)}개 URL을 찾았습니다.")

    if len(urls) <= MAX_URLS_PER_SITEMAP:
        write_sitemap(urls, OUTPUT_FILE)
        print(f"✅ {OUTPUT_FILE} 생성 완료 ({len(urls)} URLs)")
    else:
        # 5만 개 근접 시 자동 분할
        chunks = [urls[i:i + MAX_URLS_PER_SITEMAP] for i in range(0, len(urls), MAX_URLS_PER_SITEMAP)]
        for i, chunk in enumerate(chunks):
            write_sitemap(chunk, ROOT_DIR / f"sitemap-{i+1}.xml")
        write_sitemap_index(chunks, OUTPUT_FILE.parent / "sitemap.xml")
        print(f"✅ {len(chunks)}개 sitemap 파일 + sitemap.xml(인덱스) 생성 완료")

    # 카테고리별 개수 출력 (검증용)
    by_category = {}
    for u in urls:
        cat = u["loc"].split(DOMAIN + "/")[1].split("/")[0]
        by_category[cat] = by_category.get(cat, 0) + 1
    print("\n카테고리별 URL 개수:")
    for cat, count in sorted(by_category.items(), key=lambda x: -x[1]):
        print(f"  /{cat}/  : {count}개")


if __name__ == "__main__":
    main()
