import json
import re
from pathlib import Path
from subprocess import run

import html2text
from bs4 import BeautifulSoup
from huggingface_hub import HfApi

api = HfApi()


def obj_divs_to_markdown(html: str) -> str:
    soup = BeautifulSoup(html, "html.parser")
    objs = soup.select("div.obj")
    if not objs:
        return ""
    obj_html = "\n".join(div.decode_contents() for div in objs)
    md_text = html2text.html2text(obj_html).replace(
        "![](https://raw.githubusercontent.com/gradio-\napp/gradio/main/js/_website/src/lib/assets/img/anchor.svg)",
        "",
    )
    md_text = md_text.replace("🔗\n", "")
    md_text = re.sub(r"###\s*Guides[\s\S]*\Z", "", md_text)
    return md_text


MAX_LEN = 2000
OVERLAP = 200
H3_PATTERN = re.compile(r"^(###\s+.+)$", re.M)
H2_PATTERN = re.compile(r"^(##\s+.+)$", re.M)


def split_with_overlap(text: str, max_len: int = MAX_LEN, overlap: int = OVERLAP):
    """Yield slices of text each ≤ max_len, adding `overlap` chars of context."""
    start = 0
    while start < len(text):
        end = start + max_len
        yield text[start:end]
        start = end - overlap


def markdown_to_docs(
    markdown: str,
    *,
    page_url: str,
    page_title: str,
    docs: list,
    split_by_h2: bool = False,
):
    """
    Convert a Markdown string into an array of doc dictionaries, each ≤ 2000 chars.
    Sections are split on h_type headings; oversized sections are broken up with overlap.
    """

    H_PATTERN = H2_PATTERN if split_by_h2 else H3_PATTERN

    headings = [
        (m.start(), m.end(), m.group(1).lstrip("# ").strip())
        for m in H_PATTERN.finditer(markdown)
    ]
    headings.append((len(markdown), len(markdown), None))

    for idx in range(len(headings) - 1):
        start, h_end, h_text = headings[idx]
        next_start, _, _ = headings[idx + 1]
        section_body = markdown[h_end:next_start].lstrip()

        chunks = (
            [section_body]
            if len(section_body) <= MAX_LEN
            else list(split_with_overlap(section_body))
        )

        for i, chunk in enumerate(chunks):
            heading = h_text
            docs.append(
                dict(
                    text=chunk.replace("# ", "").replace("#", ""),
                    heading1=heading,
                    source_page_url=page_url,
                    source_page_title=page_title,
                )
            )
    return docs


if __name__ == "__main__":
    ROOT = Path(__file__).parent.parent
    DOCS_DIR = (ROOT / "js/_website/build/docs").resolve()
    GUIDES_DIR = (ROOT / "guides").resolve()
    docs = []
    for dir in DOCS_DIR.iterdir():
        if dir.is_dir():
            for file in dir.iterdir():
                if file.is_file() and file.suffix == ".html":
                    html = file.read_text(encoding="utf-8")
                    markdown = obj_divs_to_markdown(html)

                    page_url = f"https://gradio.app/docs/{dir.name}/{file.name.replace('.html', '')}"
                    page_title = f"{dir.name.replace('-', ' ').title()} - {file.name.replace('.html', '').replace('-', ' ').title()} Docs"

                    docs = markdown_to_docs(
                        markdown, page_url=page_url, page_title=page_title, docs=docs
                    )

    for dir in GUIDES_DIR.iterdir():
        if dir.is_dir() and dir.name not in ["assets", "cn"]:
            for file in dir.iterdir():
                if file.is_file() and file.suffix == ".md":
                    markdown = file.read_text(encoding="utf-8")
                    page_name = file.name.replace(".md", "")
                    page_name = re.sub(r"^\d+_", "", page_name)
                    dir_name = dir.name.replace("-", " ")
                    dir_name = re.sub(r"^\d+_", "", dir_name)
                    page_url = f"https://gradio.app/guides/{page_name}"
                    page_title = f"{dir_name.title()} - {page_name.replace('-', ' ').title()} Guide"

                    docs = markdown_to_docs(
                        markdown,
                        page_url=page_url,
                        page_title=page_title,
                        docs=docs,
                        split_by_h2=True,
                    )

    print(f"Generated {len(docs)} chunks.")

    with open((ROOT / "scripts/docs.json").resolve(), "w") as f:
        json.dump(docs, f)

    result = run(
        ["git", "log", "-1", "--pretty=format:%H|%s"],
        capture_output=True,
        text=True,
        check=True,
    )
    sha, subject = result.stdout.strip().split("|", 1)
    commit_hash = sha[:7]
    commit_message = subject if len(subject) <= 30 else subject[:30] + "..."

    try:
        commit_info = api.upload_file(
            path_or_fileobj=(ROOT / "scripts/docs.json").resolve(),
            path_in_repo="docs.json",
            repo_id="gradio/docs",
            repo_type="dataset",
            commit_message=f"Changes from: {commit_hash} '{commit_message}'",
        )
        print("✅  docs.json uploaded")
    except Exception as e:
        print(f"❌  Error uploading docs.json: {e}")
