
import logging
from typing import Any, Dict, Iterable, List, Optional, Tuple

from utils.helpers import get_nested, get_attr, get_attr_loose, to_int, to_float

logger = logging.getLogger(__name__)


def parse_attom_salescomps_v2(payload: Dict[str, Any]) -> Tuple[Optional[Dict[str, Any]], List[Dict[str, Any]]]:
    """
     parse ATTOM v2 Sales Comparables. Returns (subject, comps).
    """
    subject: Optional[Dict[str, Any]] = None
    comps: List[Dict[str, Any]] = []

    def parse_subject_block(s) -> Optional[Dict[str, Any]]:
        if isinstance(s, list) and s:
            s = s[0]
        if not isinstance(s, dict):
            return None

        def g(obj, *path):
            v = get_nested(obj, *path)
            if v is None and path and isinstance(path[-1], str) and path[-1].startswith("@"):
                alt = "@_" + path[-1][1:]
                v = get_nested(obj, *(path[:-1] + (alt,)))
                if v is None:
                    plain = path[-1][1:]
                    v = get_nested(obj, *(path[:-1] + (plain,)))
            return v

        return {
            "street": s.get("@_StreetAddress") or s.get("@StreetAddress") or s.get("StreetAddress"),
            "city":   s.get("@_City") or s.get("@City") or s.get("City"),
            "state":  s.get("@_State") or s.get("@State") or s.get("State"),
            "zip":    s.get("@_PostalCode") or s.get("@PostalCode") or s.get("PostalCode"),
            "sqft":   to_int(g(s, "STRUCTURE", "@GrossLivingAreaSquareFeetCount")),
            "beds":   to_int(g(s, "STRUCTURE", "@TotalBedroomCount")),
            "baths":  to_float(g(s, "STRUCTURE", "@TotalBathroomCount")),
            "year_built": to_int(g(s, "STRUCTURE", "STRUCTURE_ANALYSIS", "@PropertyStructureBuiltYear")),
            "lat":    to_float(g(s, "_IDENTIFICATION", "@LatitudeNumber")),
            "lon":    to_float(g(s, "_IDENTIFICATION", "@LongitudeNumber")),
            "last_sale_price": to_int(g(s, "SALES_HISTORY", "@PropertySalesAmount")),
            "last_sale_date":  g(s, "SALES_HISTORY", "@PropertySalesDate"),
            "source": "attom/v2/salescomparables",
        }

    def parse_comp_item(comp: Dict[str, Any]) -> Optional[Dict[str, Any]]:
        if not isinstance(comp, dict):
            return None
        sales = comp.get("SALES_HISTORY", {}) or {}
        struct = comp.get("STRUCTURE", {}) or {}

        addr_st   = get_attr_loose(comp, "StreetAddress")
        addr_city = get_attr_loose(comp, "City")
        addr_state= get_attr_loose(comp, "State")
        addr_zip  = get_attr_loose(comp, "PostalCode")

        raw_price = get_attr(sales, "@PropertySalesAmount", "@_PropertySalesAmount", "PropertySalesAmount")
        raw_sqft  = get_attr(struct, "@GrossLivingAreaSquareFeetCount", "@_GrossLivingAreaSquareFeetCount", "GrossLivingAreaSquareFeetCount")

        price = to_int(raw_price)
        sqft  = to_int(raw_sqft)
        if not price or price <= 0:
            return None
        if not sqft or sqft <= 0:
            return None

        return {
            "address": ", ".join([p for p in [addr_st, addr_city, addr_state, addr_zip] if p]),
            "sale_price": price,
            "sqft": sqft,
            "sale_date": get_attr(sales, "@TransferDate_ext", "@_TransferDate_ext", "@TransferDate", "TransferDate"),
            "distance_mi": to_float(get_attr(comp, "@DistanceFromSubjectPropertyMilesCount", "DistanceFromSubjectPropertyMilesCount") or "0"),
            "beds": to_int(get_attr(struct, "@TotalBedroomCount", "@_TotalBedroomCount", "TotalBedroomCount")),
            "baths": to_float(get_attr(struct, "@TotalBathroomCount", "@_TotalBathroomCount", "TotalBathroomCount")),
            "year_built": to_int(
                get_nested(struct, "STRUCTURE_ANALYSIS", "@PropertyStructureBuiltYear") or
                get_nested(struct, "STRUCTURE_ANALYSIS", "@_PropertyStructureBuiltYear") or
                get_nested(struct, "STRUCTURE_ANALYSIS", "PropertyStructureBuiltYear")
            ),
        }

    SUBJECT_KEYS = ("SUBJECT_PROPERTY_ext", "SUBJECT_PROPERTY")
    COMP_KEYS    = ("COMPARABLE_PROPERTY_ext", "COMPARABLE_PROPERTY", "COMPARABLES", "COMPARABLE_LIST")

    def iter_nodes(node: Any) -> Iterable[Any]:
        if isinstance(node, dict):
            yield node
            for v in node.values():
                yield from iter_nodes(v)
        elif isinstance(node, list):
            for item in node:
                yield from iter_nodes(item)

    root = get_nested(payload, "RESPONSE_GROUP", "RESPONSE", "RESPONSE_DATA") or payload

    for node in iter_nodes(root):
        if not isinstance(node, dict):
            continue

        # Subject
        for sk in SUBJECT_KEYS:
            if sk in node and subject is None:
                cand = node[sk]
                parsed = parse_subject_block(cand)
                if parsed:
                    subject = parsed

        # Comps
        for ck in COMP_KEYS:
            if ck in node:
                comp_block = node[ck]
                if isinstance(comp_block, dict):
                    items = [comp_block]
                elif isinstance(comp_block, list):
                    items = comp_block
                else:
                    items = []
                for item in items:
                    parsed = parse_comp_item(item)
                    if parsed:
                        comps.append(parsed)

    comps = [c for c in comps if c.get("sale_price") and c.get("sqft") and c["sale_price"] > 0 and c["sqft"] > 0]

    if not comps:
        try:
            rg = payload.get("RESPONSE_GROUP", {})
            resp = rg.get("RESPONSE", {})
            data = resp.get("RESPONSE_DATA", {})
            logger.warning("ATTOM returned 0 comps. Shapes: RG=%s, R=%s, RD=%s",
                        list(rg.keys())[:8], list(resp.keys())[:8], list(data.keys())[:8])
        except Exception:
            pass

    return subject, comps
