import json
from decimal import Decimal, ROUND_HALF_UP


def safe_str(value):
    return str(value).strip() if value is not None and value != "null" else ""


def format_money(value):
    try:
        if value is None:
            return "0.00"
        return format(
            Decimal(str(value)).quantize(Decimal("0.01"), rounding=ROUND_HALF_UP),
            ".2f",
        )
    except Exception:
        return "0.00"


def parse_seat_info(seat_info_str):
    defaults = {"table": "", "seat": ""}
    if not seat_info_str or seat_info_str == "null" or seat_info_str is None:
        return defaults

    try:
        parsed = json.loads(seat_info_str) if isinstance(seat_info_str, str) else seat_info_str
        return {
            "table": str(parsed.get("zoneName", "")).strip() if parsed and parsed.get("zoneName") is not None else "",
            "seat": str(parsed.get("colName", "")).strip() if parsed and parsed.get("colName") is not None else "",
        }
    except (json.JSONDecodeError, TypeError, ValueError):
        return defaults


def map_ticket_type_seat_counts(ticket_types_json: dict) -> dict:
    """
    ticket_type_id -> seat count from seatsDescriptor
    """
    seat_count_map: dict = {}
    if not (ticket_types_json.get("success") and ticket_types_json.get("data") and ticket_types_json["data"].get("items")):
        return seat_count_map

    for item in ticket_types_json["data"]["items"]:
        tt_id = str(item.get("id", "")).strip()
        if not tt_id:
            continue
        seats_desc = item.get("seatsDescriptor", "")
        count = len([s for s in seats_desc.split(",") if s.strip()]) if isinstance(seats_desc, str) else 0
        seat_count_map[tt_id] = str(count)

    return seat_count_map


def build_rows(*, event_title: str, event_date_str: str, tickets: list[dict],
               order_info_map: dict, ticket_type_map: dict, ticket_type_seat_count_map: dict,
               hidden_columns: set) -> tuple[list[dict], int]:
    """
    Returns (rows, unmapped_count)
    rows contain only visible columns (hidden removed).
    """
    rows: list[dict] = []
    unmapped_counts = 0

    for ticket in tickets:
        seat_info = parse_seat_info(ticket.get("seatInfo"))
        order_id = ticket.get("userOrderId")
        order_info = order_info_map.get(order_id, {"phone": "", "full_amount": "0.00"})

        ticket_id_key = str(ticket.get("id", ""))
        ticket_type_id = ticket_type_map.get(ticket_id_key, "")
        table_seats_count = ticket_type_seat_count_map.get(ticket_type_id, "")
        if not table_seats_count and ticket_type_id:
            unmapped_counts += 1

        price = format_money(ticket.get("price"))
        discount = format_money(ticket.get("discount"))
        sum_val = format_money(Decimal(price) - Decimal(discount))

        full_row = {
            "title": event_title,
            "date": event_date_str,
            "order_id": order_id,

            "ticket_id": ticket.get("id", ""),  # hidden
            "ticket_type": safe_str(ticket.get("ticketTypeTitle")),  # hidden
            "ticket_type_id": ticket_type_id,  # hidden

            "table": seat_info["table"],
            "table_seats_count": table_seats_count,
            "seat": seat_info["seat"],

            "price": price,  # hidden
            "discount": discount,  # hidden

            "sum": sum_val,
            "full_amount": order_info["full_amount"],
            "phone": order_info["phone"],
            "name": safe_str(ticket.get("participantName")),
            "email": safe_str(ticket.get("email")),
        }

        visible_row = {k: v for k, v in full_row.items() if k not in hidden_columns}
        rows.append(visible_row)

    return rows, unmapped_counts

