"""
state
=====

Utilities for handling Indian State Codes in GST context.

This module maps the 2-digit numeric codes (01-99) found at the start of
a GSTIN to their respective State or Union Territory names.

It is critical for validating "Place of Supply" to determine tax type
(IGST vs CGST/SGST).

:copyright: (c) 2026
:license: GPL-3.0-only
"""

# Official list of GST State Codes as per GST Council
_STATE_CODE_MAP = {
    "01": "Jammu and Kashmir",
    "02": "Himachal Pradesh",
    "03": "Punjab",
    "04": "Chandigarh",
    "05": "Uttarakhand",
    "06": "Haryana",
    "07": "Delhi",
    "08": "Rajasthan",
    "09": "Uttar Pradesh",
    "10": "Bihar",
    "11": "Sikkim",
    "12": "Arunachal Pradesh",
    "13": "Nagaland",
    "14": "Manipur",
    "15": "Mizoram",
    "16": "Tripura",
    "17": "Meghalaya",
    "18": "Assam",
    "19": "West Bengal",
    "20": "Jharkhand",
    "21": "Odisha",
    "22": "Chhattisgarh",
    "23": "Madhya Pradesh",
    "24": "Gujarat",
    "25": "Daman and Diu",  # Often merged with Dadra & Nagar Haveli
    "26": "Dadra and Nagar Haveli",
    "27": "Maharashtra",
    "28": "Andhra Pradesh (Old)",  # Kept for legacy data; current AP is 37
    "29": "Karnataka",
    "30": "Goa",
    "31": "Lakshadweep",
    "32": "Kerala",
    "33": "Tamil Nadu",
    "34": "Puducherry",
    "35": "Andaman and Nicobar Islands",
    "36": "Telangana",
    "37": "Andhra Pradesh (New)",
    "38": "Ladakh",
    "97": "Other Territory",
    "98": "Centre Jurisdiction",
    "99": "Other Territory",
}

class StateError(ValueError):
    """Custom exception for State Code errors."""
    pass


def get_state_name_from_code(code: str) -> str:
    """
    Look up the State/UT name for a given 2-digit GST state code.

    Parameters
    ----------
    code : str
        The 2-digit string (e.g., "27").

    Returns
    -------
    str
        The name of the state (e.g., "Maharashtra").
        Returns "Unknown State" if code is not found.
    """
    if not code:
        return "Unknown State"
    
    # Ensure 2 digits (e.g., '1' -> '01')
    code = code.zfill(2)
    
    return _STATE_CODE_MAP.get(code, "Unknown State")


def validate_state_code(code: str) -> bool:
    """
    Checks if the provided code is a valid Indian GST State Code.

    Parameters
    ----------
    code : str
        The 2-digit string.

    Returns
    -------
    bool
        True if the code exists in the official list.
    """
    if not code:
        return False
        
    code = code.zfill(2)
    return code in _STATE_CODE_MAP


def validate_place_of_supply(gstin: str, pos_state_code: str) -> bool:
    """
    Validates if the Place of Supply (POS) matches the Supplier's State
    derived from their GSTIN.

    Logic:
    ------
    The first 2 digits of the GSTIN represent the Supplier's State.
    If Supplier State == Place of Supply State, it is an Intra-State transaction (CGST+SGST).
    If Supplier State != Place of Supply State, it is an Inter-State transaction (IGST).

    This function simply checks if the declared POS code matches the GSTIN's code.

    Parameters
    ----------
    gstin : str
        The full 15-character GSTIN of the supplier.
    pos_state_code : str
        The 2-digit state code of the Place of Supply (usually from the invoice).

    Returns
    -------
    bool
        True if the POS code matches the GSTIN state code.
    """
    if not gstin or len(gstin) < 2:
        return False
    
    if not pos_state_code:
        return False

    # Extract supplier state code
    supplier_code = gstin[:2]
    
    # Normalize inputs (pad with 0 if single digit)
    pos_state_code = pos_state_code.zfill(2)
    
    # Check validity of both codes first
    if not validate_state_code(supplier_code):
        return False
        
    if not validate_state_code(pos_state_code):
        return False

    return supplier_code == pos_state_code