import requests
import json
import os
import re


# 🔑 Remplace avec ton token et ID Figma
FIGMA_TOKEN = "figd_GZnECchviu8iwsHghZml2QBv73EhrBD6Pz8OGLx6"
FILE_ID = "3xVCA7ReSS2AaKrwATiuDn"

HEADERS = {
    "X-Figma-Token": FIGMA_TOKEN
}
vector_node_ids = {}  # node_id: name

# Crée un dossier "images" s'il n'existe pas
os.makedirs("images", exist_ok=True)

# 🎨 Styles CSS personnalisés
custom_css = """
html {
    height: -webkit-fill-available;
}
body {
    height: -webkit-fill-available;
    position: relative;
    margin: 0;
    font-family: sans-serif;
    background-color: black;
}
.elementor{
    top:0px!important;
    left:0px!important;
}

button{
    cursor: pointer;
}

"""

# 📥 Dictionnaire global pour stocker les nodes image
image_nodes = {}  # node_id: name


def get_figma_file(file_id):
    url = f"https://api.figma.com/v1/files/{file_id}"
    res = requests.get(url, headers=HEADERS)
    return res.json()


def get_image_urls(file_id, node_ids):
    url = f"https://api.figma.com/v1/images/{file_id}?ids={','.join(node_ids)}&format=png"
    res = requests.get(url, headers=HEADERS)
    return res.json().get("images", {})


def css_from_node(node, parent_bbox=None):
    # Ignorer les nodes invisibles
    if node.get("visible", True) is False:
        return ""

    style = ""

    # Positionnement absolu
    if "absoluteBoundingBox" in node:
        bbox = node["absoluteBoundingBox"]
        if node["id"] == FIRST_NODE_ID:
            # Pas de top/left, mais position: relative
            style += "position:relative;"
            style += f"width:{int(round(bbox['width']))}px; height:{int(round(bbox['height']))}px;"
        else:
            rel_x = bbox['x'] - parent_bbox['x'] if parent_bbox else bbox['x']
            rel_y = bbox['y'] - parent_bbox['y'] if parent_bbox else bbox['y']
            style += f"position:absolute; top:{rel_y}px; left:{rel_x}px;"
            style += f"width:{int(round(bbox['width']))}px; height:{int(round(bbox['height']))}px;"


    # Fills : couleurs ou images
    fills = node.get("fills", [])
    # On garde uniquement les fills visibles
    visible_fills = [f for f in fills if f.get("visible", True)]

    if visible_fills:
        fill = visible_fills[0]
        if fill["type"] == "SOLID":
            color = fill["color"]
            r, g, b = int(color["r"] * 255), int(color["g"] * 255), int(color["b"] * 255)

            if node["type"] == "TEXT":
                style += f"color: rgb({r}, {g}, {b});"
            elif node["id"] in vector_node_ids:
                style += f"color: rgb({r}, {g}, {b});"
            else:
                style += f"background-color: rgb({r}, {g}, {b});"

    # Contours (strokes) — mais PAS pour les nodes rendus comme <img>
    is_image_node = node["id"] in image_nodes or node["id"] in vector_node_ids

    if not is_image_node:
        strokes = node.get("strokes", [])
        stroke_weight = node.get("strokeWeight", 0)

        if strokes and stroke_weight > 0:
            stroke = strokes[0]
            if stroke["type"] == "SOLID":
                c = stroke["color"]
                r, g, b = int(c["r"] * 255), int(c["g"] * 255), int(c["b"] * 255)
                style += f"border: {stroke_weight}px solid rgb({r}, {g}, {b});"

    # Coins arrondis
    if "cornerRadius" in node:
        style += f"border-radius: {node['cornerRadius']}px;"

    # Style texte
    if node["type"] == "TEXT" and "style" in node:
        text_style = node["style"]
        font_family = text_style.get("fontFamily", "sans-serif").strip(' "\'')
        font_size = text_style.get("fontSize", 14)
        font_weight = text_style.get("fontWeight", 400)

        if isinstance(font_weight, str):
            mapping = {
                "Regular": 400,
                "Bold": 700,
                "Light": 300,
                "Medium": 500,
                "Semibold": 600,
                "Black": 900,
            }
            font_weight = mapping.get(font_weight, 400)

        # Si le nom de la font contient un espace, on entoure de guillemets doubles
        if " " in font_family:
            font_family = f'"{font_family}"'

        style += f"font-family: {font_family};"
        style += f"font-size: {font_size}px;"
        style += f"font-weight: {font_weight};"

        if text_style.get("textAlignHorizontal"):
            style += f"text-align: {text_style['textAlignHorizontal'].lower()};"

    return style



def download_images(image_urls, folder="images"):
    for node_id, url in image_urls.items():
        res = requests.get(url)
        if res.status_code == 200:
            safe_name = sanitize_filename(node_id)
            filename = os.path.join(folder, f"{safe_name}.png")
            with open(filename, "wb") as f:
                f.write(res.content)
            image_nodes[node_id] = filename.replace("\\", "/")


def sanitize_filename(name):
    return re.sub(r'[\\/*?:"<>|;]', "_", name)


def download_vectors(vector_urls, folder="images"):
    for node_id, url in vector_urls.items():
        if not url:
            print(f"❌ Pas d'URL pour le node {node_id}, ignoré.")
            continue
        res = requests.get(url)
        if res.status_code == 200:
            safe_name = sanitize_filename(node_id)
            filename = os.path.join(folder, f"{safe_name}.svg")
            with open(filename, "wb") as f:
                f.write(res.content)
            vector_node_ids[node_id] = filename.replace("\\", "/")

def get_vector_urls(file_id, node_ids, format="svg"):
    url = f"https://api.figma.com/v1/images/{file_id}?ids={','.join(node_ids)}&format={format}"
    res = requests.get(url, headers=HEADERS)
    data = res.json()
    if not data.get("images"):
        print("⚠️ Aucune image vectorielle reçue :", data)
    return data.get("images", {})

def sanitize_class_name(name):
    """Nettoie le nom du node pour l'utiliser comme nom de classe CSS."""
    name = re.sub(r'\s+', '-', name)  # remplace les espaces par des tirets
    name = re.sub(r'[^a-zA-Z0-9\-_]', '', name)  # supprime les caractères spéciaux
    return name.lower()




def html_from_node(node, level=0, parent_bbox=None):
    # Ne pas traiter si node est invisible
    if node.get("visible", True) is False:
        return ""

    indent = "  " * level
    css = css_from_node(node, parent_bbox)
    name = node.get("name", "").lower()

    # 🖼 Image (bitmap)
    if node["id"] in image_nodes:
        image_url = image_nodes[node["id"]]
        return f"{indent}<img style='{css}' src='{image_url}' alt='{name}' />\n"

    # 🧩 Icônes vectorielles (FRAME, VECTOR, COMPONENT) sans enfants
    if node["type"] in ["VECTOR", "COMPONENT", "INSTANCE", "FRAME"] and not node.get("children"):
        if node["id"] in vector_node_ids:
            vector_path = vector_node_ids[node["id"]]
            return f"{indent}<img style='{css}' src='{vector_path}' alt='{name}' />\n"
        else:
            vector_node_ids[node["id"]] = node["name"]
            return ""

    # 📝 Texte
    if node["type"] == "TEXT":
        content = node.get("characters", "")
        class_attr = " class='text'"
        return f"{indent}<div{class_attr} style='{css}'>{content}</div>\n"

    # 📦 Conteneur générique (div ou bouton si "bouton" dans le nom)
    tag = "button" if name.strip().lower() == "button" else "div"
    # Ancienne ligne :
    # class_attr = " class='elementor'" if node["id"] == FIRST_NODE_ID else ""

    # Nouvelle version :
    classes = []
    if node["id"] == FIRST_NODE_ID:
        classes.append("elementor")
    if "name" in node and node["name"]:
        classes.append(sanitize_class_name(node["name"]))
    class_attr = f" class='{' '.join(classes)}'" if classes else ""
    html = f"{indent}<{tag}{class_attr} style='{css}'>\n"

    for child in node.get("children", []):
        # Récursivement appeler html_from_node, en sautant les invisibles
        html += html_from_node(child, level + 1, node.get("absoluteBoundingBox"))
    html += f"{indent}</{tag}>\n"
    return html

def increment_width_on_text_nodes(node):
    """Augmente de +1px la largeur des nœuds TEXT uniquement."""
    if node.get("type") == "TEXT" and "absoluteBoundingBox" in node:
        old_width = node["absoluteBoundingBox"]["width"]
        node["absoluteBoundingBox"]["width"] = float(old_width) + 1
        #print(f"✅ Width +1px on TEXT node: {old_width} → {node['absoluteBoundingBox']['width']} ({node.get('name')})")

    # Traitement récursif pour tous les enfants
    for child in node.get("children", []):
        increment_width_on_text_nodes(child)


def main():
    global FIRST_NODE_ID
    data = get_figma_file(FILE_ID)
    document = data["document"]
    page = document["children"][0]  # Première page

    if page["children"]:
        FIRST_NODE_ID = page["children"][0]["id"]  # <- on récupère l'ID du premier node Figma

    increment_width_on_text_nodes(page)

    # 2. Première passe — juste pour collecter les node_ids
    _ = html_from_node(page)

    # 3. Récupération des URLs des images
    if image_nodes:
        image_urls = get_image_urls(FILE_ID, list(image_nodes.keys()))
        download_images(image_urls)


    # 4. Récupération des URLs vectorielles (SVG)
    if vector_node_ids:
        vector_urls = get_vector_urls(FILE_ID, list(vector_node_ids.keys()), format="svg")
        download_vectors(vector_urls)


    # ✅ 5. Deuxième passe — regénérer le HTML avec les URLs à jour
    html = html_from_node(page)

    # 6. Génération du fichier HTML final
    with open("figma_output.html", "w", encoding="utf-8") as f:
        f.write("<!DOCTYPE html>\n<html>\n<head>\n<meta charset='utf-8'>\n<title>Figma Export</title>\n")
        f.write("<style>\n* { box-sizing: border-box; }\n")
        f.write(custom_css)
        f.write("\n</style>\n</head>\n<body>\n")
        f.write(html)
        f.write("</body>\n</html>")

    print("✅ Fichier HTML avec images et styles texte généré : figma_output.html")


if __name__ == "__main__":
    main()
