"""Connection-scoped CRM pipeline/stage alias bindings. Not CRMObjectRef.

Storage is existing connector connection config_json:

    stage_map: { alias: provider_value }
    pipeline_map: { alias: provider_value }  # HubSpot only

Salesforce has no portable pipeline resource. Bindings never select a provider.
"""
from __future__ import annotations

import re
from typing import Any

from pydantic import BaseModel, ConfigDict, Field, StrictStr

CRM_RESOURCE_ALIAS_PATTERN = r"^[a-z][a-z0-9_-]{0,63}$"
_ALIAS = re.compile(CRM_RESOURCE_ALIAS_PATTERN)


class CRMStageRef(BaseModel):
    model_config = ConfigDict(extra="forbid")
    alias: StrictStr = Field(pattern=CRM_RESOURCE_ALIAS_PATTERN)


class CRMPipelineRef(BaseModel):
    model_config = ConfigDict(extra="forbid")
    alias: StrictStr = Field(pattern=CRM_RESOURCE_ALIAS_PATTERN)


def is_crm_resource_alias(value: str) -> bool:
    return bool(_ALIAS.fullmatch(value))


def string_map(raw: Any) -> dict[str, str]:
    if not isinstance(raw, dict):
        return {}
    out: dict[str, str] = {}
    for key, value in raw.items():
        if isinstance(key, str) and is_crm_resource_alias(key) and isinstance(value, str) and value:
            out[key] = value
    return out


def stage_map_from_config(config: dict[str, Any] | None) -> dict[str, str]:
    return string_map((config or {}).get("stage_map"))


def pipeline_map_from_config(config: dict[str, Any] | None) -> dict[str, str]:
    return string_map((config or {}).get("pipeline_map"))


def resolve_alias(
    alias: str,
    mapping: dict[str, str],
    *,
    unbound_code: str,
) -> str:
    if not is_crm_resource_alias(alias):
        raise CRMResourceError("crm_validation_failed")
    bound = mapping.get(alias)
    if not bound:
        raise CRMResourceError(unbound_code)
    return bound


def reverse_alias(provider_value: Any, mapping: dict[str, str]) -> str | None:
    if not isinstance(provider_value, str) or not provider_value:
        return None
    matches = [alias for alias, bound in mapping.items() if bound == provider_value]
    if not matches:
        return None
    return sorted(matches)[0]


class CRMResourceError(ValueError):
    def __init__(self, code: str) -> None:
        self.code = code
        super().__init__(code)
