import colorsys

from aura_core.module_contract import aura_module, ModuleResult
from aura_core.types import ThemeConfig, ThemeColors, ThemeInput


def hex_to_rgb(hex_color: str) -> str:
    hex_color = hex_color.lstrip('#')
    if len(hex_color) == 3:
        hex_color = ''.join(c * 2 for c in hex_color)
    try:
        r, g, b = (int(hex_color[i:i+2], 16) for i in (0, 2, 4))
        return f"{r},{g},{b}"
    except (ValueError, IndexError):
        return "128,128,128"


def _rgb_to_hex(r: int, g: int, b: int) -> str:
    return f"#{r:02x}{g:02x}{b:02x}"


def _parse_rgb(hex_color: str) -> tuple:
    hex_color = hex_color.lstrip('#')
    if len(hex_color) == 3:
        hex_color = ''.join(c * 2 for c in hex_color)
    try:
        return tuple(int(hex_color[i:i+2], 16) for i in (0, 2, 4))
    except (ValueError, IndexError):
        return (128, 128, 128)


def _adjust_lightness(hex_color: str, factor: float) -> str:
    r, g, b = _parse_rgb(hex_color)
    h, l, s = colorsys.rgb_to_hls(r / 255, g / 255, b / 255)
    l = max(0, min(1, l * factor))
    r2, g2, b2 = colorsys.hls_to_rgb(h, l, s)
    return _rgb_to_hex(int(r2 * 255), int(g2 * 255), int(b2 * 255))


def _get_contrast_text(hex_color: str) -> str:
    r, g, b = _parse_rgb(hex_color)
    luminance = (0.299 * r + 0.587 * g + 0.114 * b) / 255
    return "#ffffff" if luminance < 0.5 else "#1a1a2e"


MOOD_PROFILES = {
    "professional": {
        "card_style": "elevated",
        "section_header_style": "underline",
        "background_pattern": "subtle-grid",
        "divider_style": "clean-line",
        "border_radius": "12px",
        "shadow_intensity": "medium",
        "font_weight_heading": "800",
        "letter_spacing": "-0.02em",
    },
    "creative": {
        "card_style": "glass",
        "section_header_style": "gradient-accent",
        "background_pattern": "flowing-waves",
        "divider_style": "wave",
        "border_radius": "20px",
        "shadow_intensity": "soft",
        "font_weight_heading": "900",
        "letter_spacing": "-0.03em",
    },
    "minimal": {
        "card_style": "bordered",
        "section_header_style": "simple",
        "background_pattern": "none",
        "divider_style": "thin-line",
        "border_radius": "8px",
        "shadow_intensity": "none",
        "font_weight_heading": "700",
        "letter_spacing": "-0.01em",
    },
    "bold": {
        "card_style": "gradient",
        "section_header_style": "badge-accent",
        "background_pattern": "geometric",
        "divider_style": "diagonal",
        "border_radius": "16px",
        "shadow_intensity": "strong",
        "font_weight_heading": "900",
        "letter_spacing": "-0.03em",
    },
    "warm": {
        "card_style": "elevated",
        "section_header_style": "decorative",
        "background_pattern": "organic",
        "divider_style": "curve",
        "border_radius": "16px",
        "shadow_intensity": "medium",
        "font_weight_heading": "800",
        "letter_spacing": "-0.02em",
    },
}

SVG_DIVIDERS = {
    "wave": '<svg viewBox="0 0 1440 80" preserveAspectRatio="none" class="w-full"><path d="M0,40 C360,80 720,0 1080,40 C1260,60 1380,50 1440,40 L1440,80 L0,80 Z" fill="{color}"/></svg>',
    "wave-inverse": '<svg viewBox="0 0 1440 80" preserveAspectRatio="none" class="w-full"><path d="M0,40 C360,0 720,80 1080,40 C1260,20 1380,30 1440,40 L1440,80 L0,80 Z" fill="{color}"/></svg>',
    "diagonal": '<svg viewBox="0 0 1440 60" preserveAspectRatio="none" class="w-full"><polygon points="0,60 1440,0 1440,60" fill="{color}"/></svg>',
    "curve": '<svg viewBox="0 0 1440 80" preserveAspectRatio="none" class="w-full"><path d="M0,80 Q720,0 1440,80 Z" fill="{color}"/></svg>',
    "clean-line": '<div class="w-full flex justify-center py-2"><div style="width:80px;height:3px;border-radius:2px;background:{color}"></div></div>',
    "thin-line": '<div class="w-full flex justify-center py-1"><div style="width:60px;height:1px;background:{color}"></div></div>',
}

BACKGROUND_PATTERNS = {
    "subtle-grid": "background-image:linear-gradient({line_color} 1px, transparent 1px),linear-gradient(90deg,{line_color} 1px, transparent 1px);background-size:40px 40px;",
    "flowing-waves": "background-image:url(\"data:image/svg+xml,%3Csvg width='100' height='20' viewBox='0 0 100 20' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath d='M21.184 20c.357-.13.72-.264.888-.14 1.24.19 2.491.14 2.718-.07 2.258-2.064 4.744-2.36 7.037-2.243 2.3.12 4.466.61 5.893 1.166.327.128.334.088.8-.115.36-.157.862-.375 1.69-.614.828-.24 1.848-.44 3.162-.54a47.2 47.2 0 012.67-.122c.92-.015 1.892.01 2.924.085l.143.015.13.018c1.792.262 3.07.755 4.032 1.234a8.258 8.258 0 011.47.94 4.728 4.728 0 01.49.447c.09.098.133.166.133.186 0-.107-.046-.22-.27-.387a5.7 5.7 0 00-.533-.39c-1.138-.73-3.49-1.51-6.55-1.342-1.6.088-3.373.353-5.338.881-1.16.312-1.717.532-2.06.678-.172.073-.28.12-.383.128-.105.007-.238-.024-.57-.143-1.485-.533-3.746-1.026-6.163-1.15-2.425-.126-5.026.165-7.4 2.326-.082.074-.393.146-1.2-.02a8.27 8.27 0 00-.542-.08L20 20h1.184z' fill='{line_color}' fill-rule='evenodd'/%3E%3C/svg%3E\");",
    "geometric": "background-image:url(\"data:image/svg+xml,%3Csvg width='60' height='60' viewBox='0 0 60 60' xmlns='http://www.w3.org/2000/svg'%3E%3Cg fill='none' fill-rule='evenodd'%3E%3Cg fill='{line_color_enc}' fill-opacity='0.4'%3E%3Cpath d='M36 34v-4h-2v4h-4v2h4v4h2v-4h4v-2h-4zm0-30V0h-2v4h-4v2h4v4h2V6h4V4h-4zM6 34v-4H4v4H0v2h4v4h2v-4h4v-2H6zM6 4V0H4v4H0v2h4v4h2V6h4V4H6z'/%3E%3C/g%3E%3C/g%3E%3C/svg%3E\");",
    "organic": "background-image:url(\"data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='80' height='80'%3E%3Ccircle cx='40' cy='40' r='1.5' fill='{line_color_enc}' fill-opacity='0.3'/%3E%3Ccircle cx='10' cy='10' r='1' fill='{line_color_enc}' fill-opacity='0.2'/%3E%3Ccircle cx='70' cy='15' r='1.2' fill='{line_color_enc}' fill-opacity='0.25'/%3E%3Ccircle cx='20' cy='65' r='0.8' fill='{line_color_enc}' fill-opacity='0.2'/%3E%3C/svg%3E\");",
    "none": "",
}

ANIMATED_BACKGROUNDS = {
    "professional": {
        "keyframes": """
@keyframes aura-grid-pulse {
  0%, 100% { opacity: 0.03; }
  50% { opacity: 0.07; }
}
@keyframes aura-line-drift {
  0% { transform: translateX(-100%); }
  100% { transform: translateX(100%); }
}""",
        "layers": """
<div class="aura-bg-layer" style="position:absolute;inset:0;overflow:hidden;pointer-events:none;z-index:0;">
  <div style="position:absolute;inset:0;background-image:linear-gradient(rgba({pr},0.04) 1px,transparent 1px),linear-gradient(90deg,rgba({pr},0.04) 1px,transparent 1px);background-size:48px 48px;animation:aura-grid-pulse 6s ease-in-out infinite;"></div>
  <div style="position:absolute;top:30%;left:0;right:0;height:1px;background:linear-gradient(90deg,transparent,rgba({pr},0.08),transparent);animation:aura-line-drift 12s linear infinite;"></div>
  <div style="position:absolute;top:65%;left:0;right:0;height:1px;background:linear-gradient(90deg,transparent,rgba({sr},0.06),transparent);animation:aura-line-drift 16s linear infinite reverse;"></div>
</div>""",
    },
    "creative": {
        "keyframes": """
@keyframes aura-wave-flow {
  0% { transform: translateX(0) translateY(0); }
  25% { transform: translateX(-5%) translateY(2%); }
  50% { transform: translateX(-10%) translateY(0); }
  75% { transform: translateX(-5%) translateY(-2%); }
  100% { transform: translateX(0) translateY(0); }
}
@keyframes aura-shimmer {
  0%, 100% { opacity: 0.3; }
  50% { opacity: 0.6; }
}""",
        "layers": """
<div class="aura-bg-layer" style="position:absolute;inset:0;overflow:hidden;pointer-events:none;z-index:0;">
  <svg style="position:absolute;bottom:-20%;left:-10%;width:120%;height:80%;opacity:0.04;animation:aura-wave-flow 20s ease-in-out infinite;" viewBox="0 0 1440 320" preserveAspectRatio="none"><path fill="rgba({pr},1)" d="M0,192L48,208C96,224,192,256,288,261.3C384,267,480,245,576,224C672,203,768,181,864,186.7C960,192,1056,224,1152,218.7C1248,213,1344,171,1392,149.3L1440,128L1440,320L0,320Z"/></svg>
  <svg style="position:absolute;bottom:-30%;left:-5%;width:115%;height:70%;opacity:0.03;animation:aura-wave-flow 25s ease-in-out infinite reverse;" viewBox="0 0 1440 320" preserveAspectRatio="none"><path fill="rgba({sr},1)" d="M0,256L48,240C96,224,192,192,288,181.3C384,171,480,181,576,202.7C672,224,768,256,864,261.3C960,267,1056,245,1152,218.7C1248,192,1344,160,1392,144L1440,128L1440,320L0,320Z"/></svg>
  <div style="position:absolute;top:15%;right:10%;width:300px;height:300px;border-radius:50%;background:radial-gradient(circle,rgba({ar},0.06),transparent 70%);animation:aura-shimmer 8s ease-in-out infinite;"></div>
</div>""",
    },
    "bold": {
        "keyframes": """
@keyframes aura-gradient-shift {
  0% { background-position: 0% 50%; }
  50% { background-position: 100% 50%; }
  100% { background-position: 0% 50%; }
}
@keyframes aura-geo-rotate {
  0% { transform: rotate(0deg) scale(1); opacity: 0.04; }
  50% { transform: rotate(180deg) scale(1.1); opacity: 0.07; }
  100% { transform: rotate(360deg) scale(1); opacity: 0.04; }
}""",
        "layers": """
<div class="aura-bg-layer" style="position:absolute;inset:0;overflow:hidden;pointer-events:none;z-index:0;">
  <div style="position:absolute;inset:0;background:linear-gradient(135deg,rgba({pr},0.03),rgba({sr},0.05),rgba({ar},0.03),rgba({pr},0.05));background-size:400% 400%;animation:aura-gradient-shift 15s ease infinite;"></div>
  <div style="position:absolute;top:10%;left:5%;width:200px;height:200px;border:1px solid rgba({pr},0.06);animation:aura-geo-rotate 30s linear infinite;"></div>
  <div style="position:absolute;bottom:15%;right:8%;width:150px;height:150px;border:1px solid rgba({sr},0.05);border-radius:10%;animation:aura-geo-rotate 25s linear infinite reverse;"></div>
  <div style="position:absolute;top:50%;left:50%;width:250px;height:250px;border:1px solid rgba({ar},0.04);border-radius:50%;animation:aura-geo-rotate 35s linear infinite;transform-origin:center;"></div>
</div>""",
    },
    "warm": {
        "keyframes": """
@keyframes aura-float {
  0%, 100% { transform: translateY(0) scale(1); }
  50% { transform: translateY(-20px) scale(1.05); }
}
@keyframes aura-glow-pulse {
  0%, 100% { opacity: 0.04; transform: scale(1); }
  50% { opacity: 0.08; transform: scale(1.15); }
}""",
        "layers": """
<div class="aura-bg-layer" style="position:absolute;inset:0;overflow:hidden;pointer-events:none;z-index:0;">
  <div style="position:absolute;top:20%;left:15%;width:250px;height:250px;border-radius:50%;background:radial-gradient(circle,rgba({pr},0.07),transparent 70%);animation:aura-float 12s ease-in-out infinite;"></div>
  <div style="position:absolute;top:60%;right:20%;width:180px;height:180px;border-radius:50%;background:radial-gradient(circle,rgba({sr},0.06),transparent 70%);animation:aura-float 16s ease-in-out infinite 3s;"></div>
  <div style="position:absolute;bottom:10%;left:40%;width:120px;height:120px;border-radius:50%;background:radial-gradient(circle,rgba({ar},0.05),transparent 70%);animation:aura-glow-pulse 10s ease-in-out infinite 1s;"></div>
</div>""",
    },
    "minimal": {
        "keyframes": """
@keyframes aura-breathe {
  0%, 100% { opacity: 0.02; }
  50% { opacity: 0.04; }
}""",
        "layers": """
<div class="aura-bg-layer" style="position:absolute;inset:0;overflow:hidden;pointer-events:none;z-index:0;">
  <div style="position:absolute;inset:0;background:linear-gradient(180deg,rgba({pr},0.02),transparent 60%);animation:aura-breathe 10s ease-in-out infinite;"></div>
</div>""",
    },
}

SECTION_ICON_MAP = {
    "hero": '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5"><path d="M11.48 3.499a.562.562 0 011.04 0l2.125 5.111a.563.563 0 00.475.345l5.518.442c.499.04.701.663.321.988l-4.204 3.602a.563.563 0 00-.182.557l1.285 5.385a.562.562 0 01-.84.61l-4.725-2.885a.563.563 0 00-.586 0L6.982 20.54a.562.562 0 01-.84-.61l1.285-5.386a.562.562 0 00-.182-.557l-4.204-3.602a.563.563 0 01.321-.988l5.518-.442a.563.563 0 00.475-.345L11.48 3.5z"/></svg>',
    "about": '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5"><path d="M15 9h3.75M15 12h3.75M15 15h3.75M4.5 19.5h15a2.25 2.25 0 002.25-2.25V6.75A2.25 2.25 0 0019.5 4.5h-15A2.25 2.25 0 002.25 6.75v10.5A2.25 2.25 0 004.5 19.5zm6-10.125a1.875 1.875 0 11-3.75 0 1.875 1.875 0 013.75 0zm1.294 6.336a6.721 6.721 0 01-3.17.789 6.721 6.721 0 01-3.168-.789 3.376 3.376 0 016.338 0z"/></svg>',
    "features": '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5"><path d="M3.75 13.5l10.5-11.25L12 10.5h8.25L9.75 21.75 12 13.5H3.75z"/></svg>',
    "pricing": '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5"><path d="M2.25 18.75a60.07 60.07 0 0115.797 2.101c.727.198 1.453-.342 1.453-1.096V18.75M3.75 4.5v.75A.75.75 0 013 6h-.75m0 0v-.375c0-.621.504-1.125 1.125-1.125H20.25M2.25 6v9m18-10.5v.75c0 .414.336.75.75.75h.75m-1.5-1.5h.375c.621 0 1.125.504 1.125 1.125v9.75c0 .621-.504 1.125-1.125 1.125h-.375m1.5-1.5H21a.75.75 0 00-.75.75v.75m0 0H3.75m0 0h-.375a1.125 1.125 0 01-1.125-1.125V15m1.5 1.5v-.75A.75.75 0 003 15h-.75M15 10.5a3 3 0 11-6 0 3 3 0 016 0zm3 0h.008v.008H18V10.5zm-12 0h.008v.008H6V10.5z"/></svg>',
    "testimonials": '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5"><path d="M20.25 8.511c.884.284 1.5 1.128 1.5 2.097v4.286c0 1.136-.847 2.1-1.98 2.193-.34.027-.68.052-1.02.072v3.091l-3-3c-1.354 0-2.694-.055-4.02-.163a2.115 2.115 0 01-.825-.242m9.345-8.334a2.126 2.126 0 00-.476-.095 48.64 48.64 0 00-8.048 0c-1.131.094-1.976 1.057-1.976 2.192v4.286c0 .837.46 1.58 1.155 1.951m9.345-8.334V6.637c0-1.621-1.152-3.026-2.76-3.235A48.455 48.455 0 0011.25 3c-2.115 0-4.198.137-6.24.402-1.608.209-2.76 1.614-2.76 3.235v6.226c0 1.621 1.152 3.026 2.76 3.235.577.075 1.157.14 1.74.194V21l4.155-4.155"/></svg>',
    "faq": '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5"><path d="M9.879 7.519c1.171-1.025 3.071-1.025 4.242 0 1.172 1.025 1.172 2.687 0 3.712-.203.179-.43.326-.67.442-.745.361-1.45.999-1.45 1.827v.75M21 12a9 9 0 11-18 0 9 9 0 0118 0zm-9 5.25h.008v.008H12v-.008z"/></svg>',
    "how_it_works": '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5"><path d="M10.5 6h9.75M10.5 6a1.5 1.5 0 11-3 0m3 0a1.5 1.5 0 10-3 0M3.75 6H7.5m3 12h9.75m-9.75 0a1.5 1.5 0 01-3 0m3 0a1.5 1.5 0 00-3 0m-3.75 0H7.5m9-6h3.75m-3.75 0a1.5 1.5 0 01-3 0m3 0a1.5 1.5 0 00-3 0m-9.75 0h9.75"/></svg>',
    "team": '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5"><path d="M18 18.72a9.094 9.094 0 003.741-.479 3 3 0 00-4.682-2.72m.94 3.198l.001.031c0 .225-.012.447-.037.666A11.944 11.944 0 0112 21c-2.17 0-4.207-.576-5.963-1.584A6.062 6.062 0 016 18.719m12 0a5.971 5.971 0 00-.941-3.197m0 0A5.995 5.995 0 0012 12.75a5.995 5.995 0 00-5.058 2.772m0 0a3 3 0 00-4.681 2.72 8.986 8.986 0 003.74.477m.94-3.197a5.971 5.971 0 00-.94 3.197M15 6.75a3 3 0 11-6 0 3 3 0 016 0zm6 3a2.25 2.25 0 11-4.5 0 2.25 2.25 0 014.5 0zm-13.5 0a2.25 2.25 0 11-4.5 0 2.25 2.25 0 014.5 0z"/></svg>',
    "contact": '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5"><path d="M21.75 6.75v10.5a2.25 2.25 0 01-2.25 2.25h-15a2.25 2.25 0 01-2.25-2.25V6.75m19.5 0A2.25 2.25 0 0019.5 4.5h-15a2.25 2.25 0 00-2.25 2.25m19.5 0v.243a2.25 2.25 0 01-1.07 1.916l-7.5 4.615a2.25 2.25 0 01-2.36 0L3.32 8.91a2.25 2.25 0 01-1.07-1.916V6.75"/></svg>',
    "resources": '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5"><path d="M12 6.042A8.967 8.967 0 006 3.75c-1.052 0-2.062.18-3 .512v14.25A8.987 8.987 0 016 18c2.305 0 4.408.867 6 2.292m0-14.25a8.966 8.966 0 016-2.292c1.052 0 2.062.18 3 .512v14.25A8.987 8.987 0 0018 18a8.967 8.967 0 00-6 2.292m0-14.25v14.25"/></svg>',
    "gallery": '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5"><path d="M2.25 15.75l5.159-5.159a2.25 2.25 0 013.182 0l5.159 5.159m-1.5-1.5l1.409-1.409a2.25 2.25 0 013.182 0l2.909 2.909M3.75 21h16.5A2.25 2.25 0 0022.5 18.75V5.25A2.25 2.25 0 0020.25 3H3.75A2.25 2.25 0 001.5 5.25v13.5A2.25 2.25 0 003.75 21zM10.5 8.25a2.25 2.25 0 11-4.5 0 2.25 2.25 0 014.5 0z"/></svg>',
    "comparison": '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5"><path d="M3 13.125C3 12.504 3.504 12 4.125 12h2.25c.621 0 1.125.504 1.125 1.125v6.75C7.5 20.496 6.996 21 6.375 21h-2.25A1.125 1.125 0 013 19.875v-6.75zM9.75 8.625c0-.621.504-1.125 1.125-1.125h2.25c.621 0 1.125.504 1.125 1.125v11.25c0 .621-.504 1.125-1.125 1.125h-2.25a1.125 1.125 0 01-1.125-1.125V8.625zM16.5 4.125c0-.621.504-1.125 1.125-1.125h2.25C20.496 3 21 3.504 21 4.125v15.75c0 .621-.504 1.125-1.125 1.125h-2.25a1.125 1.125 0 01-1.125-1.125V4.125z"/></svg>',
    "stats": '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5"><path d="M2.25 18L9 11.25l4.306 4.307a11.95 11.95 0 015.814-5.519l2.74-1.22m0 0l-5.94-2.28m5.94 2.28l-2.28 5.941"/></svg>',
    "problem": '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5"><path d="M12 9v3.75m-9.303 3.376c-.866 1.5.217 3.374 1.948 3.374h14.71c1.73 0 2.813-1.874 1.948-3.374L13.949 3.378c-.866-1.5-3.032-1.5-3.898 0L2.697 16.126zM12 15.75h.007v.008H12v-.008z"/></svg>',
    "solution": '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5"><path d="M12 18v-5.25m0 0a6.01 6.01 0 001.5-.189m-1.5.189a6.01 6.01 0 01-1.5-.189m3.75 7.478a12.06 12.06 0 01-4.5 0m3.75 2.383a14.406 14.406 0 01-3 0M14.25 18v-.192c0-.983.658-1.823 1.508-2.316a7.5 7.5 0 10-7.517 0c.85.493 1.509 1.333 1.509 2.316V18"/></svg>',
    "cta": '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5"><path d="M15.59 14.37a6 6 0 01-5.84 7.38v-4.8m5.84-2.58a14.98 14.98 0 006.16-12.12A14.98 14.98 0 009.631 8.41m5.96 5.96a14.926 14.926 0 01-5.841 2.58m-.119-8.54a6 6 0 00-7.381 5.84h4.8m2.581-5.84a14.927 14.927 0 00-2.58 5.84m2.699 2.7c-.103.021-.207.041-.311.06a15.09 15.09 0 01-2.448-2.448 14.9 14.9 0 01.06-.312m-2.24 2.39a4.493 4.493 0 00-1.757 4.306 4.493 4.493 0 004.306-1.758M16.5 9a1.5 1.5 0 11-3 0 1.5 1.5 0 013 0z"/></svg>',
}


def get_mood_profile(mood: str) -> dict:
    return MOOD_PROFILES.get(mood, MOOD_PROFILES["professional"])


def compute_colors(primary: str, secondary: str, accent: str) -> ThemeColors:
    primary_light = _adjust_lightness(primary, 1.3)
    primary_dark = _adjust_lightness(primary, 0.7)
    secondary_light = _adjust_lightness(secondary, 1.3)
    primary_text = _get_contrast_text(primary)

    return ThemeColors(
        primary=primary,
        primary_light=primary_light,
        primary_dark=primary_dark,
        primary_rgb=hex_to_rgb(primary),
        primary_text=primary_text,
        secondary=secondary,
        secondary_light=secondary_light,
        secondary_rgb=hex_to_rgb(secondary),
        accent=accent,
        accent_rgb=hex_to_rgb(accent),
    )


def generate_theme(
    mood: str = "professional",
    primary: str = "#4F46E5",
    secondary: str = "#7C3AED",
    accent: str = "#06B6D4",
    niche: str = "",
) -> ThemeConfig:
    profile = get_mood_profile(mood)
    colors = compute_colors(primary, secondary, accent)

    pr = _parse_rgb(primary)
    sr = _parse_rgb(secondary)
    ar = _parse_rgb(accent)

    r1, g1, b1 = pr
    r2, g2, b2 = sr

    line_color = f"rgba({r1},{g1},{b1},0.04)"
    line_color_enc = f"%23{primary.lstrip('#')}"

    bg_pattern_key = profile["background_pattern"]
    bg_pattern_css = BACKGROUND_PATTERNS.get(bg_pattern_key, "")
    if bg_pattern_css:
        bg_pattern_css = bg_pattern_css.replace("{line_color}", line_color).replace("{line_color_enc}", line_color_enc)

    section_bgs = {
        "hero": f"linear-gradient(135deg, {primary}, {secondary})",
        "stats": f"linear-gradient(135deg, {primary}08, {secondary}05)",
        "problem": "linear-gradient(135deg, #0f172a, #1e1b4b)",
        "solution": f"linear-gradient(135deg, {_adjust_lightness(accent, 1.8)}, {_adjust_lightness(accent, 1.6)})",
        "about": "#ffffff",
        "features": f"linear-gradient(180deg, #f8fafc, {primary}06)",
        "how_it_works": "#ffffff",
        "testimonials": f"linear-gradient(180deg, #fafaf9, {_adjust_lightness(secondary, 1.85)})",
        "pricing": "#ffffff",
        "comparison": f"linear-gradient(180deg, #f8fafc, {primary}08)",
        "gallery": "#ffffff",
        "team": f"linear-gradient(180deg, #f9fafb, {primary}05)",
        "resources": "#ffffff",
        "faq": f"linear-gradient(180deg, #f8fafc, {secondary}06)",
        "contact": "#ffffff",
        "cta": f"linear-gradient(135deg, {primary}, {secondary})",
    }

    divider_pairs_list = [
        ("hero", "wave"), ("stats", "clean-line"), ("problem", "diagonal"),
        ("solution", "curve"), ("features", "wave-inverse"), ("how_it_works", "clean-line"),
        ("testimonials", "wave"), ("pricing", "clean-line"), ("comparison", "curve"),
        ("gallery", "clean-line"), ("team", "wave-inverse"), ("resources", "clean-line"),
        ("faq", "wave"), ("contact", "clean-line"),
    ]
    if profile["divider_style"] in SVG_DIVIDERS:
        divider_pairs_list = [(s, profile["divider_style"]) for s, _ in divider_pairs_list]

    anim_bg = ANIMATED_BACKGROUNDS.get(mood, ANIMATED_BACKGROUNDS["professional"])
    anim_keyframes_raw = anim_bg.get("keyframes", "")
    anim_layers_raw = anim_bg.get("layers", "")
    pr_str = f"{r1},{g1},{b1}"
    sr_str = f"{r2},{g2},{b2}"
    ar_str = f"{ar[0]},{ar[1]},{ar[2]}"
    anim_layers_html = anim_layers_raw.replace("{pr}", pr_str).replace("{sr}", sr_str).replace("{ar}", ar_str)

    return ThemeConfig(
        mood=mood,
        profile=profile,
        colors=colors,
        card_style=profile["card_style"],
        section_header_style=profile["section_header_style"],
        border_radius=profile["border_radius"],
        shadow_intensity=profile["shadow_intensity"],
        font_weight_heading=profile["font_weight_heading"],
        letter_spacing=profile["letter_spacing"],
        background_pattern=bg_pattern_css,
        section_backgrounds=section_bgs,
        divider_pairs={s: d for s, d in divider_pairs_list},
        dividers=SVG_DIVIDERS,
        icons=SECTION_ICON_MAP,
        animated_bg_keyframes=anim_keyframes_raw,
        animated_bg_layers=anim_layers_html,
    )


@aura_module(
    name="theme",
    version="1.0.0",
    description="Visual theme engine — generates CSS variables, mood profiles, color computations, SVG dividers, section backgrounds, and animated backgrounds from brand colors and mood",
    input_model=ThemeInput,
    tags=["theme", "visual", "css", "brand"],
)
def run_theme(config: dict) -> ModuleResult:
    mood = config.get("mood", "professional")
    primary = config.get("primary", "#4F46E5")
    secondary = config.get("secondary", "#7C3AED")
    accent = config.get("accent", "#06B6D4")
    niche = config.get("niche", "")
    theme = generate_theme(mood=mood, primary=primary, secondary=secondary, accent=accent, niche=niche)
    output = theme.model_dump() if hasattr(theme, "model_dump") else theme.__dict__
    return ModuleResult(
        ok=True,
        output=output,
        metadata={"mood": mood, "niche": niche},
    )
