"""
Cars that cannot be driven home (Steven 2026-09-12: "not to show cars that
involve a ferry crossing, ie i dont want to see cars in ireland").

A straight line distance says Belfast is closer than Cornwall. A van cannot
drive there. This decides, from the only location a platform gives us,
whether collecting the car means a boat: Northern Ireland and the Republic,
the Isle of Man, the Isle of Wight, the Channel Islands and the Scottish
islands that no bridge or causeway reaches.

Nothing here guesses. A location BidBrain could not read is not a ferry
(pricing.py never rejects on a fact it does not have), and a place that
shares its name with a mainland one (Bangor, Newport, Douglas, Yarmouth,
Newcastle) is deliberately left out rather than hiding a good car: the
postcode rules below catch those when a postcode is what the platform gave.
"""

import re

# Whole postcode areas where every address needs a ferry.
FERRY_AREAS = ("bt", "im", "je", "gy", "hs", "ze")

# Areas where only some districts do. (area, first, last), inclusive.
FERRY_DISTRICTS = (
    ("kw", 15, 17),   # Orkney
    ("po", 30, 41),   # Isle of Wight
    ("pa", 20, 20),   # Rothesay, Isle of Bute
    ("pa", 41, 49),   # Islay, Jura, Colonsay
    ("pa", 60, 78),   # Mull, Iona, Tiree, Coll
    ("ka", 27, 28),   # Arran, Great Cumbrae
)

# Place names that can only mean the one place. Counties and island names
# first, then towns. Matched as whole words inside the location text, so
# "Newtownards, County Down" is caught twice over.
FERRY_PLACES = (
    # Ireland, north and south
    "northern ireland", "republic of ireland", "county antrim", "county armagh",
    "county down", "county fermanagh", "county tyrone", "county londonderry",
    "co antrim", "co armagh", "co down", "co fermanagh", "co tyrone", "co londonderry",
    "belfast", "lisburn", "londonderry", "newry", "armagh", "ballymena", "coleraine",
    "craigavon", "newtownabbey", "carrickfergus", "larne", "downpatrick", "dungannon",
    "cookstown", "magherafelt", "strabane", "limavady", "portadown", "banbridge",
    "ballymoney", "holywood", "newtownards", "antrim", "omagh", "enniskillen",
    "ballyclare", "ballynahinch", "portrush", "portstewart", "lurgan", "comber",
    "randalstown", "donaghadee", "kilkeel", "warrenpoint", "castlederg", "bushmills",
    "dublin", "cork", "galway", "limerick", "waterford", "drogheda", "dundalk",
    # Isle of Man
    "isle of man", "onchan", "port erin", "port st mary", "laxey", "ballasalla",
    # Isle of Wight
    "isle of wight", "ryde", "cowes", "shanklin", "ventnor", "bembridge", "sandown",
    # Channel Islands
    "jersey", "guernsey", "alderney", "st helier", "st peter port",
    # Scottish islands no road reaches
    "orkney", "shetland", "kirkwall", "lerwick", "stornoway", "stromness",
    "scalloway", "western isles", "outer hebrides", "isle of arran", "isle of bute",
    "isle of islay", "isle of mull", "isle of lewis", "isle of harris", "isle of barra",
    "brodick", "rothesay", "tobermory", "craignure", "bowmore", "port ellen",
    "castlebay", "lochboisdale", "lochmaddy", "benbecula",
)

_TIDY = re.compile(r"[^a-z0-9]+")
# A full postcode or an outward code on its own: BT1, BT12 5AA, PA20, IM1 1AA.
_OUTWARD = re.compile(r"\b([a-z]{1,2})(\d{1,2})[a-z]?\b")


def _tidy(text) -> str:
    return _TIDY.sub(" ", str(text or "").lower()).strip()


def postcode_reason(location) -> str:
    """The postcode in this location needs a ferry, in plain words, or ""."""
    tidy = _tidy(location)
    for area, number in _OUTWARD.findall(tidy):
        if area in FERRY_AREAS:
            return f"Needs a ferry crossing: {area.upper()} postcodes are across water."
        n = int(number)
        for a, first, last in FERRY_DISTRICTS:
            if area == a and first <= n <= last:
                return f"Needs a ferry crossing: {area.upper()}{n} is an island postcode."
    return ""


def place_reason(location, places=None) -> str:
    """This location names a place a ferry is needed for, or ""."""
    tidy = f" {_tidy(location)} "
    for place in (places if places is not None else FERRY_PLACES):
        name = _tidy(place)
        if name and f" {name} " in tidy:
            return f"Needs a ferry crossing: {str(location).strip()}."
    return ""


def ferry_reason(location, places=None) -> str:
    """Why this car cannot be driven home, or "" when it can (or when the
    location is not known, which is never a reason to drop a car)."""
    if not str(location or "").strip():
        return ""
    return postcode_reason(location) or place_reason(location, places)
