"""
Aura Graphics Pack Engine v2.0.0
================================
AI-powered brand graphics generation using Google Gemini (Nano Banana).

Generates cohesive visual identity packs:
  - Logos (5 styles: wordmark, emblem, abstract_mark, icon_text, monogram)
  - Section icons (per content section)
  - Site Essentials (favicon, nav_logo, footer_logo, og_image, cta_accent, email_header)
  - Decorative separators (6 styles)

Every asset and pack carries unique identifiers and version tracking:
  - Pack: pack_id (GP-xxxx), pack_version (auto-increments on regeneration)
  - Assets: asset_id (GPA-xxxx), revision number, lineage tracking

HISTORY: Every regenerated asset is preserved in history[]. Nothing is ever lost.
"""

ENGINE_VERSION = "2.0.0"
SCHEMA_VERSION = 3

import os
import uuid
import base64
import json
import logging
import hashlib
import datetime
from pathlib import Path
from concurrent.futures import ThreadPoolExecutor, as_completed
from tenacity import retry, stop_after_attempt, wait_exponential, retry_if_exception

from google import genai
from google.genai import types

logger = logging.getLogger(__name__)

def _resolve_env(*keys: str) -> str | None:
    for key in keys:
        val = os.environ.get(key)
        if val:
            return val
    return None


gemini_client = None


def _generate_pack_id() -> str:
    return f"GP-{uuid.uuid4().hex[:8].upper()}"


def _generate_asset_id() -> str:
    return f"GPA-{uuid.uuid4().hex[:8].upper()}"


def _get_gemini_client():
    global gemini_client
    if gemini_client is None:
        api_key = _resolve_env("AI_INTEGRATIONS_GEMINI_API_KEY", "GEMINI_API_KEY", "GOOGLE_API_KEY")
        base_url = _resolve_env("AI_INTEGRATIONS_GEMINI_BASE_URL")
        http_opts = {'api_version': ''}
        if base_url:
            http_opts['base_url'] = base_url
        gemini_client = genai.Client(
            api_key=api_key,
            http_options=http_opts
        )
    return gemini_client


def is_rate_limit_error(exception: BaseException) -> bool:
    error_msg = str(exception)
    status_code = getattr(exception, 'status', None) or getattr(exception, 'status_code', None)
    return (
        "429" in error_msg
        or "RATELIMIT_EXCEEDED" in error_msg
        or "quota" in error_msg.lower()
        or "rate limit" in error_msg.lower()
        or status_code == 429
    )


GRAPHICS_DIR = Path("static/graphics")

LOGO_STYLES = {
    "wordmark": {
        "id": "LS-01",
        "name": "Wordmark",
        "description": "A premium typographic wordmark logo — the brand name rendered in bespoke lettering with the precision of hand-engraved watch dials. Each letterform should feel individually crafted: deliberate weight variation, tasteful kerning, and subtle typographic details like tapered terminals, ink traps, or hairline serifs that reward close inspection. Think Rolls-Royce badge lettering or Breguet watch numerals — quiet authority through typographic mastery. No icon, pure lettering excellence.",
    },
    "emblem": {
        "id": "LS-02",
        "name": "Emblem",
        "description": "A prestige emblem/crest logo with the craftsmanship of a Bentley hood ornament or Swiss watch case-back engraving. The brand name sits within an architecturally structured frame — shield, medallion, or crest — with ornamental details that feel hand-tooled: fine line filigree, symmetrical scrollwork, and layered depth through precise line-weight hierarchy. Every decorative element should serve a purpose, nothing arbitrary. The kind of emblem you'd stamp into leather or emboss on heavy card stock.",
    },
    "abstract_mark": {
        "id": "LS-03",
        "name": "Abstract Mark",
        "description": "A modern abstract geometric mark with the engineered precision of a supercar air intake or turbine blade — every curve exists for a reason. The symbol should feel like it was CNC-machined from a single concept: clean geometric relationships, golden-ratio proportions, and the kind of satisfying visual tension between positive and negative space that makes designers stare. Think Pininfarina design language — minimal but loaded with intent. No text, pure form.",
    },
    "icon_text": {
        "id": "LS-04",
        "name": "Icon + Text",
        "description": "A combination mark where icon and typography are married with the fit-and-finish of a Pagani dashboard — each element distinctly excellent, yet together forming something greater. The icon should be a distilled symbol of the brand concept: bold, geometric, memorable in silhouette. The text sits alongside with deliberate spacing and weight-matched to the icon's visual density. The two elements should feel like they share a DNA — same design vocabulary, same level of refinement, unified by invisible grid alignment.",
    },
    "monogram": {
        "id": "LS-05",
        "name": "Monogram",
        "description": "An elegant monogram using the brand's initials with the intricacy of Damascus steel pattern-welding — letters interlocked, interwoven, or nested with geometric precision. Each letterform should have deliberate overlap or nesting points that feel engineered, not accidental. The kind of monogram you'd hot-stamp into bridle leather or etch into sapphire crystal. Balanced weight distribution, optical centering, and the quiet confidence of old-money craftsmanship.",
    },
}

SEPARATOR_STYLES = {
    "flowing": {
        "id": "SS-01",
        "name": "Flowing",
        "description": "organic flowing lines with the fluidity of hand-blown Murano glass — continuous, sinuous curves that breathe like silk in wind. Each line should have deliberate thickness variation as if drawn by a master calligrapher's brush: swelling at the curves, tapering to hairline at the extremities. Not random waviness — controlled elegance with the grace of Art Nouveau ironwork.",
    },
    "geometric": {
        "id": "SS-02",
        "name": "Geometric",
        "description": "precision geometric patterns with the engineered exactness of carbon fiber weave or guilloche watch dial engraving. Sharp angles, perfect symmetry, and mathematically derived spacing. Every intersection point is deliberate. Think aerospace engineering meets decorative art — the kind of pattern you'd find laser-etched into titanium or machined into a supercar's billet aluminum controls.",
    },
    "botanical": {
        "id": "SS-03",
        "name": "Botanical",
        "description": "delicate botanical filigree with the refined intricacy of Victorian botanical illustration or pressed-flower herbarium art. Individual leaves should show vein structure, stems should taper naturally, and tendrils should curl with organic Fibonacci spirals. Not clip-art flowers — this is botanical craftsmanship, the kind of detail you'd find hand-engraved on a silver tea service or embossed on fine Italian stationery.",
    },
    "abstract": {
        "id": "SS-04",
        "name": "Abstract",
        "description": "abstract artistic expression with the controlled energy of sumi-e brush mastery — each stroke placed with deliberate intention, not random splatter. Varying ink density from saturated core to feathered edge. The kind of abstract mark that looks effortless but reveals deep technical skill on close inspection. Think Franz Kline's structural boldness meets Japanese wabi-sabi restraint.",
    },
    "ornamental": {
        "id": "SS-05",
        "name": "Ornamental",
        "description": "symmetrical ornamental scrollwork with the precision of hand-chased silver repoussé or carved Baroque architectural molding. Balanced volutes, acanthus-inspired curves, and hierarchical detail levels — primary scrolls framing secondary flourishes framing tertiary finials. Every element mirrors perfectly. The craftsmanship of a master silversmith: no lazy symmetry, every curve hand-refined.",
    },
    "minimal": {
        "id": "SS-06",
        "name": "Minimal",
        "description": "ultra-minimal with the restraint of a single piano note in a concert hall — one thin, perfectly weighted horizontal line with a small, exquisitely detailed centered accent element. The line should have a subtle warmth: not ruler-straight but hand-ruled with barely perceptible character. The center accent is tiny but precious — like a single diamond set into platinum. Maximum impact from minimum elements.",
    },
}

DEFAULT_MODEL = "gemini-2.5-flash-image"

MODELS = {
    "gemini-2.5-flash-image": {
        "id": "MDL-01",
        "name": "Nano Banana (Fast)",
        "tier": "standard",
        "cost_per_image": "$0.04",
    },
    "gemini-2.0-flash-exp": {
        "id": "MDL-03",
        "name": "Flash Exp (Experimental)",
        "tier": "experimental",
        "cost_per_image": "~$0.04",
    },
    "gemini-3-pro-image-preview": {
        "id": "MDL-02",
        "name": "Nano Banana Pro (Legendary)",
        "tier": "legendary",
        "cost_per_image": "$0.13",
    },
}

SITE_ESSENTIALS = {
    "favicon": {
        "id": "SE-01",
        "name": "Favicon",
        "description": "A tiny square icon (32x32) that appears in browser tabs. Must be instantly recognizable at very small sizes.",
        "prompt_template": """Create a premium favicon icon for a brand called "{brand_name}" in the {niche} niche.

CRAFTSMANSHIP STANDARD: This is the brand's smallest ambassador — like a signet ring or cufflink monogram. Every pixel matters at this scale. IMPORTANT: This is a FLAT 2D graphic icon — no photorealistic textures, no 3D materials, no surface rendering.

CRITICAL SIZE CONSTRAINT: Must read clearly at 32x32 pixels. Ultra-simple. If it has more than one element, it's too complex.

DESIGN:
- Single, bold, iconic shape or letterform that distills the entire brand into one symbol
- Use {primary} as the dominant color — pure, saturated, no muddy tones
- The shape must have PERFECT geometric balance — optically centered, not mathematically centered
- Designed with the precision mindset of fine engraving — but rendered as clean flat vector art
- Ultra-simple silhouette that reads instantly at 32x32 pixels — if it doesn't read at thumbnail size, it fails
- Clean edges with pixel-perfect crispness — no anti-aliasing haze, no fuzzy borders
- Transparent or solid background (no gradients at this scale)
- NO text, NO fine details, NO complexity — this is reductive design mastery
- The icon should feel like it was carved from a single piece of precious material""",
        "size_hint": "32x32",
    },
    "nav_logo": {
        "id": "SE-02",
        "name": "Navigation Logo",
        "description": "A compact horizontal logo variant optimized for website navigation bars. Smaller and simpler than the main logo.",
        "prompt_template": """Create a compact horizontal navigation bar logo for "{brand_name}" ({niche}).

CRAFTSMANSHIP STANDARD: This logo lives in the navigation bar — the brand's constant presence on every page. Like a dashboard badge: always visible, never distracting, unmistakably premium. IMPORTANT: Flat 2D vector graphic — no photorealistic textures or 3D rendering.

CRITICAL SIZE CONSTRAINT: Must be perfectly legible at 40px height. Every element must read at this small scale.

DESIGN:
- Horizontal aspect ratio optimized for 40px height
- Clean, refined brand name in a carefully chosen typeface — weight-matched to the accompanying icon
- Small icon/symbol to the left of the text, proportionally balanced (not oversized)
- Use {primary} as dominant color, {secondary} for icon accent
- The spacing between icon and text should feel like hand-kerned typography — deliberate, not default
- Transparent background — must sit cleanly on any header color
- Think of the precision of a Leica camera top-plate engraving: compact, authoritative, quietly luxurious
- NO gradients, NO shadows, NO effects — pure form and color
- Must remain crisp and legible, not blurry or crowded at navigation scale""",
        "size_hint": "wide_small",
    },
    "footer_logo": {
        "id": "SE-03",
        "name": "Footer Logo",
        "description": "A monochrome or muted version of the logo for website footers. Typically white/light for dark footer backgrounds.",
        "prompt_template": """Create a footer logo variant for "{brand_name}" ({niche}).

CRAFTSMANSHIP STANDARD: The footer logo is the brand's closing signature — like a hallmark stamped on fine silverware. Understated, elegant, permanent. IMPORTANT: Flat 2D vector graphic — no photorealistic textures or 3D rendering.

DESIGN:
- Monochrome white or light gray design — must look stunning on a dark background
- Horizontal layout, clean and spacious
- Slightly more restrained than the main logo — less visual weight, more breathing room
- Transparent background (this will sit on dark footer sections)
- The white rendering should feel like platinum foil stamping on dark leather — luminous and precise
- Typography should be thinner/lighter weight than the main logo — whispered, not spoken
- NO colored elements — purely monochrome white with possible gray tonal variation for depth
- NO shadows, NO glows — the contrast against the dark background provides all the presence needed
- Think of a luxury brand's embossed packaging: the logo is there, unmistakable, but never loud""",
        "size_hint": "wide_small",
    },
    "og_image": {
        "id": "SE-04",
        "name": "Social Share Image",
        "description": "The image that appears when your site is shared on social media (Facebook, Twitter, LinkedIn). 1200x630 pixels.",
        "prompt_template": """Create a social media share card image for "{brand_name}" — a {niche} brand with tagline "{tagline}".

CRAFTSMANSHIP STANDARD: This is the brand's first impression on social media — a digital business card that must command attention in a feed of mediocrity. Like the cover of a Taschen art book: bold, confident, impossible to scroll past.

DESIGN:
- 1200x630 landscape aspect ratio — MUST have a solid or gradient background (NOT transparent)
- Background: rich, deep gradient using {primary} and {secondary} — bold, saturated color depth. Optionally add an ultra-subtle flat geometric pattern overlay (not a texture render — a clean vector pattern at low opacity)
- Brand name "{brand_name}" in LARGE, bold, beautifully typeset text as the dominant focal point — centered or slightly above center
- Tagline "{tagline}" in smaller, lighter text below the brand name — refined spacing, not cramped
- Use {accent} sparingly for a small highlight element: an underline, a dot, a subtle decorative accent
- Typography must have generous letter-spacing and feel hand-placed, not template-generated
- Ensure EXTREME contrast between text and background — this must read clearly at tiny social thumbnail sizes
- NO photographs, NO complex illustrations, NO photorealistic textures or material rendering — pure flat typographic and color design
- The overall impression should be: "this brand knows exactly what it is" """,
        "size_hint": "1200x630",
    },
    "cta_accent": {
        "id": "SE-05",
        "name": "CTA Accent Graphic",
        "description": "A small decorative accent graphic used near call-to-action buttons to draw attention.",
        "prompt_template": """Create a small decorative accent graphic for call-to-action areas on a {niche} website.

CRAFTSMANSHIP STANDARD: This is visual energy in concentrated form — small, vibrant, irresistible. Every detail at this tiny scale must be perfect. IMPORTANT: Flat 2D vector graphic — no photorealistic textures or 3D rendering.

DESIGN:
- Abstract decorative element: an energy burst, a directional swoosh, converging arrows, or radiating sparks
- Use {accent} as the dominant color — vivid, saturated, electric
- Transparent background — this floats near CTA buttons as a visual attractor
- The element should feel kinetic — like captured motion, frozen energy, a visual "go" signal
- Must work beautifully at 80-120px — small but potent
- Clean vector-style rendering: crisp edges, no blur, no soft gradients
- Think of the glow around a supercar's start/stop button or the light streak of a F1 car's brake light
- NOT busy — this is one elegant gesture, not a firework display
- NO text, NO photorealistic elements, NO 3D materials — pure flat abstract energy""",
        "size_hint": "small_accent",
    },
    "email_header": {
        "id": "SE-06",
        "name": "Email Header",
        "description": "A branded header graphic for marketing emails. Horizontal, 600px wide.",
        "prompt_template": """Create an email header banner for "{brand_name}" ({niche}).

CRAFTSMANSHIP STANDARD: This banner opens every marketing email — it sets the tone before a single word is read. Like the masthead of a luxury magazine: authoritative, beautiful, and instantly establishing quality expectations.

DESIGN:
- Horizontal banner, 600px wide × 150px tall proportions
- MUST have a solid or gradient background (NOT transparent) — rich, confident color using {primary} and {secondary}
- Brand name "{brand_name}" in clean, bold typography — centered or left-aligned with intentional asymmetry
- The background should feel layered and rich: a deep gradient with optional ultra-subtle flat geometric pattern overlay at very low opacity (NOT a material texture — a clean vector pattern)
- Typography should feel hand-set: generous letter-spacing, perfect vertical centering, weight that commands without shouting
- Use {accent} only for a tiny highlight detail — a thin accent line, a small decorative element
- Must render reliably across ALL email clients — no transparency tricks, no complex layering
- NO photographs, NO illustrations, NO photorealistic textures or material rendering — pure flat typographic and color design
- The banner should feel like opening a luxury product's packaging: the first visual says "you're in good hands" """,
        "size_hint": "email_banner",
    },
}


def _archive_asset_to_history(pack: dict, asset_data: dict) -> None:
    if not asset_data or not asset_data.get("asset_id"):
        return
    if "history" not in pack:
        pack["history"] = []
    archived = dict(asset_data)
    archived["replaced_at"] = datetime.datetime.utcnow().isoformat()
    pack["history"].append(archived)


def _build_style_seed(brand_data: dict, niche: str, mood: str) -> dict:
    primary = brand_data.get("color_primary", "#4F46E5")
    secondary = brand_data.get("color_secondary", "#7C3AED")
    accent = brand_data.get("color_accent", "#06B6D4")
    brand_name = brand_data.get("name", "")
    tagline = brand_data.get("tagline", "")
    tone = brand_data.get("tone", "professional")
    palette_desc = brand_data.get("palette", {}).get("description", "")

    return {
        "brand_name": brand_name,
        "tagline": tagline,
        "niche": niche,
        "mood": mood,
        "tone": tone,
        "colors": {
            "primary": primary,
            "secondary": secondary,
            "accent": accent,
        },
        "palette_description": palette_desc,
        "style_direction": f"Design for a {tone} {niche} brand called '{brand_name}'. The visual style should feel {mood}. Use colors {primary} (primary), {secondary} (secondary), {accent} (accent). {palette_desc}",
    }


def _ensure_domain_dir(domain: str) -> Path:
    safe_domain = domain.replace("/", "_").replace("\\", "_")
    domain_dir = GRAPHICS_DIR / safe_domain
    domain_dir.mkdir(parents=True, exist_ok=True)
    return domain_dir


def _save_image_bytes(image_bytes: bytes, filepath: Path) -> str:
    filepath.write_bytes(image_bytes)
    return f"/{filepath}"


def _compute_content_hash(data: bytes) -> str:
    return hashlib.sha256(data).hexdigest()[:12]


@retry(
    stop=stop_after_attempt(7),
    wait=wait_exponential(multiplier=1, min=2, max=128),
    retry=retry_if_exception(is_rate_limit_error),
    reraise=True
)
def _generate_image_gemini(prompt: str, model: str = None) -> bytes:
    model = model or DEFAULT_MODEL
    client = _get_gemini_client()

    if "2.0-flash" in model:
        modalities = ["Text", "Image"]
    else:
        modalities = ["IMAGE"]

    response = client.models.generate_content(
        model=model,
        contents=prompt,
        config=types.GenerateContentConfig(
            response_modalities=modalities,
        )
    )

    if not response.candidates:
        raise ValueError(f"No candidates in Gemini response (model={model})")

    candidate = response.candidates[0]
    if not candidate.content or not candidate.content.parts:
        raise ValueError(f"No content parts in Gemini response (model={model})")

    for part in candidate.content.parts:
        if hasattr(part, 'inline_data') and part.inline_data:
            image_data = part.inline_data.data
            if isinstance(image_data, str):
                return base64.b64decode(image_data)
            return bytes(image_data)

    raise ValueError(f"No image data found in Gemini response (model={model})")


def _build_influence_suffix(influence_url: str = "", reference_prompt: str = "") -> str:
    parts = []
    if influence_url:
        parts.append(f"""
STYLE INFLUENCE (BRAND KIT REFERENCE):
The client has provided a reference image from their brand kit at: {influence_url}
Study the visual language, color distribution, line weight, and overall aesthetic of that reference.
Incorporate its stylistic DNA — color temperature, geometric vs organic quality, level of detail — into this new asset.
Do NOT copy it literally; instead let it guide the visual vocabulary and craftsmanship level.""")
    if reference_prompt:
        parts.append(f"""
CREATIVE DIRECTION (USER-PROVIDED):
{reference_prompt}
Incorporate these specific creative directions while maintaining brand coherence and quality standards.""")
    return "\n".join(parts)


def generate_logo(
    brand_data: dict,
    niche: str,
    mood: str,
    style: str = "icon_text",
    model: str = None,
    domain: str = "",
    previous_asset_id: str = None,
    revision: int = 1,
    influence_url: str = "",
    reference_prompt: str = "",
) -> dict:
    model = model or DEFAULT_MODEL
    style_seed = _build_style_seed(brand_data, niche, mood)
    style_meta = LOGO_STYLES.get(style, LOGO_STYLES["icon_text"])
    style_desc = style_meta["description"]
    brand_name = style_seed["brand_name"]
    primary = style_seed["colors"]["primary"]
    secondary = style_seed["colors"]["secondary"]
    accent = style_seed["colors"]["accent"]

    prompt = f"""Design an ultra-premium logo for "{brand_name}" — a {niche} brand.

Think step by step about what makes this brand unique, then create a logo that a Fortune 500 company would pay $50,000 for.

STYLE DIRECTIVE: {style_desc}

BRAND DNA:
- Niche: {niche} | Mood: {mood} | Tone: {style_seed['tone']}
- Tagline: "{style_seed['tagline']}"

EXACT COLORS (mandatory — use these precise hex values):
- Primary: {primary} — dominant structural color, at least 60% of the mark
- Secondary: {secondary} — supporting elements, typographic contrast, 25-30%
- Accent: {accent} — ONE surgical highlight, a single jewel-like detail, max 10%

COMPOSITION RULES (non-negotiable):
1. TRANSPARENT BACKGROUND — no rectangles, no circles behind, no background fill whatsoever
2. The logo floats on transparency with generous padding on all sides
3. Optically centered (not mathematically — adjust for visual weight distribution)
4. Total element count: maximum 3 distinct visual components (icon + text + accent detail)
5. If text is present: each letter must be individually considered — custom kerning, optical spacing, deliberate weight
6. The silhouette alone must be recognizable — it should read clearly as a solid black shape

RENDERING STANDARD:
- FLAT 2D vector illustration style — razor-crisp edges like laser-cut stainless steel
- Line weights: deliberate hierarchy — primary strokes 3-4x thicker than detail strokes
- Geometric precision: all angles are intentional (45°, 30°, 60° relationships), all curves follow consistent radius logic
- Color application: solid fills only, no gradients, no transparency, no blending
- NO photorealism, NO 3D rendering, NO shadows, NO glows, NO textures, NO bevels
- NO busy ornamentation — every element must justify its existence
- The overall impression: engineered luxury, quiet authority, timeless sophistication"""

    prompt += _build_influence_suffix(influence_url, reference_prompt)

    logger.info(f"Generating {style} logo for {domain} with {model}")
    image_bytes = _generate_image_gemini(prompt, model)
    content_hash = _compute_content_hash(image_bytes)

    domain_dir = _ensure_domain_dir(domain)
    safe_brand = brand_name.replace(" ", "-").replace("/", "").replace("\\", "")[:40].lower()
    filename = f"brand-logo-{style}-{safe_brand}.png"
    filepath = domain_dir / filename
    url = _save_image_bytes(image_bytes, filepath)
    asset_id = _generate_asset_id()
    model_meta = MODELS.get(model, {"id": "MDL-00", "name": model, "tier": "unknown"})

    return {
        "asset_id": asset_id,
        "type": "logo",
        "classification": "logo",
        "display_name": f"Brand Logo ({style_meta['name']})",
        "style_id": style_meta["id"],
        "style": style,
        "style_name": style_meta["name"],
        "revision": revision,
        "replaces": previous_asset_id,
        "url": url,
        "filename": filename,
        "content_hash": content_hash,
        "model_id": model_meta["id"],
        "model": model,
        "model_name": model_meta["name"],
        "prompt_fingerprint": hashlib.md5(prompt.encode()).hexdigest()[:8],
        "generated_at": datetime.datetime.utcnow().isoformat(),
        "engine_version": ENGINE_VERSION,
    }


def generate_section_icon(
    brand_data: dict,
    niche: str,
    mood: str,
    section_type: str,
    section_title: str,
    icon_hint: str = "",
    model: str = None,
    domain: str = "",
    previous_asset_id: str = None,
    revision: int = 1,
    influence_url: str = "",
    reference_prompt: str = "",
) -> dict:
    model = model or DEFAULT_MODEL
    style_seed = _build_style_seed(brand_data, niche, mood)
    primary = style_seed["colors"]["primary"]
    secondary = style_seed["colors"]["secondary"]

    icon_context = f"representing the concept of '{icon_hint}'" if icon_hint else f"representing the concept of '{section_title}'"

    section_type_hints = {
        "features": "key capabilities or benefits — a precision-cut gemstone, a master-tooled gear, or a radiating star with crisp facets",
        "how_it_works": "a process or workflow — interlocking gears with visible teeth precision, a stepped pathway with clean angles, or a circuit-trace flowpath",
        "problem": "a challenge or pain point — a fractured geometric shape showing clean break lines, a maze with visible walls, or a precision warning diamond",
        "solution": "a resolution or answer — a key with detailed ward cuts, a lightbulb with filament structure, or a checkmark with confident stroke weight",
        "about": "identity or story — a refined silhouette with considered proportions, an architectural building with structural detail, or a heart with geometric construction lines",
        "stats": "data or metrics — a chart with precisely weighted bars, an upward trend arrow with engineered curves, or a gauge dial with tick marks",
        "testimonials": "social proof or trust — speech bubbles with crisp corners and deliberate overlap, a constellation of stars with geometric placement, or quotation marks with typographic weight",
        "pricing": "value or plans — a price tag with a clean fold detail, a diamond with facet lines, or tiered blocks with precise stacking",
        "comparison": "evaluation or choice — a balance scale with mechanical pivot detail, parallel columns with equal weight, or a versus symbol with architectural symmetry",
        "gallery": "visual showcase — an aperture with blade-like segments, a picture frame with mitered corner detail, or a grid with golden-ratio proportions",
        "team": "people or collaboration — group silhouettes with deliberate overlap hierarchy, interlocking hands with finger detail, or connected nodes with precise link lines",
        "resources": "tools or knowledge — a book with visible spine and page edges, a toolbox with latch detail, or a download arrow with technical precision",
        "faq": "questions and answers — a question mark with considered typographic weight and terminal detail, a chat bubble with crisp geometry, or a help circle with precise stroke width",
        "contact": "communication or reach — an envelope with fold lines and seal detail, a phone with refined proportions, or a location pin with inner circle precision",
        "cta": "action or engagement — a rocket with exhaust detail and aerodynamic lines, a forward arrow with speed-streak taper, or a lightning bolt with angular facets",
    }
    type_hint = section_type_hints.get(section_type, "a relevant symbolic representation with considered geometric construction")

    prompt = f"""Design a single premium icon {icon_context} for a {niche} website.

Think about what ONE symbol best represents "{section_title}" — then render it with the precision of Swiss graphic design.

PURPOSE: Icon for the "{section_type}" section titled "{section_title}"
VISUAL METAPHOR: {type_hint}

EXACT COLORS:
- Primary fill: {primary} — the main shape, bold and confident
- Secondary detail: {secondary} — one internal detail or accent element for visual depth

COMPOSITION (strict rules):
1. ONE single unified symbol — not multiple objects, not a scene, ONE iconic shape
2. TRANSPARENT BACKGROUND — no circle behind it, no badge, no square, nothing. Just the icon floating
3. The icon occupies roughly 70% of the canvas, centered with breathing room
4. Maximum 2 colors ({primary} and {secondary}) — no other colors
5. The silhouette alone must communicate the concept (squint test: does it read as a solid shape?)

RENDERING:
- FLAT 2D vector illustration — crisp edges, solid fills, no gradients
- Bold geometric construction — clean angles, consistent curves, mathematical relationships between elements
- Line weight hierarchy if using strokes: thick for primary structure (4px+), thin for inner detail (1-2px)
- Every edge laser-sharp — no soft edges, no blur, no feathering
- NO text, NO labels, NO words
- NO 3D, NO shadows, NO gradients, NO glow, NO photorealism
- The quality standard: Apple's SF Symbols or Google Material Icons — simple but unmistakably premium"""

    prompt += _build_influence_suffix(influence_url, reference_prompt)

    logger.info(f"Generating icon for section '{section_type}' ({domain})")
    image_bytes = _generate_image_gemini(prompt, model)
    content_hash = _compute_content_hash(image_bytes)

    domain_dir = _ensure_domain_dir(domain)
    safe_title = section_title.replace(" ", "-").replace("/", "").replace("\\", "")[:30].lower()
    filename = f"section-icon-{section_type}-{safe_title}.png"
    filepath = domain_dir / filename
    url = _save_image_bytes(image_bytes, filepath)
    asset_id = _generate_asset_id()
    model_meta = MODELS.get(model, {"id": "MDL-00", "name": model, "tier": "unknown"})

    return {
        "asset_id": asset_id,
        "type": "icon",
        "classification": "icon_graphic",
        "display_name": f"Section Icon — {section_title}",
        "section": section_type,
        "section_title": section_title,
        "revision": revision,
        "replaces": previous_asset_id,
        "url": url,
        "filename": filename,
        "content_hash": content_hash,
        "model_id": model_meta["id"],
        "model": model,
        "model_name": model_meta["name"],
        "prompt_fingerprint": hashlib.md5(prompt.encode()).hexdigest()[:8],
        "generated_at": datetime.datetime.utcnow().isoformat(),
        "engine_version": ENGINE_VERSION,
    }


def generate_separator(
    brand_data: dict,
    niche: str,
    mood: str,
    separator_style: str = "auto",
    model: str = None,
    domain: str = "",
    previous_asset_id: str = None,
    revision: int = 1,
    influence_url: str = "",
    reference_prompt: str = "",
) -> dict:
    model = model or DEFAULT_MODEL
    style_seed = _build_style_seed(brand_data, niche, mood)
    primary = style_seed["colors"]["primary"]
    secondary = style_seed["colors"]["secondary"]
    accent = style_seed["colors"]["accent"]

    resolved_style = separator_style
    if separator_style == "auto":
        mood_separator_map = {
            "professional": "geometric",
            "creative": "abstract",
            "minimal": "minimal",
            "bold": "geometric",
            "warm": "botanical",
        }
        resolved_style = mood_separator_map.get(mood, "flowing")

    style_meta = SEPARATOR_STYLES.get(resolved_style, SEPARATOR_STYLES["flowing"])
    style_desc = style_meta["description"]

    prompt = f"""Design a wide horizontal decorative separator line for a premium {niche} website.

SEPARATOR STYLE: {style_desc}

COLORS:
- Primary: {primary} — main decorative element
- Secondary: {secondary} — supporting detail
- Accent: {accent} — optional single tiny highlight

COMPOSITION (strict):
1. EXTREME wide aspect ratio — approximately 1200px wide × 40px tall (a thin horizontal ribbon)
2. TRANSPARENT BACKGROUND — the decorative element floats, no filled rectangle behind it
3. Perfectly symmetrical — mirrored left-to-right from a center axis
4. The center may have a small focal motif (a diamond, a knot, a flourish) with decorative lines extending outward to both edges
5. Lines taper and fade toward the edges — the endpoints dissolve gracefully, not hard-stop
6. Total visual weight: LIGHT — this whispers, it does not shout

RENDERING:
- FLAT 2D vector illustration — crisp clean lines, no texture, no grain
- Line weight: thin and refined (1-3px equivalent), deliberate variation for visual rhythm
- Solid color fills only — no gradients, no transparency effects
- NO text, NO 3D, NO shadows, NO photorealism
- The quality of a hand-engraved certificate border — precise, elegant, restrained"""

    prompt += _build_influence_suffix(influence_url, reference_prompt)

    logger.info(f"Generating {resolved_style} separator for {domain}")
    image_bytes = _generate_image_gemini(prompt, model)
    content_hash = _compute_content_hash(image_bytes)

    domain_dir = _ensure_domain_dir(domain)
    filename = f"section-separator-{resolved_style}-divider.png"
    filepath = domain_dir / filename
    url = _save_image_bytes(image_bytes, filepath)
    asset_id = _generate_asset_id()
    model_meta = MODELS.get(model, {"id": "MDL-00", "name": model, "tier": "unknown"})

    return {
        "asset_id": asset_id,
        "type": "separator",
        "classification": "section_separator",
        "display_name": f"Section Separator ({style_meta['name']})",
        "style_id": style_meta["id"],
        "style": resolved_style,
        "style_name": style_meta["name"],
        "requested_style": separator_style,
        "revision": revision,
        "replaces": previous_asset_id,
        "url": url,
        "filename": filename,
        "content_hash": content_hash,
        "model_id": model_meta["id"],
        "model": model,
        "model_name": model_meta["name"],
        "prompt_fingerprint": hashlib.md5(prompt.encode()).hexdigest()[:8],
        "generated_at": datetime.datetime.utcnow().isoformat(),
        "engine_version": ENGINE_VERSION,
    }


def generate_essential(
    essential_type: str,
    brand_data: dict,
    niche: str,
    mood: str,
    model: str = None,
    domain: str = "",
    previous_asset_id: str = None,
    revision: int = 1,
    influence_url: str = "",
    reference_prompt: str = "",
) -> dict:
    model = model or DEFAULT_MODEL
    if essential_type not in SITE_ESSENTIALS:
        raise ValueError(f"Unknown essential type: {essential_type}")

    meta = SITE_ESSENTIALS[essential_type]
    style_seed = _build_style_seed(brand_data, niche, mood)
    primary = style_seed["colors"]["primary"]
    secondary = style_seed["colors"]["secondary"]
    accent = style_seed["colors"]["accent"]
    brand_name = style_seed["brand_name"]
    tagline = style_seed["tagline"]

    prompt = meta["prompt_template"].format(
        brand_name=brand_name, niche=niche, mood=mood,
        primary=primary, secondary=secondary, accent=accent, tagline=tagline,
    )
    prompt += _build_influence_suffix(influence_url, reference_prompt)

    logger.info(f"Generating essential '{essential_type}' for {domain} with {model}")
    image_bytes = _generate_image_gemini(prompt, model)
    content_hash = _compute_content_hash(image_bytes)

    domain_dir = _ensure_domain_dir(domain)
    safe_brand = brand_name.replace(" ", "-").replace("/", "").replace("\\", "")[:40].lower()
    filename = f"essential-{essential_type}-{safe_brand}.png"
    filepath = domain_dir / filename
    url = _save_image_bytes(image_bytes, filepath)
    asset_id = _generate_asset_id()
    model_meta = MODELS.get(model, {"id": "MDL-00", "name": model, "tier": "unknown"})

    return {
        "asset_id": asset_id,
        "type": "essential",
        "essential_type": essential_type,
        "classification": "essential",
        "display_name": meta["name"],
        "essential_id": meta["id"],
        "size_hint": meta["size_hint"],
        "revision": revision,
        "replaces": previous_asset_id,
        "url": url,
        "filename": filename,
        "content_hash": content_hash,
        "model_id": model_meta["id"],
        "model": model,
        "model_name": model_meta["name"],
        "prompt_fingerprint": hashlib.md5(prompt.encode()).hexdigest()[:8],
        "generated_at": datetime.datetime.utcnow().isoformat(),
        "engine_version": ENGINE_VERSION,
    }


def generate_full_graphics_pack(
    brand_data: dict,
    niche: str,
    mood: str,
    site_copy: dict,
    domain: str,
    logo_style: str = "icon_text",
    separator_style: str = "auto",
    model: str = None,
    progress_callback=None,
    existing_pack: dict = None,
) -> dict:
    model = model or DEFAULT_MODEL
    style_seed = _build_style_seed(brand_data, niche, mood)

    prev_version = 0
    if existing_pack and isinstance(existing_pack, dict):
        prev_version = existing_pack.get("pack_version", existing_pack.get("version", 0))
    pack_version = prev_version + 1
    prev_pack_id = existing_pack.get("pack_id") if existing_pack else None

    history = []
    if existing_pack and isinstance(existing_pack, dict):
        history = list(existing_pack.get("history", []))
        if existing_pack.get("logo"):
            _archive_asset_to_history({"history": history}, existing_pack["logo"])
        for sec_type, icon_data in (existing_pack.get("icons") or {}).items():
            if icon_data:
                _archive_asset_to_history({"history": history}, icon_data)
        if existing_pack.get("separator"):
            _archive_asset_to_history({"history": history}, existing_pack["separator"])
        for ess_type, ess_data in (existing_pack.get("essentials") or {}).items():
            if ess_data:
                _archive_asset_to_history({"history": history}, ess_data)

    pack = {
        "pack_id": _generate_pack_id(),
        "pack_version": pack_version,
        "schema_version": SCHEMA_VERSION,
        "engine_version": ENGINE_VERSION,
        "replaces_pack_id": prev_pack_id,
        "style_seed": style_seed,
        "model_id": MODELS.get(model, {}).get("id", "MDL-00"),
        "model": model,
        "model_name": MODELS.get(model, {}).get("name", model),
        "domain": domain,
        "generated_at": datetime.datetime.utcnow().isoformat(),
        "logo": None,
        "icons": {},
        "separator": None,
        "essentials": {},
        "asset_index": {},
        "history": history,
        "status": "generating",
        "errors": [],
    }

    total_steps = 1 + 1 + len(SITE_ESSENTIALS)
    ICON_ELIGIBLE = {"features", "how_it_works", "problem", "solution", "about",
                     "stats", "testimonials", "pricing", "comparison", "gallery",
                     "team", "resources", "faq", "contact", "cta"}
    sections = site_copy.get("sections", [])
    icon_sections = []
    if sections and isinstance(sections, list):
        for sec in sections:
            sec_type = sec.get("type", "")
            if sec_type in ICON_ELIGIBLE:
                icon_sections.append(sec)
    else:
        for key in site_copy:
            if key in ICON_ELIGIBLE:
                title = key.replace("_", " ").title()
                content = site_copy[key]
                if isinstance(content, dict):
                    title = content.get("title", title)
                icon_sections.append({"type": key, "title": title, "icon": ""})
    total_steps += len(icon_sections)
    current_step = 0

    def _report(msg):
        nonlocal current_step
        current_step += 1
        pct = int((current_step / total_steps) * 100)
        if progress_callback:
            progress_callback(pct, msg)

    prev_logo_id = None
    prev_logo_rev = 0
    if existing_pack and existing_pack.get("logo"):
        prev_logo_id = existing_pack["logo"].get("asset_id")
        prev_logo_rev = existing_pack["logo"].get("revision", 0)

    try:
        logo = generate_logo(brand_data, niche, mood, logo_style, model, domain,
                             previous_asset_id=prev_logo_id, revision=prev_logo_rev + 1)
        pack["logo"] = logo
        pack["asset_index"][logo["asset_id"]] = {"type": "logo", "style": logo_style}
        _report(f"Logo generated ({logo_style} style) [{logo['asset_id']}]")
    except Exception as e:
        logger.error(f"Logo generation failed for {domain}: {e}")
        pack["errors"].append({"asset": "logo", "error": str(e), "error_id": _generate_asset_id()})
        _report(f"Logo generation failed: {str(e)[:100]}")

    existing_icons = existing_pack.get("icons", {}) if existing_pack else {}

    def _gen_icon(sec):
        sec_type = sec.get("type", "")
        sec_title = sec.get("title", sec_type)
        icon_hint = sec.get("icon", "")
        prev_icon = existing_icons.get(sec_type, {})
        prev_id = prev_icon.get("asset_id")
        prev_rev = prev_icon.get("revision", 0)
        try:
            return sec_type, generate_section_icon(
                brand_data, niche, mood, sec_type, sec_title, icon_hint, model, domain,
                previous_asset_id=prev_id, revision=prev_rev + 1,
            )
        except Exception as e:
            logger.error(f"Icon generation failed for {sec_type} ({domain}): {e}")
            return sec_type, {"error": str(e)}

    with ThreadPoolExecutor(max_workers=2) as executor:
        futures = {executor.submit(_gen_icon, sec): sec for sec in icon_sections}
        for future in as_completed(futures):
            sec_type, result = future.result()
            if "error" in result:
                pack["errors"].append({"asset": f"icon_{sec_type}", "error": result["error"], "error_id": _generate_asset_id()})
                _report(f"Icon for '{sec_type}' failed")
            else:
                pack["icons"][sec_type] = result
                pack["asset_index"][result["asset_id"]] = {"type": "icon", "section": sec_type}
                _report(f"Icon for '{sec_type}' generated [{result['asset_id']}]")

    prev_sep_id = None
    prev_sep_rev = 0
    if existing_pack and existing_pack.get("separator"):
        prev_sep_id = existing_pack["separator"].get("asset_id")
        prev_sep_rev = existing_pack["separator"].get("revision", 0)

    try:
        separator = generate_separator(brand_data, niche, mood, separator_style, model, domain,
                                       previous_asset_id=prev_sep_id, revision=prev_sep_rev + 1)
        pack["separator"] = separator
        pack["asset_index"][separator["asset_id"]] = {"type": "separator", "style": separator["style"]}
        _report(f"Separator generated ({separator['style']} style) [{separator['asset_id']}]")
    except Exception as e:
        logger.error(f"Separator generation failed for {domain}: {e}")
        pack["errors"].append({"asset": "separator", "error": str(e), "error_id": _generate_asset_id()})
        _report(f"Separator generation failed: {str(e)[:100]}")

    existing_essentials = existing_pack.get("essentials", {}) if existing_pack else {}
    for ess_key, ess_meta in SITE_ESSENTIALS.items():
        prev_ess = existing_essentials.get(ess_key)
        prev_ess_id = prev_ess.get("asset_id") if prev_ess else None
        prev_ess_rev = prev_ess.get("revision", 0) if prev_ess else 0
        try:
            ess_result = generate_essential(
                ess_key, brand_data, niche, mood, model, domain,
                previous_asset_id=prev_ess_id, revision=prev_ess_rev + 1,
            )
            pack["essentials"][ess_key] = ess_result
            pack["asset_index"][ess_result["asset_id"]] = {"type": "essential", "essential_type": ess_key}
            _report(f"Essential '{ess_meta['name']}' generated [{ess_result['asset_id']}]")
        except Exception as e:
            logger.error(f"Essential '{ess_key}' generation failed for {domain}: {e}")
            pack["errors"].append({"asset": f"essential_{ess_key}", "error": str(e), "error_id": _generate_asset_id()})
            _report(f"Essential '{ess_meta['name']}' failed: {str(e)[:100]}")

    pack["status"] = "complete" if not pack["errors"] else "partial"
    pack["completed_at"] = datetime.datetime.utcnow().isoformat()
    essentials_generated = len(pack["essentials"])
    pack["stats"] = {
        "logo_generated": pack["logo"] is not None,
        "icons_generated": len(pack["icons"]),
        "icons_total": len(icon_sections),
        "separator_generated": pack["separator"] is not None,
        "essentials_generated": essentials_generated,
        "essentials_total": len(SITE_ESSENTIALS),
        "total_assets": (1 if pack["logo"] else 0) + len(pack["icons"]) + (1 if pack["separator"] else 0) + essentials_generated,
        "errors": len(pack["errors"]),
    }

    return pack


def regenerate_asset(
    asset_type: str,
    brand_data: dict,
    niche: str,
    mood: str,
    domain: str,
    model: str = "gemini-2.5-flash-image",
    existing_pack: dict = None,
    **kwargs,
) -> dict:
    prev_asset = None
    prev_id = None
    prev_rev = 0

    if existing_pack:
        if asset_type == "logo":
            prev_asset = existing_pack.get("logo")
        elif asset_type == "icon":
            prev_asset = existing_pack.get("icons", {}).get(kwargs.get("section_type", ""))
        elif asset_type == "separator":
            prev_asset = existing_pack.get("separator")
        elif asset_type == "essential":
            prev_asset = existing_pack.get("essentials", {}).get(kwargs.get("essential_type", ""))

        if prev_asset:
            prev_id = prev_asset.get("asset_id")
            prev_rev = prev_asset.get("revision", 0)

    influence_url = kwargs.get("influence_url", "")
    reference_prompt = kwargs.get("reference_prompt", "")

    if asset_type == "logo":
        style = kwargs.get("style", "icon_text")
        return generate_logo(brand_data, niche, mood, style, model, domain,
                             previous_asset_id=prev_id, revision=prev_rev + 1,
                             influence_url=influence_url, reference_prompt=reference_prompt)
    elif asset_type == "icon":
        section_type = kwargs.get("section_type", "features")
        section_title = kwargs.get("section_title", section_type)
        icon_hint = kwargs.get("icon_hint", "")
        return generate_section_icon(brand_data, niche, mood, section_type, section_title, icon_hint, model, domain,
                                     previous_asset_id=prev_id, revision=prev_rev + 1,
                                     influence_url=influence_url, reference_prompt=reference_prompt)
    elif asset_type == "separator":
        separator_style = kwargs.get("separator_style", "auto")
        return generate_separator(brand_data, niche, mood, separator_style, model, domain,
                                  previous_asset_id=prev_id, revision=prev_rev + 1,
                                  influence_url=influence_url, reference_prompt=reference_prompt)
    elif asset_type == "essential":
        essential_type = kwargs.get("essential_type", "favicon")
        return generate_essential(essential_type, brand_data, niche, mood, model, domain,
                                  previous_asset_id=prev_id, revision=prev_rev + 1,
                                  influence_url=influence_url, reference_prompt=reference_prompt)
    else:
        raise ValueError(f"Unknown asset type: {asset_type}")


def get_graphics_pack_summary(graphics_pack: dict) -> dict:
    if not graphics_pack:
        return {"has_pack": False}

    logo = graphics_pack.get("logo")
    sep = graphics_pack.get("separator")
    essentials = graphics_pack.get("essentials", {})
    history = graphics_pack.get("history", [])

    return {
        "has_pack": True,
        "pack_id": graphics_pack.get("pack_id", ""),
        "pack_version": graphics_pack.get("pack_version", graphics_pack.get("version", 1)),
        "schema_version": graphics_pack.get("schema_version", 1),
        "engine_version": graphics_pack.get("engine_version", ""),
        "status": graphics_pack.get("status", "unknown"),
        "generated_at": graphics_pack.get("generated_at", ""),
        "model_id": graphics_pack.get("model_id", ""),
        "model": graphics_pack.get("model", ""),
        "model_name": graphics_pack.get("model_name", ""),
        "has_logo": logo is not None,
        "logo_asset_id": logo.get("asset_id", "") if logo else "",
        "logo_style": logo.get("style", "") if logo else "",
        "logo_style_id": logo.get("style_id", "") if logo else "",
        "logo_revision": logo.get("revision", 0) if logo else 0,
        "logo_url": logo.get("url", "") if logo else "",
        "icon_count": len(graphics_pack.get("icons", {})),
        "icon_sections": list(graphics_pack.get("icons", {}).keys()),
        "icon_asset_ids": {k: v.get("asset_id", "") for k, v in graphics_pack.get("icons", {}).items()},
        "has_separator": sep is not None,
        "separator_asset_id": sep.get("asset_id", "") if sep else "",
        "separator_style": sep.get("style", "") if sep else "",
        "separator_style_id": sep.get("style_id", "") if sep else "",
        "separator_revision": sep.get("revision", 0) if sep else 0,
        "separator_url": sep.get("url", "") if sep else "",
        "essentials": {k: {"name": v.get("display_name", k), "asset_id": v.get("asset_id", ""), "revision": v.get("revision", 0), "url": v.get("url", "")} for k, v in essentials.items()},
        "essentials_count": len(essentials),
        "essentials_total": len(SITE_ESSENTIALS),
        "history_count": len(history),
        "total_assets": graphics_pack.get("stats", {}).get("total_assets", 0),
        "asset_index": graphics_pack.get("asset_index", {}),
        "errors": graphics_pack.get("errors", []),
        "stats": graphics_pack.get("stats", {}),
    }


def flatten_pack_assets(graphics_pack: dict) -> list:
    assets = []
    if not graphics_pack:
        return assets
    logo = graphics_pack.get("logo")
    if logo and logo.get("url"):
        assets.append(logo)
    for sec_type, icon_data in (graphics_pack.get("icons") or {}).items():
        if icon_data and icon_data.get("url"):
            assets.append(icon_data)
    sep = graphics_pack.get("separator")
    if sep and sep.get("url"):
        assets.append(sep)
    for ess_type, ess_data in (graphics_pack.get("essentials") or {}).items():
        if ess_data and ess_data.get("url"):
            assets.append(ess_data)
    return assets


def resolve_asset_by_id(graphics_pack: dict, asset_id: str) -> dict:
    if not graphics_pack or not asset_id:
        return None

    if graphics_pack.get("logo", {}).get("asset_id") == asset_id:
        return graphics_pack["logo"]
    for sec_type, icon_data in (graphics_pack.get("icons") or {}).items():
        if icon_data and icon_data.get("asset_id") == asset_id:
            return icon_data
    if graphics_pack.get("separator", {}).get("asset_id") == asset_id:
        return graphics_pack["separator"]
    for ess_type, ess_data in (graphics_pack.get("essentials") or {}).items():
        if ess_data and ess_data.get("asset_id") == asset_id:
            return ess_data
    for hist_item in graphics_pack.get("history", []):
        if hist_item.get("asset_id") == asset_id:
            return hist_item

    return None
