"""
Load the hand entered cars for Phase 1 from data/cars.json.

The file is a plain list of cars. Every car needs at least the fields the gate
checks against. If the file is missing, malformed, or a car has a value that
cannot be read as a number, this fails loudly with a clear message rather than
quietly producing wrong results. Section 2 rule, fail loudly.
"""

import json
import os
from .pricing import Car

DATA_DIR = os.path.join(os.path.dirname(os.path.dirname(os.path.abspath(__file__))), "data")
CARS_PATH = os.path.join(DATA_DIR, "cars.json")

# Fields that may be left blank and what they become.
_INT_FIELDS = ["year", "mileage", "owners", "grade", "reserve", "cap_clean",
               "glass_retail", "cazana_retail", "actually_paid"]
_FLOAT_FIELDS = ["distance_miles"]
_TEXT_FIELDS = ["reg", "make", "model", "derivative", "engine",
                "service_history", "photo_url", "listing_url", "source"]


class CarDataError(Exception):
    pass


def _as_int(value, field, where):
    if value in (None, "", "?"):
        return None
    try:
        # Tolerate "12,500" and "12500.0" and " 12500 ".
        return int(round(float(str(value).replace(",", "").strip())))
    except (ValueError, TypeError):
        raise CarDataError(
            f"{where}: field '{field}' has value {value!r}, which is not a number. "
            f"Fix it in cars.json or leave it blank."
        )


def _as_float(value, field, where):
    if value in (None, "", "?"):
        return None
    try:
        return float(str(value).replace(",", "").strip())
    except (ValueError, TypeError):
        raise CarDataError(
            f"{where}: field '{field}' has value {value!r}, which is not a number. "
            f"Fix it in cars.json or leave it blank."
        )


def _as_bool(value):
    if isinstance(value, bool):
        return value
    return str(value).strip().lower() in ("yes", "true", "y", "1", "vat", "vat qualifying")


def load_cars(path: str = CARS_PATH):
    if not os.path.exists(path):
        raise CarDataError(
            f"Could not find {path}. Create it with your cars before running the build."
        )

    with open(path, "r", encoding="utf-8") as f:
        try:
            raw = json.load(f)
        except json.JSONDecodeError as e:
            raise CarDataError(
                f"cars.json is not valid JSON. {e}. Check for a missing comma or bracket."
            )

    if not isinstance(raw, list):
        raise CarDataError("cars.json must be a list of cars, written inside [ ].")

    cars = []
    for i, item in enumerate(raw, start=1):
        if not isinstance(item, dict):
            raise CarDataError(f"Car number {i} in cars.json is not a set of fields.")
        where = f"Car number {i} ({item.get('reg', 'no reg')})"

        car = Car()
        for field in _TEXT_FIELDS:
            setattr(car, field, str(item.get(field, "") or "").strip())
        for field in _INT_FIELDS:
            setattr(car, field, _as_int(item.get(field), field, where))
        for field in _FLOAT_FIELDS:
            setattr(car, field, _as_float(item.get(field), field, where))
        car.vat_qualifying = _as_bool(item.get("vat_qualifying"))
        cars.append(car)

    return cars
