from __future__ import annotations

import json
import re


DEFAULT_ICON_COLOR = "qing-orange"
WORKSPACE_ICON_COLORS: tuple[str, ...] = (
    "qing-orange",
    "yellow",
    "green",
    "emerald",
    "blue",
    "azure",
    "indigo",
    "qing-purple",
    "purple",
    "pink",
    "red",
    "orange",
)
WORKSPACE_ICON_NAMES: tuple[str, ...] = (
    "user",
    "user-group",
    "user-remove",
    "user-add",
    "user-circle",
    "base-camera",
    "view-grid",
    "inbox",
    "inbox-in",
    "share",
    "sitemap",
    "airplane",
    "template",
    "music-note",
    "movie-play",
    "clock",
    "document",
    "document-search",
    "clipboard-check",
    "document-download",
    "document-text",
    "clipboard-copy",
    "presentation-chart-bar",
    "chart-square-bar",
    "database",
    "server",
    "calendar",
    "mail",
    "annotation",
    "chat",
    "bell",
    "key",
    "shopping-bag",
    "download",
    "eye",
    "eye-off",
    "emoji-happy",
    "emoji-sad",
    "sun",
    "moon",
    "cloud",
    "lightning-bolt",
    "fire",
    "star",
    "sparkles",
    "heart",
    "cake",
    "gift",
    "light-bulb",
    "exclamation",
    "cog",
    "thumb-up",
    "thumb-down",
    "cloud-download",
    "cloud-upload",
    "printer",
    "phone-incoming",
    "phone-missed-call",
    "terminal",
    "search-circle",
    "x-circle",
    "check-circle",
    "exclamation-circle",
    "question-mark-circle",
    "information-circle",
    "academic-cap",
    "briefcase",
    "home",
    "phone",
    "photograph",
    "puzzle",
    "color-swatch",
    "lock-open",
    "lock-closed",
    "shield-check",
    "shield-exclamation",
    "currency-dollar",
    "currency-yen",
    "globe",
    "at-symbol",
    "slack",
    "microphone",
    "speakerphone",
    "trash",
    "book-open",
    "truck",
    "filter",
    "essetional-filter-search",
    "essetional-filter-tick",
    "table",
    "calculator",
    "location-radar",
    "essetional-weight",
    "school-award",
    "comp-cloud-connection",
    "comp-cloud-remove",
    "comp-cpu-charge",
    "comp-cpu-setting",
    "comp-cpu",
    "comp-devices",
    "comp-driver-2",
    "comp-driver-refresh",
    "location-global",
    "location-location",
    "location-map",
    "location-gps",
    "essetional-ranking",
    "chart-bar",
    "business-graph",
    "business-status-up",
    "business-trend-down",
    "business-trend-up",
    "business-presention-chart",
    "business-favorite-chart",
    "business-health",
    "receipt-refund",
    "receipt-tax",
    "money-receipt-2-1",
    "money-transaction-minus",
    "action-hourglass-full",
    "action-work",
    "bug-f",
    "essetional-pet",
    "files-folder",
    "badge-check",
    "money-wallet-1",
    "money-ticket",
    "money-money",
    "money-tag",
    "money-wallet-2",
    "business-personalcard",
    "car-airplane",
    "car-bus",
    "car-car",
    "car-driving",
    "car-gas-station",
    "car-smart-car",
    "car-ship",
    "location-map-1",
    "location-route-square",
    "cone",
    "design-brush-4",
    "paint-roll",
    "wrench-f",
    "essetional-reserve",
    "essetional-broom",
    "design-brush-2",
    "essetional-judge",
    "design-bucket",
    "palette",
    "comp-electricity",
    "vial",
    "beaker",
    "leaf-f",
    "cursor-click",
    "solid-search-alt-2",
    "md-library",
    "building-3",
    "office-building",
    "building-hospital",
    "school",
    "store",
    "video-camera-vintage-f",
    "comp-monitor",
    "delivery-truck",
    "delivery-box-1",
    "delivery-box-add",
    "delivery-box-remove",
    "settings-setting-3",
    "document-duplicate",
    "essetional-flag-2",
    "flag",
    "icon-currency-dollar",
    "clipboard-list",
    "save-as",
    "wifi",
    "status-online",
    "scissors",
    "globe-alt",
    "ban",
    "finger-print",
    "qrcode",
    "paper-clip",
    "translate",
    "cube-transparent",
    "variable",
    "switch-vertical",
    "sports-baseball",
    "sports-basketball",
    "sports-soccer",
    "sports-football",
    "sports-volleyball",
)
GENERIC_WORKSPACE_ICON_NAMES: tuple[str, ...] = ("template",)
DEFAULT_ICON_STYLE_POOL: tuple[tuple[str, str], ...] = (
    ("briefcase", "qing-orange"),
    ("calendar", "emerald"),
    ("files-folder", "azure"),
    ("shield-check", "indigo"),
    ("user-group", "qing-purple"),
    ("chart-square-bar", "blue"),
    ("clock", "pink"),
    ("document-text", "green"),
    ("home", "orange"),
    ("globe", "red"),
)
LEGACY_EX_ICON_MAP = {
    "ex-alert-outlined": "exclamation-circle",
    "ex-clock-outlined": "clock",
    "ex-folder-outlined": "folder",
    "ex-form-outlined": "template",
    "ex-home-outlined": "home",
    "ex-user-outlined": "user",
}

ICON_ALIAS_MAP = {
    "archive-box": "template",
    "beaker": "beaker",
    "briefcase": "briefcase",
    "building-office": "office-building",
    "calendar": "calendar",
    "chat": "chat",
    "chat-bubble-left-right": "chat",
    "chart-bar": "chart-bar",
    "chart-square-bar": "chart-square-bar",
    "check-badge": "badge-check",
    "check-circle": "check-circle",
    "clipboard-check": "clipboard-check",
    "clock": "clock",
    "cube": "cube-transparent",
    "document-text": "document-text",
    "folder": "files-folder",
    "home": "home",
    "office-building": "office-building",
    "shield-check": "shield-check",
    "template": "template",
    "user": "user",
    "user-group": "user-group",
    "users": "user-group",
    "view-grid": "view-grid",
    "warning": "exclamation-circle",
}


def encode_workspace_icon(
    *,
    icon: str | None,
    color: str | None,
    title: str,
    fallback_icon_name: str | None = None,
) -> str:
    if icon and _looks_like_icon_json(icon):
        return icon
    normalized_icon_name = normalize_workspace_icon_name(icon or fallback_icon_name)
    icon_name = normalized_icon_name
    icon_color = color or DEFAULT_ICON_COLOR
    if icon_name:
        return json.dumps(
            {
                "icon": None,
                "iconColor": icon_color,
                "iconName": icon_name,
                "iconText": None,
            },
            ensure_ascii=False,
            separators=(",", ":"),
        )
    icon_text = (title or "未")[:1]
    return json.dumps(
        {
            "iconColor": icon_color,
            "iconText": icon_text,
        },
        ensure_ascii=False,
        separators=(",", ":"),
    )


def choose_default_workspace_icon_style(*, title: str) -> tuple[str, str]:
    seed_text = (title or "未命名").strip() or "未命名"
    digest = 0
    for index, char in enumerate(seed_text):
        digest = (digest * 131 + ord(char) + index) % 1_000_003
    return DEFAULT_ICON_STYLE_POOL[digest % len(DEFAULT_ICON_STYLE_POOL)]


def parse_workspace_icon(value: str | None) -> tuple[str | None, str | None, str | None]:
    if not value:
        return None, None, None
    stripped = str(value).strip()
    if not stripped:
        return None, None, None
    if _looks_like_icon_json(stripped):
        try:
            payload = json.loads(stripped)
        except Exception:
            return normalize_workspace_icon_name(stripped), None, None
        icon_name = normalize_workspace_icon_name(payload.get("iconName"))
        icon_color = str(payload.get("iconColor") or "").strip() or None
        icon_text = str(payload.get("iconText") or "").strip() or None
        return icon_name, icon_color, icon_text
    return normalize_workspace_icon_name(stripped), None, None


def workspace_icon_config(value: str | None) -> dict[str, str | None]:
    icon_name, icon_color, icon_text = parse_workspace_icon(value)
    raw = str(value).strip() if value not in (None, "") else None
    return {
        "icon_name": icon_name,
        "icon_color": icon_color,
        "icon_text": icon_text,
        "raw": raw,
    }


def workspace_icon_catalog_payload() -> dict[str, object]:
    return {
        "icon_names": list(WORKSPACE_ICON_NAMES),
        "icon_colors": list(WORKSPACE_ICON_COLORS),
        "generic_icon_names": list(GENERIC_WORKSPACE_ICON_NAMES),
        "notes": [
            "Use explicit icon + color for app/package/portal creation.",
            "Do not use template for newly created workspace resources.",
            "The CLI validates candidates only; it does not infer an icon from business names.",
        ],
        "common_examples": {
            "employee": ["business-personalcard", "user-group", "user"],
            "task": ["clipboard-check", "action-work"],
            "worklog": ["clock", "action-hourglass-full"],
            "order": ["delivery-box-1", "shopping-bag"],
            "payment": ["money-receipt-2-1", "money-wallet-1"],
            "opportunity": ["business-graph", "business-trend-up"],
            "dashboard": ["view-grid", "chart-square-bar", "presentation-chart-bar"],
        },
    }


def validate_workspace_icon_choice(
    *,
    icon: str | None,
    color: str | None,
    require_explicit: bool,
    disallow_generic: bool,
) -> tuple[bool, str | None, str | None, dict[str, object]]:
    normalized_icon = _normalize_workspace_icon_candidate(icon)
    normalized_color = str(color or "").strip() or None
    details: dict[str, object] = {
        "icon": icon,
        "normalized_icon": normalized_icon,
        "color": color,
        "icon_catalog_command": "qingflow --json builder icon catalog",
    }
    if require_explicit and not normalized_icon:
        return False, "WORKSPACE_ICON_REQUIRED", "icon is required when creating a workspace resource", details
    if require_explicit and not normalized_color:
        return False, "WORKSPACE_ICON_COLOR_REQUIRED", "color is required when creating a workspace resource", details
    if normalized_icon and normalized_icon not in WORKSPACE_ICON_NAMES:
        details["allowed_icon_names"] = list(WORKSPACE_ICON_NAMES)
        return False, "WORKSPACE_ICON_NOT_FOUND", "icon is not in the workspace icon catalog", details
    if normalized_color and normalized_color not in WORKSPACE_ICON_COLORS:
        details["allowed_icon_colors"] = list(WORKSPACE_ICON_COLORS)
        return False, "WORKSPACE_ICON_COLOR_NOT_FOUND", "color is not in the workspace icon color catalog", details
    if disallow_generic and normalized_icon in GENERIC_WORKSPACE_ICON_NAMES:
        details["generic_icon_names"] = list(GENERIC_WORKSPACE_ICON_NAMES)
        return False, "GENERIC_WORKSPACE_ICON_NOT_ALLOWED", "template is a generic icon and is not allowed for new workspace resources", details
    return True, None, None, details


def _normalize_workspace_icon_candidate(icon: str | None) -> str | None:
    if not icon:
        return None
    raw = str(icon).strip()
    if not raw:
        return None
    if _looks_like_icon_json(raw):
        try:
            payload = json.loads(raw)
        except Exception:
            return None
        return _normalize_workspace_icon_candidate(payload.get("iconName"))
    normalized = raw.lower()
    if normalized in WORKSPACE_ICON_NAMES:
        return normalized
    legacy = LEGACY_EX_ICON_MAP.get(normalized)
    if legacy in WORKSPACE_ICON_NAMES:
        return legacy
    return normalized


def encode_workspace_icon_with_defaults(
    *,
    icon: str | None,
    color: str | None,
    title: str,
    fallback_icon_name: str | None = None,
    existing_icon: str | None = None,
) -> str:
    if icon and _looks_like_icon_json(icon):
        return icon
    if not icon and not color and existing_icon:
        existing_payload = str(existing_icon).strip()
        if existing_payload:
            return existing_payload
    existing_icon_name, existing_icon_color, _ = parse_workspace_icon(existing_icon)
    default_icon, default_color = choose_default_workspace_icon_style(title=title)
    resolved_icon = icon or existing_icon_name
    resolved_color = color or existing_icon_color
    if not resolved_icon:
        if icon is None and color is None:
            resolved_icon = default_icon
        else:
            resolved_icon = fallback_icon_name or default_icon
    if not resolved_color:
        resolved_color = default_color
    return encode_workspace_icon(
        icon=resolved_icon,
        color=resolved_color,
        title=title,
        fallback_icon_name=fallback_icon_name,
    )


def normalize_workspace_icon_name(icon: str | None) -> str | None:
    if not icon:
        return None
    if _looks_like_icon_json(icon):
        try:
            payload = json.loads(icon)
        except Exception:
            return "template"
        return normalize_workspace_icon_name(payload.get("iconName")) or "template"
    normalized = icon.strip().lower()
    if normalized in LEGACY_EX_ICON_MAP:
        return LEGACY_EX_ICON_MAP[normalized]
    if normalized in ICON_ALIAS_MAP:
        return ICON_ALIAS_MAP[normalized]
    for token in normalized.replace("_", "-").split("-"):
        if token in ICON_ALIAS_MAP:
            return ICON_ALIAS_MAP[token]
    if "folder" in normalized or "package" in normalized:
        return "files-folder"
    if "home" in normalized:
        return "home"
    if "portal" in normalized or "dashboard" in normalized:
        return "view-grid"
    if "user" in normalized or "member" in normalized or "contact" in normalized:
        return "user"
    if "clock" in normalized or "time" in normalized or "schedule" in normalized or "log" in normalized:
        return "clock"
    if "risk" in normalized or "alert" in normalized or "warn" in normalized or "safety" in normalized:
        return "exclamation-circle"
    if re.fullmatch(r"[a-z0-9-]+", normalized):
        return normalized
    return "template"


def _looks_like_icon_json(value: str) -> bool:
    stripped = value.strip()
    return stripped.startswith("{") and stripped.endswith("}")
