import copy
import re
import xml.etree.ElementTree as ET


def _clean_root_background(root):
    """
    Remove root SVG background-color style.
    """
    if "style" in root.attrib:
        style = root.attrib["style"]
        parts = []

        for item in style.split(";"):
            item = item.strip()
            if item.startswith("background-color:"):
                continue
            if item:
                parts.append(item)

        root.attrib["style"] = "; ".join(parts)


def _copy_svg_defs_and_styles(root, new_root):
    """
    Keep defs/style/metadata/desc because glyphs, fonts, symbols,
    gradients, and styles may depend on them.
    """
    for child in list(root):
        tag = child.tag.lower()

        if (
            tag.endswith("defs")
            or tag.endswith("style")
            or tag.endswith("metadata")
            or tag.endswith("desc")
        ):
            new_root.append(copy.deepcopy(child))


def _remove_background_from_circles(group, ns):
    """
    Remove solid fill from wheel background circles only.
    """
    for circle in group.findall(".//svg:circle", ns):
        style = circle.attrib.get("style", "")

        if "fill:" in style:
            parts = []

            for item in style.split(";"):
                item = item.strip()

                if item.startswith("fill:"):
                    parts.append("fill:none")
                elif item:
                    parts.append(item)

            circle.attrib["style"] = "; ".join(parts)

        if "fill" in circle.attrib:
            circle.attrib["fill"] = "none"


def _find_kerykeion_node(root, ns, kr_node_attr, node_names):
    """
    Find Kerykeion SVG group by kr:node attribute.
    """
    for node_name in node_names:
        for g in root.findall(".//svg:g", ns):
            if g.attrib.get(kr_node_attr) == node_name:
                return copy.deepcopy(g)

    return None


def extract_wheel(full_svg, output_svg):
    """
    Birth chart wheel extractor.

    Keeps only:
    kr:node="Full_Wheel"

    Output:
    clean wheel-only SVG.
    """
    try:
        tree = ET.parse(full_svg)
        root = tree.getroot()

        m = re.match(r"\{(.*)\}", root.tag)
        svg_ns = m.group(1) if m else "http://www.w3.org/2000/svg"

        ET.register_namespace("", svg_ns)
        ET.register_namespace("xlink", "http://www.w3.org/1999/xlink")
        ET.register_namespace("kr", "https://www.kerykeion.net/")

        ns = {"svg": svg_ns}
        kr_node_attr = "{https://www.kerykeion.net/}node"

        new_root = ET.Element(root.tag, root.attrib)

        new_root.set("viewBox", "0 0 480 480")
        new_root.set("width", "100%")
        new_root.set("height", "100%")
        new_root.set("preserveAspectRatio", "xMidYMid meet")

        _clean_root_background(new_root)
        _copy_svg_defs_and_styles(root, new_root)

        full_wheel = _find_kerykeion_node(
            root,
            ns,
            kr_node_attr,
            ["Full_Wheel"]
        )

        if full_wheel is None:
            print("Full_Wheel not found")
            return False

        if "transform" in full_wheel.attrib:
            full_wheel.attrib["transform"] = ""

        _remove_background_from_circles(full_wheel, ns)

        new_root.append(full_wheel)

        ET.ElementTree(new_root).write(
            output_svg,
            encoding="utf-8",
            xml_declaration=True
        )

        return True

    except Exception as e:
        print("Birth chart extract error:", e)
        return False


def extract_synastry_wheel(full_svg, output_svg):
    """
    Synastry chart wheel extractor.

    It removes:
    - left details
    - right tables
    - aspect grid table
    - planet position tables

    Keeps only wheel group from Kerykeion SVG.

    Output:
    charts/synastry-chart/person_synastry_chart.svg
    """
    try:
        tree = ET.parse(full_svg)
        root = tree.getroot()

        m = re.match(r"\{(.*)\}", root.tag)
        svg_ns = m.group(1) if m else "http://www.w3.org/2000/svg"

        ET.register_namespace("", svg_ns)
        ET.register_namespace("xlink", "http://www.w3.org/1999/xlink")
        ET.register_namespace("kr", "https://www.kerykeion.net/")

        ns = {"svg": svg_ns}
        kr_node_attr = "{https://www.kerykeion.net/}node"

        new_root = ET.Element(root.tag, root.attrib)

        # Synastry wheel is larger than natal wheel.
        # This canvas keeps only wheel cleanly.
        new_root.set("viewBox", "0 0 600 600")
        new_root.set("width", "100%")
        new_root.set("height", "100%")
        new_root.set("preserveAspectRatio", "xMidYMid meet")

        _clean_root_background(new_root)
        _copy_svg_defs_and_styles(root, new_root)

        # Try multiple possible Kerykeion node names.
        synastry_wheel = _find_kerykeion_node(
            root,
            ns,
            kr_node_attr,
            [
                "Full_Wheel",
                "Synastry_Wheel",
                "Wheel",
                "Chart_Wheel",
            ]
        )

        if synastry_wheel is None:
            print("Synastry wheel group not found")
            return False

        # Remove Kerykeion original page offset.
        if "transform" in synastry_wheel.attrib:
            synastry_wheel.attrib["transform"] = ""

        _remove_background_from_circles(synastry_wheel, ns)

        # Center the extracted wheel.
        wrapper = ET.Element(f"{{{svg_ns}}}g")
        wrapper.set("transform", "translate(60, 40) scale(1.0)")
        wrapper.append(synastry_wheel)

        new_root.append(wrapper)

        ET.ElementTree(new_root).write(
            output_svg,
            encoding="utf-8",
            xml_declaration=True
        )

        return True

    except Exception as e:
        print("Synastry extract error:", e)
        return False

def extract_composite_wheel(full_svg, output_svg):
    """
    Composite chart wheel extractor.

    Removes:
    - title
    - side panels
    - tables
    - aspect lists

    Keeps:
    - wheel
    - zodiac signs
    - planets
    - houses
    - aspect lines
    - colors
    """
    try:
        tree = ET.parse(full_svg)
        root = tree.getroot()

        m = re.match(r"\{(.*)\}", root.tag)
        svg_ns = m.group(1) if m else "http://www.w3.org/2000/svg"

        ET.register_namespace("", svg_ns)
        ET.register_namespace("xlink", "http://www.w3.org/1999/xlink")
        ET.register_namespace("kr", "https://www.kerykeion.net/")

        ns = {"svg": svg_ns}
        kr_node_attr = "{https://www.kerykeion.net/}node"

        new_root = ET.Element(root.tag, root.attrib)

        new_root.set("viewBox", "0 0 640 640")
        new_root.set("width", "100%")
        new_root.set("height", "100%")
        new_root.set("preserveAspectRatio", "xMidYMid meet")

        _clean_root_background(new_root)
        _copy_svg_defs_and_styles(root, new_root)

        composite_wheel = _find_kerykeion_node(
            root,
            ns,
            kr_node_attr,
            [
                "Full_Wheel",
                "Composite_Wheel",
                "Wheel",
                "Chart_Wheel",
            ],
        )

        if composite_wheel is None:
            print("Composite wheel group not found")
            return False

        if "transform" in composite_wheel.attrib:
            composite_wheel.attrib["transform"] = ""

        # IMPORTANT:
        # Do NOT call _remove_background_from_circles()
        # because it removes wheel fills/colors.

        wrapper = ET.Element(f"{{{svg_ns}}}g")
        wrapper.set("transform", "translate(55, 45) scale(1.08)")
        wrapper.append(composite_wheel)

        new_root.append(wrapper)

        ET.ElementTree(new_root).write(
            output_svg,
            encoding="utf-8",
            xml_declaration=True,
        )

        return True

    except Exception as e:
        print("Composite chart extract error:", e)
        return False

def extract_solar_return_wheel(full_svg, output_svg):
    """
    Solar Return / Transit dual-wheel extractor.

    Removes:
    - top title
    - left natal/transit details
    - right planet tables
    - house position comparison
    - bottom transit aspects table

    Keeps:
    - chart wheel
    - zodiac signs
    - planets
    - house lines/numbers
    - aspect lines
    - degree marks
    - wheel colors
    """
    try:
        tree = ET.parse(full_svg)
        root = tree.getroot()

        m = re.match(r"\{(.*)\}", root.tag)
        svg_ns = m.group(1) if m else "http://www.w3.org/2000/svg"

        ET.register_namespace("", svg_ns)
        ET.register_namespace("xlink", "http://www.w3.org/1999/xlink")
        ET.register_namespace("kr", "https://www.kerykeion.net/")

        ns = {"svg": svg_ns}
        kr_node_attr = "{https://www.kerykeion.net/}node"

        new_root = ET.Element(root.tag, root.attrib)

        # Solar Return dual wheel needs more room than natal single wheel
        new_root.set("viewBox", "0 0 620 620")
        new_root.set("width", "100%")
        new_root.set("height", "100%")
        new_root.set("preserveAspectRatio", "xMidYMid meet")

        _clean_root_background(new_root)
        _copy_svg_defs_and_styles(root, new_root)

        solar_wheel = _find_kerykeion_node(
            root,
            ns,
            kr_node_attr,
            [
                "Full_Wheel",
                "Transit_Wheel",
                "Synastry_Wheel",
                "Wheel",
                "Chart_Wheel",
            ],
        )

        if solar_wheel is None:
            print("Solar Return wheel group not found")
            return False

        # Remove original full-page offset
        if "transform" in solar_wheel.attrib:
            solar_wheel.attrib["transform"] = ""

        # Do NOT remove wheel colored fills.
        # We keep zodiac colors and wheel sectors as-is.

        wrapper = ET.Element(f"{{{svg_ns}}}g")

        # Center wheel inside new 620x620 canvas.
        # If wheel is slightly cut, change translate/scale here.
        wrapper.set("transform", "translate(35, 25) scale(1.05)")
        wrapper.append(solar_wheel)

        new_root.append(wrapper)

        ET.ElementTree(new_root).write(
            output_svg,
            encoding="utf-8",
            xml_declaration=True,
        )

        return True

    except Exception as e:
        print("Solar Return chart extract error:", e)
        return False

def extract_lunar_return_wheel(full_svg, output_svg):
    """
    Extract ONLY the Lunar Return wheel from a full Kerykeion dual-wheel SVG.

    Removes:
    - title
    - left natal/transit details
    - right tables
    - bottom aspect table

    Keeps:
    - wheel
    - zodiac signs
    - planets
    - house lines / house numbers
    - aspect lines
    - degree marks
    - wheel colors
    """
    try:
        tree = ET.parse(full_svg)
        root = tree.getroot()

        m = re.match(r"\{(.*)\}", root.tag)
        svg_ns = m.group(1) if m else "http://www.w3.org/2000/svg"

        ET.register_namespace("", svg_ns)
        ET.register_namespace("xlink", "http://www.w3.org/1999/xlink")
        ET.register_namespace("kr", "https://www.kerykeion.net/")

        ns = {"svg": svg_ns}
        kr_node_attr = "{https://www.kerykeion.net/}node"

        def find_kerykeion_node(root_node, candidates):
            for elem in root_node.iter():
                node_name = elem.attrib.get(kr_node_attr, "")
                if node_name in candidates:
                    return copy.deepcopy(elem)
            return None

        # Try common wheel node names used by Kerykeion
        lunar_wheel = find_kerykeion_node(
            root,
            [
                "Transit_Wheel",
                "Full_Wheel",
                "Synastry_Wheel",
                "Wheel",
                "Chart_Wheel",
            ],
        )

        if lunar_wheel is None:
            print("Lunar Return wheel group not found")
            return False

        # Remove original page transform so only wheel stays
        if "transform" in lunar_wheel.attrib:
            lunar_wheel.attrib["transform"] = ""

        # Create new clean SVG root
        new_root = ET.Element(root.tag, root.attrib)
        new_root.set("viewBox", "0 0 640 640")
        new_root.set("width", "100%")
        new_root.set("height", "100%")
        new_root.set("preserveAspectRatio", "xMidYMid meet")

        # Optional clean background helper if you already use it
        try:
            _clean_root_background(new_root)
        except Exception:
            pass

        # Copy defs/style if your project already has helper
        try:
            _copy_svg_defs_and_styles(root, new_root)
        except Exception:
            pass

        # Wrap and center the wheel
        wrapper = ET.Element(f"{{{svg_ns}}}g")

        # IMPORTANT:
        # Adjust translate / scale if wheel appears too high/low.
        wrapper.set("transform", "translate(55, 45) scale(1.08)")
        wrapper.append(lunar_wheel)

        new_root.append(wrapper)

        ET.ElementTree(new_root).write(
            output_svg,
            encoding="utf-8",
            xml_declaration=True,
        )

        return True

    except Exception as e:
        print("Lunar Return chart extract error:", e)
        return False

def extract_transit_wheel(full_svg, output_svg):
    """
    Transit chart wheel extractor.

    Removes:
    - title
    - left details
    - right planet tables
    - bottom aspect tables

    Keeps only:
    - transit/natal wheel
    - zodiac signs
    - planets
    - houses
    - aspect lines
    - degree marks
    - wheel colors

    Usage:
        extract_transit_wheel(
            "charts/transit/full_transit_chart.svg",
            "charts/transit/transit_chart_wheel.svg"
        )
    """
    try:
        tree = ET.parse(full_svg)
        root = tree.getroot()

        m = re.match(r"\{(.*)\}", root.tag)
        svg_ns = m.group(1) if m else "http://www.w3.org/2000/svg"

        ET.register_namespace("", svg_ns)
        ET.register_namespace("xlink", "http://www.w3.org/1999/xlink")
        ET.register_namespace("kr", "https://www.kerykeion.net/")

        ns = {"svg": svg_ns}
        kr_node_attr = "{https://www.kerykeion.net/}node"

        new_root = ET.Element(root.tag, root.attrib)

        # Transit dual wheel needs bigger canvas than natal wheel
        new_root.set("viewBox", "0 0 640 640")
        new_root.set("width", "100%")
        new_root.set("height", "100%")
        new_root.set("preserveAspectRatio", "xMidYMid meet")

        _clean_root_background(new_root)
        _copy_svg_defs_and_styles(root, new_root)

        transit_wheel = _find_kerykeion_node(
            root,
            ns,
            kr_node_attr,
            [
                "Transit_Wheel",
                "Full_Wheel",
                "Synastry_Wheel",
                "Wheel",
                "Chart_Wheel",
            ],
        )

        if transit_wheel is None:
            print("Transit wheel group not found")
            return False

        # Remove original Kerykeion page position
        if "transform" in transit_wheel.attrib:
            transit_wheel.attrib["transform"] = ""

        # IMPORTANT:
        # Do not remove circle fills here.
        # For transit chart, we want to keep wheel colors.

        wrapper = ET.Element(f"{{{svg_ns}}}g")

        # Adjust this if wheel is slightly cut:
        # translate(x, y) moves wheel
        # scale() controls size
        wrapper.set("transform", "translate(55, 45) scale(1.08)")
        wrapper.append(transit_wheel)

        new_root.append(wrapper)

        ET.ElementTree(new_root).write(
            output_svg,
            encoding="utf-8",
            xml_declaration=True,
        )

        return True

    except Exception as e:
        print("Transit chart extract error:", e)
        return False


def extract_progressed_wheel(full_svg, output_svg):
    """
    Progressed chart wheel extractor.

    Removes:
    - title
    - left natal/transit details
    - right planet tables
    - bottom aspect tables

    Keeps only:
    - progressed/natal wheel
    - zodiac signs
    - planets
    - houses
    - aspect lines
    - degree marks
    - wheel colors
    """
    try:
        tree = ET.parse(full_svg)
        root = tree.getroot()

        m = re.match(r"\{(.*)\}", root.tag)
        svg_ns = m.group(1) if m else "http://www.w3.org/2000/svg"

        ET.register_namespace("", svg_ns)
        ET.register_namespace("xlink", "http://www.w3.org/1999/xlink")
        ET.register_namespace("kr", "https://www.kerykeion.net/")

        ns = {"svg": svg_ns}
        kr_node_attr = "{https://www.kerykeion.net/}node"

        new_root = ET.Element(root.tag, root.attrib)
        new_root.set("viewBox", "0 0 640 640")
        new_root.set("width", "100%")
        new_root.set("height", "100%")
        new_root.set("preserveAspectRatio", "xMidYMid meet")

        _clean_root_background(new_root)
        _copy_svg_defs_and_styles(root, new_root)

        progressed_wheel = _find_kerykeion_node(
            root,
            ns,
            kr_node_attr,
            [
                "Transit_Wheel",
                "Full_Wheel",
                "Synastry_Wheel",
                "Wheel",
                "Chart_Wheel",
            ],
        )

        if progressed_wheel is None:
            print("Progressed wheel group not found")
            return False

        if "transform" in progressed_wheel.attrib:
            progressed_wheel.attrib["transform"] = ""

        wrapper = ET.Element(f"{{{svg_ns}}}g")
        wrapper.set("transform", "translate(55, 45) scale(1.08)")
        wrapper.append(progressed_wheel)

        new_root.append(wrapper)

        ET.ElementTree(new_root).write(
            output_svg,
            encoding="utf-8",
            xml_declaration=True,
        )

        return True

    except Exception as e:
        print("Progressed chart extract error:", e)
        return False