"""
Professional PPTX Generation — dark-themed, branded presentations.
"""

import io
import re
from pptx import Presentation
from pptx.util import Inches, Pt, Emu
from pptx.dml.color import RGBColor
from pptx.enum.text import PP_ALIGN, MSO_ANCHOR
from pptx.enum.shapes import MSO_SHAPE

BG = RGBColor(0x0F, 0x0F, 0x1A)
BG_LIGHT = RGBColor(0x16, 0x16, 0x2A)
WHITE = RGBColor(0xFF, 0xFF, 0xFF)
BODY = RGBColor(0xE2, 0xE2, 0xEA)
MUTED = RGBColor(0x6B, 0x6B, 0x8A)
ACCENT = RGBColor(0xE5, 0x3E, 0x3E)
CODE_BG = RGBColor(0x1A, 0x1B, 0x26)

SLIDE_W = Inches(13.333)
SLIDE_H = Inches(7.5)
FONT = "Calibri"
MONO = "Consolas"


def generate_pptx(markdown: str, title: str = "Presentation") -> bytes:
    prs = Presentation()
    prs.slide_width = SLIDE_W
    prs.slide_height = SLIDE_H

    slides_data = _parse_slides(markdown, title)

    for sd in slides_data:
        if sd["type"] == "title":
            _add_title_slide(prs, sd)
        elif sd["type"] == "code":
            _add_code_slide(prs, sd)
        else:
            _add_content_slide(prs, sd)

    _add_end_slide(prs)

    buf = io.BytesIO()
    prs.save(buf)
    buf.seek(0)
    return buf.getvalue()


def _set_bg(slide, color=BG):
    bg = slide.background
    fill = bg.fill
    fill.solid()
    fill.fore_color.rgb = color


def _add_accent_bar(slide):
    shape = slide.shapes.add_shape(MSO_SHAPE.RECTANGLE, 0, 0, Inches(0.07), SLIDE_H)
    shape.fill.solid()
    shape.fill.fore_color.rgb = ACCENT
    shape.line.fill.background()


def _add_accent_line(slide, left, top, width):
    shape = slide.shapes.add_shape(MSO_SHAPE.RECTANGLE, left, top, width, Pt(3))
    shape.fill.solid()
    shape.fill.fore_color.rgb = ACCENT
    shape.line.fill.background()


def _add_textbox(slide, left, top, width, height):
    return slide.shapes.add_textbox(left, top, width, height)


def _set_para(para, text, size, color, bold=False, font=FONT, align=PP_ALIGN.LEFT):
    para.text = _clean_md(text)
    para.font.size = Pt(size)
    para.font.color.rgb = color
    para.font.bold = bold
    para.font.name = font
    para.alignment = align
    para.space_after = Pt(4)
    para.space_before = Pt(2)


def _clean_md(text):
    text = re.sub(r'\*\*(.+?)\*\*', r'\1', text)
    text = re.sub(r'\*(.+?)\*', r'\1', text)
    text = re.sub(r'`(.+?)`', r'\1', text)
    text = re.sub(r'\[([^\]]+)\]\([^)]+\)', r'\1', text)
    return text.strip()


def _add_title_slide(prs, sd):
    slide = prs.slides.add_slide(prs.slide_layouts[6])
    _set_bg(slide)

    tb = _add_textbox(slide, Inches(1.5), Inches(2), Inches(10), Inches(1.5))
    tf = tb.text_frame
    tf.word_wrap = True
    _set_para(tf.paragraphs[0], sd["title"], 40, WHITE, bold=True, align=PP_ALIGN.CENTER)

    _add_accent_line(slide, Inches(5), Inches(3.6), Inches(3.333))

    if sd.get("subtitle"):
        tb2 = _add_textbox(slide, Inches(2), Inches(4), Inches(9), Inches(1))
        tf2 = tb2.text_frame
        tf2.word_wrap = True
        _set_para(tf2.paragraphs[0], sd["subtitle"], 20, MUTED, align=PP_ALIGN.CENTER)

    tb3 = _add_textbox(slide, Inches(4), Inches(6.5), Inches(5), Inches(0.5))
    tf3 = tb3.text_frame
    _set_para(tf3.paragraphs[0], "Generated by Son of Anton", 10, MUTED, align=PP_ALIGN.CENTER)


def _add_content_slide(prs, sd):
    slide = prs.slides.add_slide(prs.slide_layouts[6])
    _set_bg(slide)
    _add_accent_bar(slide)

    if sd.get("title"):
        tb = _add_textbox(slide, Inches(0.8), Inches(0.4), Inches(11.5), Inches(0.8))
        tf = tb.text_frame
        tf.word_wrap = True
        _set_para(tf.paragraphs[0], sd["title"], 28, WHITE, bold=True)
        _add_accent_line(slide, Inches(0.8), Inches(1.25), Inches(2))

    top = Inches(1.6) if sd.get("title") else Inches(0.5)
    tb = _add_textbox(slide, Inches(0.8), top, Inches(11.5), SLIDE_H - top - Inches(0.5))
    tf = tb.text_frame
    tf.word_wrap = True

    first = True
    for item in sd.get("items", []):
        if first:
            p = tf.paragraphs[0]
            first = False
        else:
            p = tf.add_paragraph()

        itype = item["type"]
        text = item["text"]

        if itype == "subheading":
            _set_para(p, text, 22, ACCENT, bold=True)
            p.space_before = Pt(16)
        elif itype == "bullet":
            _set_para(p, f"  •  {text}", 16, BODY)
            p.space_before = Pt(6)
        elif itype == "numbered":
            _set_para(p, f"  {item.get('num', '•')}  {text}", 16, BODY)
            p.space_before = Pt(6)
        elif itype == "code":
            _set_para(p, text[:500], 12, RGBColor(0xA0, 0xE0, 0xA0), font=MONO)
            p.space_before = Pt(8)
        else:
            _set_para(p, text, 16, BODY)
            p.space_before = Pt(6)


def _add_code_slide(prs, sd):
    slide = prs.slides.add_slide(prs.slide_layouts[6])
    _set_bg(slide)
    _add_accent_bar(slide)

    if sd.get("title"):
        tb = _add_textbox(slide, Inches(0.8), Inches(0.4), Inches(11.5), Inches(0.7))
        tf = tb.text_frame
        _set_para(tf.paragraphs[0], sd["title"], 24, WHITE, bold=True)

    code_shape = slide.shapes.add_shape(
        MSO_SHAPE.ROUNDED_RECTANGLE,
        Inches(0.8), Inches(1.3), Inches(11.5), Inches(5.5),
    )
    code_shape.fill.solid()
    code_shape.fill.fore_color.rgb = CODE_BG
    code_shape.line.color.rgb = RGBColor(0x2A, 0x2A, 0x4A)
    code_shape.line.width = Pt(1)

    tf = code_shape.text_frame
    tf.word_wrap = True
    tf.margin_left = Inches(0.3)
    tf.margin_top = Inches(0.2)

    code = sd.get("code", "")
    lines = code.split("\n")[:35]
    for i, line in enumerate(lines):
        p = tf.paragraphs[0] if i == 0 else tf.add_paragraph()
        p.text = line
        p.font.size = Pt(11)
        p.font.name = MONO
        p.font.color.rgb = RGBColor(0xC0, 0xC0, 0xD0)
        p.space_after = Pt(1)
        p.space_before = Pt(1)


def _add_end_slide(prs):
    slide = prs.slides.add_slide(prs.slide_layouts[6])
    _set_bg(slide)
    tb = _add_textbox(slide, Inches(2), Inches(2.5), Inches(9), Inches(1.5))
    tf = tb.text_frame
    tf.word_wrap = True
    _set_para(tf.paragraphs[0], "Thank You", 44, WHITE, bold=True, align=PP_ALIGN.CENTER)
    _add_accent_line(slide, Inches(5.5), Inches(4), Inches(2.333))
    tb2 = _add_textbox(slide, Inches(3), Inches(4.5), Inches(7), Inches(0.5))
    tf2 = tb2.text_frame
    _set_para(tf2.paragraphs[0], "Powered by Son of Anton 🔥", 14, MUTED, align=PP_ALIGN.CENTER)


def _parse_slides(markdown: str, default_title: str = "Presentation") -> list[dict]:
    slides = []
    in_code = False
    code_lang = ""
    code_lines = []
    current = None
    num_counter = 0

    for line in markdown.split("\n"):
        if line.strip().startswith("```"):
            if in_code:
                code_text = "\n".join(code_lines)
                if len(code_text.strip()) > 10:
                    slides.append({
                        "type": "code",
                        "title": f"Code ({code_lang})" if code_lang else "Code",
                        "code": code_text,
                    })
                in_code = False
                code_lines = []
                code_lang = ""
            else:
                in_code = True
                raw = line.strip()[3:]
                code_lang = raw.split(":")[0].split()[0] if raw else ""
            continue

        if in_code:
            code_lines.append(line)
            continue

        stripped = line.strip()
        if not stripped:
            continue
        if stripped == "---":
            current = None
            num_counter = 0
            continue

        if stripped.startswith("# ") and not stripped.startswith("## "):
            title_text = stripped[2:].strip()
            subtitle = ""
            current = {"type": "title", "title": title_text, "subtitle": subtitle, "items": []}
            slides.append(current)
            num_counter = 0
            continue

        if stripped.startswith("## "):
            current = {"type": "content", "title": stripped[3:].strip(), "items": []}
            slides.append(current)
            num_counter = 0
            continue

        if current is None:
            current = {"type": "content", "title": "", "items": []}
            slides.append(current)

        if current["type"] == "title" and not current.get("subtitle") and len(current.get("items", [])) == 0:
            current["subtitle"] = _clean_md(stripped)
            continue

        if stripped.startswith("### "):
            current["items"].append({"type": "subheading", "text": stripped[4:].strip()})
        elif re.match(r'^[-*+]\s', stripped):
            current["items"].append({"type": "bullet", "text": re.sub(r'^[-*+]\s+', '', stripped)})
        elif re.match(r'^\d+[.)]\s', stripped):
            num_counter += 1
            current["items"].append({"type": "numbered", "text": re.sub(r'^\d+[.)]\s+', '', stripped), "num": str(num_counter)})
        else:
            current["items"].append({"type": "text", "text": stripped})

        if len(current.get("items", [])) > 10:
            current = None

    if not slides:
        slides.append({"type": "title", "title": default_title, "subtitle": "", "items": []})
        chunks = [s.strip() for s in markdown.split("\n\n") if s.strip()]
        for chunk in chunks[:10]:
            slides.append({
                "type": "content", "title": "",
                "items": [{"type": "text", "text": chunk[:300]}],
            })

    return slides