from __future__ import annotations

import hashlib
import json
import re
from datetime import date, datetime
from typing import Any


NULL_MARKERS = {"", "nan", "none", "null", "nat", "未填写", "暂无", "-"}


def clean_text(value: Any) -> str:
    if value is None:
        return ""
    text = str(value).strip()
    text = text.replace("\u3000", " ")
    text = re.sub(r"\s+", " ", text)
    return text


def is_blank(value: Any) -> bool:
    return clean_text(value).lower() in NULL_MARKERS


def normalize_optional_text(value: Any) -> str | None:
    text = clean_text(value)
    if text.lower() in NULL_MARKERS:
        return None
    return text


def normalize_phone(value: Any) -> str | None:
    text = clean_text(value)
    if not text:
        return None
    digits = re.sub(r"\D+", "", text)
    if len(digits) >= 11:
        return digits[-11:]
    if len(digits) >= 7:
        return digits
    return None


def normalize_date(value: Any) -> str | None:
    text = clean_text(value)
    if not text or text.lower() in NULL_MARKERS:
        return None
    text = text.replace("年", ".").replace("月", ".").replace("日", "")
    text = text.replace("/", ".").replace("-", ".")
    text = re.sub(r"\.+", ".", text).strip(".")
    candidates = [
        "%Y.%m.%d",
        "%Y.%m",
        "%m.%d",
    ]
    for fmt in candidates:
        try:
            parsed = datetime.strptime(text, fmt)
            year = parsed.year
            if fmt == "%m.%d":
                year = date.today().year
            month = parsed.month
            day = parsed.day if "%d" in fmt else 1
            return date(year, month, day).isoformat()
        except ValueError:
            continue
    if re.fullmatch(r"\d{4}\.\d{1,2}\.\d{1,2}", text):
        parts = [int(item) for item in text.split(".")]
        return date(parts[0], parts[1], parts[2]).isoformat()
    return None


def normalize_tags(value: Any) -> list[str]:
    if isinstance(value, (list, tuple, set)):
        return [clean_text(item) for item in value if clean_text(item)]
    text = clean_text(value)
    if not text:
        return []
    parts = re.split(r"[,，、/\s]+", text)
    return [item for item in (part.strip() for part in parts) if item]


def normalize_money(value: Any) -> str | None:
    text = clean_text(value)
    if not text or text.lower() in NULL_MARKERS:
        return None
    return text


def normalize_value_for_type(value: Any, value_type: str) -> str | None:
    if value_type == "date":
        return normalize_date(value)
    if value_type == "phone":
        return normalize_phone(value)
    if value_type == "tags":
        tags = normalize_tags(value)
        return json.dumps(tags, ensure_ascii=False)
    if value_type == "money":
        return normalize_money(value)
    return normalize_optional_text(value)


def guess_value_type(label: str, values: list[Any]) -> str:
    joined_label = clean_text(label).lower()
    if any(keyword in joined_label for keyword in ["微信", "wechat", "wx"]):
        return "string"
    if any(keyword in joined_label for keyword in ["电话", "手机", "手机号", "mobile", "phone"]):
        return "phone"
    if any(keyword in joined_label for keyword in ["生日", "日期", "时间", "生效", "到期", "续费", "回访", "预约"]):
        return "date"
    if any(keyword in joined_label for keyword in ["金额", "保费", "保额", "消费", "价格", "佣金"]):
        return "money"

    non_blank = [clean_text(value) for value in values if not is_blank(value)]
    if not non_blank:
        return "string"

    date_hits = sum(1 for value in non_blank if normalize_date(value))
    if date_hits == len(non_blank):
        return "date"

    phone_hits = sum(1 for value in non_blank if normalize_phone(value))
    if phone_hits == len(non_blank):
        return "phone"

    numeric_hits = sum(1 for value in non_blank if re.fullmatch(r"[\d.]+", value))
    if numeric_hits == len(non_blank):
        return "number"

    return "string"


def stable_field_key(label: str) -> str:
    text = clean_text(label).lower()
    ascii_text = re.sub(r"[^a-z0-9]+", "_", text).strip("_")
    if ascii_text:
        return ascii_text[:48]
    digest = hashlib.sha1(clean_text(label).encode("utf-8")).hexdigest()[:12]
    return f"fld_{digest}"


def dumps_json(data: Any) -> str:
    return json.dumps(data, ensure_ascii=False, sort_keys=True)
