"""
UK Distribution Network Operator (DNO) Region Lookup

Maps UK postcodes to their electricity distribution region.
This is used to match users to the correct British Gas tariff rates.

There are 14 DNO regions in Great Britain + 1 in Northern Ireland:
- UK Power Networks: East, London, South East
- Western Power Distribution: East Midlands, West Midlands, South West, South Wales
- Electricity North West: North West England
- Northern Powergrid: North East, Yorkshire
- SP Energy Networks: Central/South Scotland (SP Distribution), Merseyside/North Wales (SP Manweb)
- SSE Networks: North Scotland (Scottish Hydro Electric)
- NIE Networks: Northern Ireland
"""

# Mapping of postcode prefixes (outcodes) to DNO regions
# This covers all UK postcode areas
POSTCODE_TO_DNO = {
    # UK Power Networks (East) - Eastern England
    "CB": "UKPN_East", "CO": "UKPN_East", "CM": "UKPN_East",
    "IP": "UKPN_East", "NR": "UKPN_East", "PE": "UKPN_East",
    "SG": "UKPN_East", "SS": "UKPN_East",

    # UK Power Networks (London)
    "E": "UKPN_London", "EC": "UKPN_London", "N": "UKPN_London",
    "NW": "UKPN_London", "SE": "UKPN_London", "SW": "UKPN_London",
    "W": "UKPN_London", "WC": "UKPN_London",
    "BR": "UKPN_London", "CR": "UKPN_London", "DA": "UKPN_London",
    "EN": "UKPN_London", "HA": "UKPN_London", "IG": "UKPN_London",
    "KT": "UKPN_London", "RM": "UKPN_London", "SM": "UKPN_London",
    "TW": "UKPN_London", "UB": "UKPN_London", "WD": "UKPN_London",

    # UK Power Networks (South East)
    "BN": "UKPN_SouthEast", "CT": "UKPN_SouthEast", "GU": "UKPN_SouthEast",
    "HP": "UKPN_SouthEast", "ME": "UKPN_SouthEast", "MK": "UKPN_SouthEast",
    "OX": "UKPN_SouthEast", "PO": "UKPN_SouthEast", "RG": "UKPN_SouthEast",
    "RH": "UKPN_SouthEast", "SL": "UKPN_SouthEast", "SO": "UKPN_SouthEast",
    "TN": "UKPN_SouthEast", "LU": "UKPN_SouthEast", "AL": "UKPN_SouthEast",

    # Western Power Distribution (East Midlands)
    "DE": "WPD_EastMidlands", "DN": "WPD_EastMidlands", "LE": "WPD_EastMidlands",
    "LN": "WPD_EastMidlands", "NG": "WPD_EastMidlands", "NN": "WPD_EastMidlands",
    "S": "WPD_EastMidlands",

    # Western Power Distribution (West Midlands)
    "B": "WPD_WestMidlands", "CV": "WPD_WestMidlands", "DY": "WPD_WestMidlands",
    "HR": "WPD_WestMidlands", "ST": "WPD_WestMidlands", "TF": "WPD_WestMidlands",
    "WR": "WPD_WestMidlands", "WS": "WPD_WestMidlands", "WV": "WPD_WestMidlands",

    # Western Power Distribution (South West)
    "BA": "WPD_SouthWest", "BH": "WPD_SouthWest", "BS": "WPD_SouthWest",
    "DT": "WPD_SouthWest", "EX": "WPD_SouthWest", "GL": "WPD_SouthWest",
    "PL": "WPD_SouthWest", "SN": "WPD_SouthWest", "SP": "WPD_SouthWest",
    "TA": "WPD_SouthWest", "TQ": "WPD_SouthWest", "TR": "WPD_SouthWest",

    # Western Power Distribution (South Wales)
    "CF": "WPD_SouthWales", "LD": "WPD_SouthWales", "NP": "WPD_SouthWales",
    "SA": "WPD_SouthWales", "SY": "WPD_SouthWales",

    # Electricity North West
    "BB": "ENW", "BL": "ENW", "CA": "ENW",
    "CW": "ENW", "FY": "ENW", "L": "ENW",
    "LA": "ENW", "M": "ENW", "OL": "ENW",
    "PR": "ENW", "SK": "ENW", "WA": "ENW", "WN": "ENW",

    # Northern Powergrid (North East)
    "DH": "NPG_NorthEast", "DL": "NPG_NorthEast", "NE": "NPG_NorthEast",
    "SR": "NPG_NorthEast", "TS": "NPG_NorthEast",

    # Northern Powergrid (Yorkshire)
    "BD": "NPG_Yorkshire", "HG": "NPG_Yorkshire", "HD": "NPG_Yorkshire",
    "HU": "NPG_Yorkshire", "HX": "NPG_Yorkshire", "LS": "NPG_Yorkshire",
    "WF": "NPG_Yorkshire", "YO": "NPG_Yorkshire",

    # SP Energy Networks (SP Distribution) - Central/South Scotland
    "DD": "SPEN_SPD", "EH": "SPEN_SPD", "FK": "SPEN_SPD",
    "G": "SPEN_SPD", "KA": "SPEN_SPD", "KY": "SPEN_SPD",
    "ML": "SPEN_SPD", "PA": "SPEN_SPD", "TD": "SPEN_SPD",
    "DG": "SPEN_SPD",

    # SP Energy Networks (SP Manweb) - Merseyside, Cheshire, North Wales
    "CH": "SPEN_Manweb", "LL": "SPEN_Manweb",

    # SSE Networks (Scottish Hydro Electric) - North Scotland
    "AB": "SSE_Hydro", "IV": "SSE_Hydro", "KW": "SSE_Hydro",
    "PH": "SSE_Hydro", "ZE": "SSE_Hydro", "HS": "SSE_Hydro",

    # Northern Ireland Electricity
    "BT": "NIE",
}

# Human-readable DNO names
DNO_NAMES = {
    "UKPN_East": "UK Power Networks (East)",
    "UKPN_London": "UK Power Networks (London)",
    "UKPN_SouthEast": "UK Power Networks (South East)",
    "WPD_EastMidlands": "Western Power (East Midlands)",
    "WPD_WestMidlands": "Western Power (West Midlands)",
    "WPD_SouthWest": "Western Power (South West)",
    "WPD_SouthWales": "Western Power (South Wales)",
    "ENW": "Electricity North West",
    "NPG_NorthEast": "Northern Powergrid (North East)",
    "NPG_Yorkshire": "Northern Powergrid (Yorkshire)",
    "SPEN_SPD": "SP Energy Networks (SP Distribution)",
    "SPEN_Manweb": "SP Energy Networks (SP Manweb)",
    "SSE_Hydro": "SSE Networks (Scottish Hydro)",
    "NIE": "NIE Networks (Northern Ireland)",
}

# Map DNO codes to the region names used in our config
DNO_TO_REGION = {
    "UKPN_East": "East Anglia",
    "UKPN_London": "London",
    "UKPN_SouthEast": "South East",
    "WPD_EastMidlands": "East Midlands",
    "WPD_WestMidlands": "West Midlands",
    "WPD_SouthWest": "South West",
    "WPD_SouthWales": "South Wales",
    "ENW": "North West",
    "NPG_NorthEast": "North East",
    "NPG_Yorkshire": "Yorkshire",
    "SPEN_SPD": "Central Scotland",
    "SPEN_Manweb": "Merseyside",
    "SSE_Hydro": "North Scotland",
    "NIE": "Northern Ireland",
}


def get_postcode_prefix(postcode: str) -> str:
    """
    Extract the postcode area (letters only) from a UK postcode.

    Examples:
        "HP20 1HP" -> "HP"
        "SW1A 1AA" -> "SW"
        "M1 1AA" -> "M"
        "NR3 3AF" -> "NR"
    """
    postcode_clean = postcode.replace(" ", "").upper()

    # Extract leading letters (postcode area)
    prefix = ""
    for char in postcode_clean:
        if char.isalpha():
            prefix += char
        else:
            break

    return prefix


def get_dno_for_postcode(postcode: str) -> dict:
    """
    Get the DNO information for a given UK postcode.

    Args:
        postcode: UK postcode (e.g., "HP20 1HP", "M1 1AA")

    Returns:
        Dictionary with:
        - dno_code: Internal DNO code
        - dno_name: Human-readable DNO name
        - region: Region name used in tariff lookup
        - postcode_prefix: The postcode area used for matching
    """
    prefix = get_postcode_prefix(postcode)

    if not prefix:
        return {
            "dno_code": None,
            "dno_name": "Unknown",
            "region": None,
            "postcode_prefix": prefix,
            "error": "Invalid postcode format"
        }

    # Look up the DNO
    dno_code = POSTCODE_TO_DNO.get(prefix)

    if not dno_code:
        # Try shorter prefix (e.g., "SW" instead of "SW1")
        for length in range(len(prefix), 0, -1):
            shorter = prefix[:length]
            if shorter in POSTCODE_TO_DNO:
                dno_code = POSTCODE_TO_DNO[shorter]
                break

    if dno_code:
        return {
            "dno_code": dno_code,
            "dno_name": DNO_NAMES.get(dno_code, "Unknown"),
            "region": DNO_TO_REGION.get(dno_code),
            "postcode_prefix": prefix
        }
    else:
        return {
            "dno_code": None,
            "dno_name": "Unknown",
            "region": None,
            "postcode_prefix": prefix,
            "error": f"Postcode area '{prefix}' not found in DNO lookup"
        }


def find_matching_region(postcode: str, available_regions: list) -> str:
    """
    Find the best matching region from available cached regions.

    Args:
        postcode: User's postcode
        available_regions: List of region names we have tariff data for

    Returns:
        Best matching region name, or first available if no match
    """
    dno_info = get_dno_for_postcode(postcode)
    user_region = dno_info.get("region")

    if user_region and user_region in available_regions:
        return user_region

    # Fallback: return first available region
    if available_regions:
        return available_regions[0]

    return None


# CLI for testing
if __name__ == "__main__":
    import sys

    if len(sys.argv) > 1:
        postcode = sys.argv[1]
    else:
        postcode = "HP20 1HP"

    result = get_dno_for_postcode(postcode)
    print(f"\nPostcode: {postcode}")
    print(f"Prefix: {result['postcode_prefix']}")
    print(f"DNO: {result['dno_name']}")
    print(f"Region: {result['region']}")

    if result.get('error'):
        print(f"Error: {result['error']}")
