import os
import re
import logging
from typing import Any

logger = logging.getLogger(__name__)

DEFAULT_COLORS = {"#4F46E5", "#7C3AED", "#06B6D4", "#4f46e5", "#7c3aed", "#06b6d4"}

PLACEHOLDER_PHRASES = [
    "lorem ipsum", "dolor sit amet", "your company", "company name",
    "example.com", "john doe", "jane doe", "acme", "test company",
    "sample text", "placeholder", "insert your", "your name here",
    "coming soon", "[your", "{your", "xxx", "todo", "tbd",
    "default description", "default title", "enter your",
]

GENERIC_HEADLINES = [
    "welcome", "welcome to our website", "hello world", "about us",
    "our services", "get started", "home", "untitled",
]

SECTION_KEYS = [
    "headline", "subheadline", "hero_body", "about", "about_body", "about_mission",
    "features", "testimonials", "faq_items", "how_it_works_steps", "pricing_tiers",
    "stats", "team_members", "resource_items", "gallery_sections", "comparison",
    "contact_title", "contact_body", "contact_methods",
    "cta_final_title", "cta_final_body", "cta_text",
    "solution_title", "solution_body", "solution_points",
    "problem_title", "problem_body", "problem_points",
]

LIST_SECTIONS = {
    "features": {"min_legendary": 4, "min_good": 2, "weight": 8},
    "testimonials": {"min_legendary": 3, "min_good": 1, "weight": 6},
    "faq_items": {"min_legendary": 4, "min_good": 2, "weight": 5},
    "how_it_works_steps": {"min_legendary": 3, "min_good": 2, "weight": 5},
    "pricing_tiers": {"min_legendary": 3, "min_good": 1, "weight": 5},
    "stats": {"min_legendary": 3, "min_good": 2, "weight": 4},
    "team_members": {"min_legendary": 2, "min_good": 1, "weight": 3},
    "resource_items": {"min_legendary": 3, "min_good": 1, "weight": 4},
    "contact_methods": {"min_legendary": 2, "min_good": 1, "weight": 4},
    "solution_points": {"min_legendary": 3, "min_good": 1, "weight": 4},
    "problem_points": {"min_legendary": 3, "min_good": 1, "weight": 3},
    "gallery_sections": {"min_legendary": 2, "min_good": 1, "weight": 2},
}

CATEGORY_WEIGHTS = {
    "brand_identity": 15,
    "hero_section": 12,
    "site_copy_quality": 25,
    "brand_kit_assets": 12,
    "visual_theme": 8,
    "content_depth": 8,
    "contact_info": 6,
    "sales_letter": 7,
    "hero_image": 7,
}

RATING_VALUES = {"legendary": 1.0, "good": 0.7, "default": 0.3, "missing": 0.0}


def _has_placeholder(text: str) -> bool:
    if not text:
        return False
    lower = text.lower().strip()
    return any(p in lower for p in PLACEHOLDER_PHRASES)


def _niche_keyword_hits(text: str, keywords: list[str]) -> int:
    if not text or not keywords:
        return 0
    lower = text.lower()
    return sum(1 for kw in keywords if kw.lower() in lower)


def _word_count(text: str) -> int:
    if not text:
        return 0
    return len(re.findall(r'\b\w+\b', str(text)))


def _extract_niche_keywords(domain: str, niche: str, context_keywords: list[str] | None = None) -> list[str]:
    keywords = set()
    if niche:
        for word in re.findall(r'\b\w{3,}\b', niche.lower()):
            if word not in {"the", "and", "for", "with", "from", "that", "this", "are", "was", "were", "been", "have", "has", "not"}:
                keywords.add(word)
    if domain:
        clean = re.sub(r'\.(com|org|net|io|co|info|biz|us|me|app|dev)$', '', domain.lower())
        for word in re.split(r'[.\-_]', clean):
            if len(word) >= 3:
                keywords.add(word)
    if context_keywords:
        for kw in context_keywords:
            if isinstance(kw, str) and len(kw) >= 3:
                keywords.add(kw.lower())
    return list(keywords)


def _rate(score: float) -> str:
    if score >= 0.9:
        return "legendary"
    if score >= 0.6:
        return "good"
    if score >= 0.2:
        return "default"
    return "missing"


def _scan_brand_identity(brand: dict, domain: str, niche: str, niche_kws: list[str]) -> dict:
    checks = []
    score_parts = []

    options = brand.get("options", [])
    has_options = len(options) >= 1
    checks.append({"element": "brand_options", "value": len(options), "rating": "legendary" if len(options) >= 2 else ("good" if has_options else "missing"), "detail": f"{len(options)} brand options"})
    score_parts.append(1.0 if len(options) >= 2 else (0.7 if has_options else 0.0))

    chosen_idx = brand.get("recommended", 0)
    chosen = options[chosen_idx] if options and chosen_idx < len(options) else {}
    brand_name = chosen.get("name", "")
    name_is_domain = brand_name.lower().replace(" ", "").replace("-", "") == domain.lower().replace(".", "").replace("-", "")
    name_is_generic = brand_name.lower().strip() in ["", "your company", "company name", "untitled"]
    name_has_placeholder = _has_placeholder(brand_name)
    name_score = 0.0
    if brand_name and not name_is_generic and not name_has_placeholder:
        name_score = 0.7 if name_is_domain else 1.0
    checks.append({"element": "brand_name", "value": brand_name, "rating": _rate(name_score), "detail": "Domain-derived" if name_is_domain else ("Custom name" if name_score >= 0.9 else "Generic/missing")})
    score_parts.append(name_score)

    tagline = chosen.get("tagline", "")
    tag_score = 0.0
    if tagline and not _has_placeholder(tagline):
        tag_score = 1.0 if _niche_keyword_hits(tagline, niche_kws) > 0 else 0.7
    checks.append({"element": "tagline", "value": tagline[:80] if tagline else "", "rating": _rate(tag_score), "detail": f"{_word_count(tagline)} words" + (", niche-relevant" if tag_score >= 0.9 else "")})
    score_parts.append(tag_score)

    colors = [brand.get("color_primary", ""), brand.get("color_secondary", ""), brand.get("color_accent", "")]
    default_count = sum(1 for c in colors if c.lower() in DEFAULT_COLORS)
    custom_colors = 3 - default_count
    color_score = custom_colors / 3.0
    checks.append({"element": "brand_colors", "value": {"primary": colors[0], "secondary": colors[1], "accent": colors[2]}, "rating": _rate(color_score), "detail": f"{custom_colors}/3 custom colors" + (", all defaults" if custom_colors == 0 else "")})
    score_parts.append(color_score)

    avg = sum(score_parts) / len(score_parts) if score_parts else 0
    return {"category": "brand_identity", "rating": _rate(avg), "score": round(avg, 3), "weight": CATEGORY_WEIGHTS["brand_identity"], "checks": checks}


def _scan_hero_section(site_copy: dict, niche_kws: list[str]) -> dict:
    checks = []
    score_parts = []

    headline = site_copy.get("headline", "")
    hl_score = 0.0
    if headline:
        is_generic = headline.lower().strip() in GENERIC_HEADLINES or _has_placeholder(headline)
        if not is_generic:
            hl_score = 1.0 if _niche_keyword_hits(headline, niche_kws) > 0 else 0.7
    checks.append({"element": "headline", "value": headline[:100] if headline else "", "rating": _rate(hl_score), "detail": f"{_word_count(headline)} words" + (", niche-specific" if hl_score >= 0.9 else "")})
    score_parts.append(hl_score)

    subheadline = site_copy.get("subheadline", "")
    sub_score = 0.0
    if subheadline and not _has_placeholder(subheadline):
        sub_score = 1.0 if _word_count(subheadline) >= 8 and _niche_keyword_hits(subheadline, niche_kws) > 0 else 0.7 if _word_count(subheadline) >= 5 else 0.4
    checks.append({"element": "subheadline", "value": subheadline[:120] if subheadline else "", "rating": _rate(sub_score), "detail": f"{_word_count(subheadline)} words"})
    score_parts.append(sub_score)

    hero_body = site_copy.get("hero_body", "")
    hb_score = 0.0
    if hero_body and not _has_placeholder(hero_body):
        wc = _word_count(hero_body)
        hb_score = 1.0 if wc >= 20 else 0.7 if wc >= 10 else 0.4
    checks.append({"element": "hero_body", "value": hero_body[:150] + "..." if len(hero_body or "") > 150 else hero_body or "", "rating": _rate(hb_score), "detail": f"{_word_count(hero_body)} words"})
    score_parts.append(hb_score)

    cta = site_copy.get("cta_text", "")
    cta_score = 0.0
    if cta:
        is_generic_cta = cta.lower().strip() in ["get started", "learn more", "click here", "sign up", "buy now", "submit"]
        cta_score = 0.5 if is_generic_cta else 1.0
    checks.append({"element": "cta_text", "value": cta, "rating": _rate(cta_score), "detail": "Custom CTA" if cta_score >= 0.9 else ("Generic CTA" if cta_score > 0 else "Missing")})
    score_parts.append(cta_score)

    avg = sum(score_parts) / len(score_parts) if score_parts else 0
    return {"category": "hero_section", "rating": _rate(avg), "score": round(avg, 3), "weight": CATEGORY_WEIGHTS["hero_section"], "checks": checks}


def _scan_site_copy_quality(site_copy: dict, niche_kws: list[str]) -> dict:
    checks = []
    score_parts = []

    text_fields = {
        "about": ("about", "about_body"),
        "about_mission": ("about_mission",),
        "solution_title": ("solution_title",),
        "solution_body": ("solution_body",),
        "problem_title": ("problem_title",),
        "problem_body": ("problem_body",),
        "contact_title": ("contact_title",),
        "contact_body": ("contact_body",),
        "cta_final_title": ("cta_final_title",),
        "cta_final_body": ("cta_final_body",),
    }

    for name, keys in text_fields.items():
        value = ""
        for k in keys:
            v = site_copy.get(k, "")
            if isinstance(v, str) and v:
                value = v
                break
        sec_score = 0.0
        if value and not _has_placeholder(value):
            wc = _word_count(value)
            kw_hits = _niche_keyword_hits(value, niche_kws)
            if wc >= 30 and kw_hits >= 2:
                sec_score = 1.0
            elif wc >= 20 and kw_hits >= 1:
                sec_score = 0.8
            elif wc >= 10:
                sec_score = 0.6
            else:
                sec_score = 0.4
        checks.append({
            "element": f"copy_{name}",
            "value": value[:100] + "..." if len(value) > 100 else value,
            "rating": _rate(sec_score),
            "detail": f"{_word_count(value)} words, {_niche_keyword_hits(value, niche_kws)} niche hits" if value else "Missing",
        })
        score_parts.append(sec_score)

    for list_key, spec in LIST_SECTIONS.items():
        raw = site_copy.get(list_key, [])
        items = raw if isinstance(raw, list) else []
        count = len(items)
        sec_score = 0.0
        placeholder_items = 0
        niche_items = 0

        for item in items:
            text_content = ""
            if isinstance(item, dict):
                text_content = " ".join(str(v) for v in item.values() if isinstance(v, str))
            elif isinstance(item, str):
                text_content = item
            if _has_placeholder(text_content):
                placeholder_items += 1
            if _niche_keyword_hits(text_content, niche_kws) > 0:
                niche_items += 1

        if count >= spec["min_legendary"] and placeholder_items == 0:
            sec_score = 1.0 if niche_items >= count * 0.3 else 0.8
        elif count >= spec["min_good"] and placeholder_items == 0:
            sec_score = 0.7
        elif count > 0:
            sec_score = 0.4 if placeholder_items < count else 0.2
        checks.append({
            "element": f"list_{list_key}",
            "value": f"{count} items",
            "rating": _rate(sec_score),
            "detail": f"{count} items ({niche_items} niche-relevant, {placeholder_items} placeholder)" if count > 0 else "Missing section",
        })
        score_parts.append(sec_score)

    avg = sum(score_parts) / len(score_parts) if score_parts else 0
    return {"category": "site_copy_quality", "rating": _rate(avg), "score": round(avg, 3), "weight": CATEGORY_WEIGHTS["site_copy_quality"], "checks": checks}


def _scan_brand_kit(kit: Any, assets: list) -> dict:
    checks = []
    score_parts = []

    has_kit = kit is not None and getattr(kit, 'status', '') == 'complete'
    checks.append({"element": "brand_kit_status", "value": getattr(kit, 'status', 'none') if kit else "none", "rating": "legendary" if has_kit else ("default" if kit else "missing"), "detail": "Brand kit processed" if has_kit else "No brand kit"})
    score_parts.append(1.0 if has_kit else (0.3 if kit else 0.0))

    asset_types = {}
    for a in assets:
        cls = getattr(a, 'classification', 'unknown') or 'unknown'
        asset_types[cls] = asset_types.get(cls, 0) + 1

    total_assets = len(assets)
    checks.append({"element": "total_assets", "value": total_assets, "rating": _rate(min(total_assets / 5.0, 1.0)), "detail": f"{total_assets} uploaded assets"})
    score_parts.append(min(total_assets / 5.0, 1.0))

    has_logo = asset_types.get("logo", 0) > 0
    checks.append({"element": "logo", "value": asset_types.get("logo", 0), "rating": "legendary" if has_logo else "missing", "detail": f"{asset_types.get('logo', 0)} logo(s)" if has_logo else "No logo uploaded"})
    score_parts.append(1.0 if has_logo else 0.0)

    classified = sum(1 for a in assets if getattr(a, 'classification', None))
    cls_ratio = classified / total_assets if total_assets else 0
    checks.append({"element": "classification_coverage", "value": f"{classified}/{total_assets}", "rating": _rate(cls_ratio), "detail": f"{classified} of {total_assets} assets classified"})
    score_parts.append(cls_ratio)

    key_types = ["team_photo", "product_image", "lifestyle"]
    variety = sum(1 for t in key_types if t in asset_types)
    variety_score = variety / len(key_types)
    checks.append({"element": "asset_variety", "value": dict(asset_types), "rating": _rate(variety_score), "detail": f"{variety}/{len(key_types)} key asset types present"})
    score_parts.append(variety_score)

    avg = sum(score_parts) / len(score_parts) if score_parts else 0
    return {"category": "brand_kit_assets", "rating": _rate(avg), "score": round(avg, 3), "weight": CATEGORY_WEIGHTS["brand_kit_assets"], "checks": checks}


def _scan_visual_theme(brand: dict, niche: str) -> dict:
    checks = []
    score_parts = []

    colors = [brand.get("color_primary", ""), brand.get("color_secondary", ""), brand.get("color_accent", "")]
    custom = sum(1 for c in colors if c and c.lower() not in DEFAULT_COLORS)
    color_score = custom / 3.0
    checks.append({"element": "custom_palette", "value": colors, "rating": _rate(color_score), "detail": f"{custom}/3 custom colors"})
    score_parts.append(color_score)

    has_industry = bool(brand.get("industry_context", ""))
    checks.append({"element": "industry_context", "value": brand.get("industry_context", "")[:80], "rating": "legendary" if has_industry else "default", "detail": "Industry context set" if has_industry else "No industry context"})
    score_parts.append(1.0 if has_industry else 0.3)

    palette_count = len(brand.get("color_palettes", brand.get("palettes", [])))
    pal_score = min(palette_count / 2.0, 1.0) if palette_count else 0.3
    checks.append({"element": "color_palettes", "value": palette_count, "rating": _rate(pal_score), "detail": f"{palette_count} palette(s) available"})
    score_parts.append(pal_score)

    avg = sum(score_parts) / len(score_parts) if score_parts else 0
    return {"category": "visual_theme", "rating": _rate(avg), "score": round(avg, 3), "weight": CATEGORY_WEIGHTS["visual_theme"], "checks": checks}


def _scan_content_depth(site_copy: dict) -> dict:
    checks = []
    score_parts = []

    depth_checks = {
        "features": 4, "testimonials": 3, "faq_items": 4,
        "how_it_works_steps": 3, "pricing_tiers": 3, "stats": 3,
        "resource_items": 3, "team_members": 2, "contact_methods": 2,
    }

    for key, ideal in depth_checks.items():
        raw = site_copy.get(key, [])
        count = len(raw) if isinstance(raw, list) else 0
        ratio = min(count / ideal, 1.0) if ideal else 0
        checks.append({"element": f"depth_{key}", "value": count, "rating": _rate(ratio), "detail": f"{count}/{ideal} items"})
        score_parts.append(ratio)

    avg = sum(score_parts) / len(score_parts) if score_parts else 0
    return {"category": "content_depth", "rating": _rate(avg), "score": round(avg, 3), "weight": CATEGORY_WEIGHTS["content_depth"], "checks": checks}


def _scan_contact_info(site_copy: dict) -> dict:
    checks = []
    score_parts = []

    methods = site_copy.get("contact_methods", [])
    if not isinstance(methods, list):
        methods = []

    has_email = any("@" in str(m.get("value", "")) for m in methods if isinstance(m, dict))
    has_phone = any(any(w in str(m.get("label", "")).lower() for w in ["phone", "tel", "call"]) for m in methods if isinstance(m, dict))
    has_address = any(any(w in str(m.get("label", "")).lower() for w in ["address", "location", "office"]) or "," in str(m.get("value", "")) for m in methods if isinstance(m, dict))
    has_social = any(any(w in str(m.get("value", "")).lower() for w in ["facebook", "twitter", "instagram", "linkedin", "tiktok", "youtube"]) for m in methods if isinstance(m, dict))
    has_link = any("http" in str(m.get("value", "")).lower() for m in methods if isinstance(m, dict))

    placeholder_contacts = sum(1 for m in methods if isinstance(m, dict) and _has_placeholder(str(m.get("value", ""))))

    checks.append({"element": "email", "value": has_email, "rating": "legendary" if has_email else "missing", "detail": "Email found" if has_email else "No email"})
    score_parts.append(1.0 if has_email else 0.0)

    checks.append({"element": "phone", "value": has_phone, "rating": "good" if has_phone else "default", "detail": "Phone found" if has_phone else "No phone"})
    score_parts.append(0.8 if has_phone else 0.3)

    checks.append({"element": "address_or_location", "value": has_address, "rating": "good" if has_address else "default", "detail": "Address found" if has_address else "No address"})
    score_parts.append(0.8 if has_address else 0.3)

    checks.append({"element": "social_or_links", "value": has_social or has_link, "rating": "good" if has_social or has_link else "default", "detail": "Social/web links found" if has_social or has_link else "No links"})
    score_parts.append(0.8 if has_social or has_link else 0.3)

    checks.append({"element": "placeholder_contacts", "value": placeholder_contacts, "rating": "legendary" if placeholder_contacts == 0 and methods else ("default" if placeholder_contacts > 0 else "missing"), "detail": f"{placeholder_contacts} placeholder(s)" if placeholder_contacts else "All authentic"})
    score_parts.append(1.0 if placeholder_contacts == 0 and methods else 0.2)

    avg = sum(score_parts) / len(score_parts) if score_parts else 0
    return {"category": "contact_info", "rating": _rate(avg), "score": round(avg, 3), "weight": CATEGORY_WEIGHTS["contact_info"], "checks": checks}


def _scan_sales_letter(sales_letter: str | None, niche_kws: list[str]) -> dict:
    checks = []
    score_parts = []

    if not sales_letter:
        return {"category": "sales_letter", "rating": "missing", "score": 0.0, "weight": CATEGORY_WEIGHTS["sales_letter"], "checks": [
            {"element": "sales_letter_presence", "value": False, "rating": "missing", "detail": "No sales letter generated"}
        ]}

    wc = _word_count(sales_letter)
    wc_score = min(wc / 500.0, 1.0)
    checks.append({"element": "word_count", "value": wc, "rating": _rate(wc_score), "detail": f"{wc} words (target: 500+)"})
    score_parts.append(wc_score)

    has_placeholder = _has_placeholder(sales_letter)
    checks.append({"element": "placeholder_free", "value": not has_placeholder, "rating": "legendary" if not has_placeholder else "default", "detail": "No placeholders" if not has_placeholder else "Contains placeholder text"})
    score_parts.append(1.0 if not has_placeholder else 0.2)

    kw_hits = _niche_keyword_hits(sales_letter, niche_kws)
    kw_score = min(kw_hits / 5.0, 1.0)
    checks.append({"element": "niche_relevance", "value": kw_hits, "rating": _rate(kw_score), "detail": f"{kw_hits} niche keyword mentions"})
    score_parts.append(kw_score)

    avg = sum(score_parts) / len(score_parts) if score_parts else 0
    return {"category": "sales_letter", "rating": _rate(avg), "score": round(avg, 3), "weight": CATEGORY_WEIGHTS["sales_letter"], "checks": checks}


def _scan_hero_image(hero_image_url: str | None, brand_kit_assets: list) -> dict:
    checks = []
    score_parts = []

    if not hero_image_url:
        return {"category": "hero_image", "rating": "missing", "score": 0.0, "weight": CATEGORY_WEIGHTS["hero_image"], "checks": [
            {"element": "hero_image_presence", "value": False, "rating": "missing", "detail": "No hero image"}
        ]}

    file_exists = os.path.isfile(hero_image_url.lstrip("/"))
    checks.append({"element": "file_exists", "value": file_exists, "rating": "legendary" if file_exists else "default", "detail": "File present on disk" if file_exists else "File missing"})
    score_parts.append(1.0 if file_exists else 0.0)

    bk_hero = any(getattr(a, 'classification', '') in ('hero_banner', 'hero') for a in brand_kit_assets)
    is_dalle = "/dalle/" in (hero_image_url or "") or "/hero_" in (hero_image_url or "") or "/generated/" in (hero_image_url or "")
    origin = "brand_kit" if bk_hero else ("ai_generated" if is_dalle else "unknown")
    origin_score = 1.0 if bk_hero else (0.8 if is_dalle else 0.5)
    checks.append({"element": "image_origin", "value": origin, "rating": _rate(origin_score), "detail": f"Origin: {origin}"})
    score_parts.append(origin_score)

    avg = sum(score_parts) / len(score_parts) if score_parts else 0
    return {"category": "hero_image", "rating": _rate(avg), "score": round(avg, 3), "weight": CATEGORY_WEIGHTS["hero_image"], "checks": checks}


def _generate_suggestions(categories: list[dict]) -> list[dict]:
    suggestions = []
    priority_order = {"missing": 0, "default": 1, "good": 2, "legendary": 3}

    sorted_cats = sorted(categories, key=lambda c: (priority_order.get(c["rating"], 3), c["score"]))

    for cat in sorted_cats:
        if cat["rating"] in ("legendary",):
            continue
        for check in cat.get("checks", []):
            if check["rating"] in ("missing", "default"):
                sug = _suggestion_for(cat["category"], check["element"], check["rating"], check["detail"])
                if sug:
                    suggestions.append(sug)

    return suggestions[:15]


def _suggestion_for(category: str, element: str, rating: str, detail: str) -> dict | None:
    priority = "high" if rating == "missing" else "medium"

    suggestion_map = {
        ("brand_identity", "brand_name"): "Create a unique, memorable brand name that reflects your niche — avoid using the raw domain name.",
        ("brand_identity", "tagline"): "Add a compelling tagline that communicates your value proposition with niche-specific language.",
        ("brand_identity", "brand_colors"): "Customize your brand colors — the current palette uses system defaults. Choose colors that resonate with your industry.",
        ("hero_section", "headline"): "Write a powerful, niche-specific headline that immediately tells visitors what you offer.",
        ("hero_section", "cta_text"): "Replace the generic CTA with action-specific text (e.g., 'Book Your Free Consultation' instead of 'Get Started').",
        ("brand_kit_assets", "logo"): "Upload a custom logo to your brand kit — it appears in the hero, nav, and footer.",
        ("brand_kit_assets", "total_assets"): "Upload more brand assets (team photos, product images, lifestyle shots) for a richer site.",
        ("sales_letter", "sales_letter_presence"): "Generate a sales letter — it's used for marketplace listings and adds credibility.",
        ("hero_image", "hero_image_presence"): "Generate or upload a hero image — it's the first visual impression visitors get.",
        ("contact_info", "email"): "Add a real email address to your contact section for credibility.",
    }

    text = suggestion_map.get((category, element))
    if not text:
        if rating == "missing":
            text = f"Add content for {element.replace('_', ' ')} in the {category.replace('_', ' ')} section."
        elif rating == "default":
            text = f"Customize {element.replace('_', ' ')} — current content appears generic or uses defaults."
        else:
            return None

    return {"category": category, "element": element, "priority": priority, "suggestion": text, "current": detail}


def scan_package(package, brand_kit=None, brand_kit_assets=None, context_keywords=None) -> dict:
    domain = package.domain_name
    niche = package.chosen_niche or ""
    brand = package.brand or {}
    site_copy = package.site_copy or {}
    sales_letter = package.sales_letter
    hero_image_url = package.hero_image_url

    if brand_kit_assets is None:
        brand_kit_assets = []

    niche_kws = _extract_niche_keywords(domain, niche, context_keywords)

    categories = [
        _scan_brand_identity(brand, domain, niche, niche_kws),
        _scan_hero_section(site_copy, niche_kws),
        _scan_site_copy_quality(site_copy, niche_kws),
        _scan_brand_kit(brand_kit, brand_kit_assets),
        _scan_visual_theme(brand, niche),
        _scan_content_depth(site_copy),
        _scan_contact_info(site_copy),
        _scan_sales_letter(sales_letter, niche_kws),
        _scan_hero_image(hero_image_url, brand_kit_assets),
    ]

    total_weight = sum(c["weight"] for c in categories)
    weighted_score = sum(c["score"] * c["weight"] for c in categories) / total_weight if total_weight else 0
    overall_score = round(weighted_score * 100)

    total_checks = sum(len(c.get("checks", [])) for c in categories)
    legendary_checks = sum(1 for c in categories for ch in c.get("checks", []) if ch["rating"] == "legendary")
    good_checks = sum(1 for c in categories for ch in c.get("checks", []) if ch["rating"] == "good")
    default_checks = sum(1 for c in categories for ch in c.get("checks", []) if ch["rating"] == "default")
    missing_checks = sum(1 for c in categories for ch in c.get("checks", []) if ch["rating"] == "missing")

    if overall_score >= 85:
        overall_rating = "legendary"
    elif overall_score >= 65:
        overall_rating = "good"
    elif overall_score >= 35:
        overall_rating = "default"
    else:
        overall_rating = "missing"

    suggestions = _generate_suggestions(categories)

    return {
        "domain": domain,
        "package_id": package.id,
        "niche": niche,
        "niche_keywords": niche_kws,
        "overall_score": overall_score,
        "overall_rating": overall_rating,
        "summary": {
            "total_checks": total_checks,
            "legendary": legendary_checks,
            "good": good_checks,
            "default": default_checks,
            "missing": missing_checks,
        },
        "categories": {c["category"]: {
            "rating": c["rating"],
            "score": round(c["score"] * 100),
            "weight": c["weight"],
            "checks": c["checks"],
        } for c in categories},
        "suggestions": suggestions,
    }
