"""
invoice
=======

Business logic for B2B (Business-to-Business) invoice validation.
This module cross-references supplier and buyer data to enforce
taxation rules (CGST/SGST vs IGST) and structural integrity.

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

# Import the modules we built previously
from .gstin import validate as validate_gstin
from .state import validate_state_code

# --- CONSTANTS ---
INTRA_STATE = "INTRA_STATE"
INTER_STATE = "INTER_STATE"
UNKNOWN_STATE = "UNKNOWN"

class InvoiceValidationError(ValueError):
    """Custom exception for invoice-level business logic failures."""
    pass


def validate_supplier_and_buyer_gstin(supplier: str, buyer: str) -> bool:
    """
    Ensures both GSTINs are structurally valid and not identical.
    
    A transaction where Supplier GSTIN == Buyer GSTIN is generally 
    an invalid B2B tax invoice (you cannot bill yourself within the same state).

    Parameters
    ----------
    supplier : str
        The 15-character Supplier GSTIN.
    buyer : str
        The 15-character Buyer GSTIN.

    Returns
    -------
    bool
        True if both are valid and distinct.
    """
    if not supplier or not buyer:
        return False
        
    supplier_clean = supplier.strip().upper()
    buyer_clean = buyer.strip().upper()

    # 1. Fraud / Logic Check: Cannot be the exact same GSTIN
    if supplier_clean == buyer_clean:
        return False

    # 2. Structural & Checksum Validation
    if not validate_gstin(supplier_clean):
        return False
        
    if not validate_gstin(buyer_clean):
        return False

    return True


def determine_transaction_type(supplier: str, buyer: str) -> str:
    """
    Determines if the transaction is Intra-state (Local) or Inter-state.
    
    Logic:
    Extracts the first 2 digits (State Code) from both GSTINs.
    If they match -> INTRA_STATE
    If they differ -> INTER_STATE

    Parameters
    ----------
    supplier : str
    buyer : str

    Returns
    -------
    str
        'INTRA_STATE', 'INTER_STATE', or 'UNKNOWN' if inputs are invalid.
    """
    if not validate_supplier_and_buyer_gstin(supplier, buyer):
        return UNKNOWN_STATE

    supplier_state = supplier[:2]
    buyer_state = buyer[:2]

    if supplier_state == buyer_state:
        return INTRA_STATE
    
    return INTER_STATE


def _safe_float(value) -> float:
    """
    Internal helper to safely convert messy OCR data to a float.
    Handles None, empty strings, and string-formatted numbers.
    """
    if value is None or value == "":
        return 0.0
    try:
        return float(value)
    except (ValueError, TypeError):
        return 0.0


def validate_tax_structure(transaction_type: str, tax_data: dict) -> bool:
    """
    Validates if the extracted tax components map correctly to the transaction type.

    Rule Engine:
    - INTRA_STATE -> Must have CGST and SGST > 0. IGST must be 0.
    - INTER_STATE -> Must have IGST > 0. CGST and SGST must be 0.
    
    Parameters
    ----------
    transaction_type : str
        Output from `determine_transaction_type`.
    tax_data : dict
        A dictionary containing tax amounts, e.g., {'cgst': 150.5, 'sgst': 150.5, 'igst': 0}

    Returns
    -------
    bool
        True if the tax structure legally matches the transaction type.
    """
    if transaction_type == UNKNOWN_STATE:
        return False

    # Safely extract values using .get() to prevent KeyErrors if OCR missed a field
    cgst = _safe_float(tax_data.get("cgst"))
    sgst = _safe_float(tax_data.get("sgst"))
    igst = _safe_float(tax_data.get("igst"))

    if transaction_type == INTRA_STATE:
        # For Intra-state: IGST must be zero. Either CGST or SGST must be present.
        # (Assuming non-zero rated items for this logic check)
        if igst > 0:
            return False
        if cgst == 0 and sgst == 0:
            return False
        
        # In Indian GST, CGST and SGST amounts should be mathematically equal.
        # We use a small tolerance (0.1) for floating-point rounding errors.
        if abs(cgst - sgst) > 0.1:
            return False
            
        return True

    elif transaction_type == INTER_STATE:
        # For Inter-state: CGST and SGST must be zero. IGST must be present.
        if cgst > 0 or sgst > 0:
            return False
        if igst == 0:
            return False
            
        return True

    return False