from __future__ import annotations

import math
import re
from datetime import date, datetime, time
from decimal import Decimal, InvalidOperation
from typing import Sequence

from openpyxl.utils.datetime import from_excel

from market_api.importer.mappings import SheetSpec


BLANK_TEXT_VALUES = {"", "/", "／", "-", "--", "—", "——", "无", "null", "none", "nan"}


def is_blank(value: object) -> bool:
    if value is None:
        return True
    if isinstance(value, float) and math.isnan(value):
        return True
    if isinstance(value, str):
        return value.strip().lower() in BLANK_TEXT_VALUES
    return False


def normalize_cell(value: object, kind: str) -> object:
    if is_blank(value):
        return None
    match kind:
        case "integer":
            return normalize_integer(value)
        case "numeric":
            return normalize_numeric(value)
        case "timestamp":
            return normalize_timestamp(value)
        case _:
            return normalize_text(value)


def normalize_text(value: object) -> str | None:
    if is_blank(value):
        return None
    if isinstance(value, datetime):
        return value.strftime("%Y-%m-%d %H:%M:%S")
    if isinstance(value, date):
        return value.strftime("%Y-%m-%d")
    if isinstance(value, float) and value.is_integer():
        return str(int(value))
    return str(value).strip()


def normalize_numeric(value: object) -> Decimal | None:
    if isinstance(value, bool) or is_blank(value):
        return None
    if isinstance(value, int):
        return Decimal(value)
    if isinstance(value, float):
        if math.isnan(value) or math.isinf(value):
            return None
        return Decimal(str(value))
    text = normalize_text(value)
    if text is None:
        return None
    cleaned = (
        text.replace(",", "")
        .replace("，", "")
        .replace("万元", "")
        .replace("元", "")
        .strip()
    )
    if not re.fullmatch(r"[-+]?\d+(\.\d+)?", cleaned):
        return None
    try:
        return Decimal(cleaned)
    except InvalidOperation:
        return None


def normalize_integer(value: object) -> int | None:
    numeric = normalize_numeric(value)
    if numeric is None:
        return None
    if numeric == numeric.to_integral_value():
        return int(numeric)
    return None


def normalize_timestamp(value: object) -> datetime | None:
    if is_blank(value):
        return None
    if isinstance(value, datetime):
        return value.replace(tzinfo=None)
    if isinstance(value, date):
        return datetime.combine(value, time.min)
    if isinstance(value, int | float) and not isinstance(value, bool):
        try:
            converted = from_excel(value)
        except (TypeError, ValueError, OverflowError):
            return None
        if isinstance(converted, datetime):
            return converted.replace(tzinfo=None)
        if isinstance(converted, time):
            return datetime.combine(date(1899, 12, 30), converted)
        return None

    text = normalize_text(value)
    if text is None:
        return None
    text = text.replace("T", " ").removesuffix("Z")
    for fmt in (
        "%Y-%m-%d %H:%M:%S",
        "%Y/%m/%d %H:%M:%S",
        "%Y-%m-%d %H:%M",
        "%Y/%m/%d %H:%M",
        "%Y-%m-%d",
        "%Y/%m/%d",
        "%Y年%m月%d日",
    ):
        try:
            return datetime.strptime(text, fmt)
        except ValueError:
            continue
    return None


def row_to_record(
    row: Sequence[object],
    spec: SheetSpec,
    source_row_number: int,
) -> dict[str, object]:
    record: dict[str, object] = {"源行号": source_row_number}
    for column in spec.columns:
        value = row[column.excel_index] if column.excel_index < len(row) else None
        record[column.db_name] = normalize_cell(value, column.kind)
    return record

