"""Phaxor — Bolt Analysis Engine (Python port)"""
import math

BOLT_GRADES = {
    '4.6': {'label': 'Grade 4.6', 'Sp': 225, 'Sy': 240, 'Sut': 400},
    '4.8': {'label': 'Grade 4.8', 'Sp': 310, 'Sy': 340, 'Sut': 420},
    '5.8': {'label': 'Grade 5.8', 'Sp': 380, 'Sy': 420, 'Sut': 520},
    '8.8': {'label': 'Grade 8.8', 'Sp': 600, 'Sy': 660, 'Sut': 830},
    '9.8': {'label': 'Grade 9.8', 'Sp': 650, 'Sy': 720, 'Sut': 900},
    '10.9': {'label': 'Grade 10.9', 'Sp': 830, 'Sy': 940, 'Sut': 1040},
    '12.9': {'label': 'Grade 12.9', 'Sp': 970, 'Sy': 1100, 'Sut': 1220},
    'custom': {'label': 'Custom', 'Sp': 600, 'Sy': 660, 'Sut': 830},
}

BOLT_SIZES = {
    'M5': {'label': 'M5 × 0.8', 'At': 14.18, 'd': 5, 'pitch': 0.8},
    'M6': {'label': 'M6 × 1.0', 'At': 20.12, 'd': 6, 'pitch': 1.0},
    'M8': {'label': 'M8 × 1.25', 'At': 36.61, 'd': 8, 'pitch': 1.25},
    'M10': {'label': 'M10 × 1.5', 'At': 58.00, 'd': 10, 'pitch': 1.5},
    'M12': {'label': 'M12 × 1.75', 'At': 84.27, 'd': 12, 'pitch': 1.75},
    'M14': {'label': 'M14 × 2.0', 'At': 115.4, 'd': 14, 'pitch': 2.0},
    'M16': {'label': 'M16 × 2.0', 'At': 157.0, 'd': 16, 'pitch': 2.0},
    'M18': {'label': 'M18 × 2.5', 'At': 192.5, 'd': 18, 'pitch': 2.5},
    'M20': {'label': 'M20 × 2.5', 'At': 245.0, 'd': 20, 'pitch': 2.5},
    'M22': {'label': 'M22 × 2.5', 'At': 303.0, 'd': 22, 'pitch': 2.5},
    'M24': {'label': 'M24 × 3.0', 'At': 353.0, 'd': 24, 'pitch': 3.0},
    'M30': {'label': 'M30 × 3.5', 'At': 561.0, 'd': 30, 'pitch': 3.5},
}

def solve_bolt_analysis(inputs: dict) -> dict | None:
    """Bolt Analysis Calculator."""
    grade_key = inputs.get('gradeKey', '8.8')
    size_key = inputs.get('sizeKey', 'M10')
    ext_load = float(inputs.get('externalLoad', 0))
    grip_len = float(inputs.get('gripLength', 0))
    preload_factor = float(inputs.get('preloadFactor', 0.75))
    joint_dia = float(inputs.get('jointDia', 20))

    if grip_len <= 0:
        return None

    grade = BOLT_GRADES.get(grade_key, BOLT_GRADES['8.8'])
    size = BOLT_SIZES.get(size_key, BOLT_SIZES['M10'])

    at = size['At']
    d = size['d']

    # Proof load
    proof_load = grade['Sp'] * at

    # Preload
    fi = preload_factor * proof_load

    # Bolt stiffness
    eb = 207000  # MPa
    kb = (at * eb) / grip_len

    # Joint stiffness
    ej = 207000
    D = joint_dia
    t = grip_len / 2
    num = (1.155 * t + D - d) * (D + d)
    den = (1.155 * t + D + d) * (D - d)
    
    km = kb * 5
    if den > 0 and num > 0:
        try:
            val = math.log(num / den)
            if val != 0:
                km = (0.5774 * math.pi * d * ej) / val
        except ValueError:
            pass
            
    # Stiffness ratio
    c = kb / (kb + km) if (kb + km) > 0 else 0

    # Loads
    fb = fi + c * ext_load
    fj = fi - (1 - c) * ext_load

    # Stresses
    bolt_stress = fb / at if at > 0 else 0

    # FOS
    fos_yield = grade['Sy'] / bolt_stress if bolt_stress > 0 else float('inf')
    fos_proof = grade['Sp'] / bolt_stress if bolt_stress > 0 else float('inf')
    
    separation_load = (1 - c) * ext_load
    fos_separation = fi / separation_load if separation_load > 0 else float('inf')
    
    separated = fj <= 0
    
    # Torque
    k_torque = 0.2
    torque_req = k_torque * (d / 1000) * fi

    return {
        'proofLoad': float(f"{proof_load:.1f}"),
        'clampLoad': float(f"{fi:.1f}"),
        'boltStiffness': float(f"{kb:.1f}"),
        'jointStiffness': float(f"{km:.1f}"),
        'stiffnessRatio': float(f"{c:.4f}"),
        'boltLoad': float(f"{fb:.1f}"),
        'jointLoad': float(f"{max(fj, 0):.1f}"),
        'boltStress': float(f"{bolt_stress:.1f}"),
        'fosYield': float(f"{fos_yield:.2f}"),
        'fosProof': float(f"{fos_proof:.2f}"),
        'fosSeparation': float(f"{fos_separation:.2f}"),
        'separated': separated,
        'tensileArea': float(f"{at:.2f}"),
        'torqueRequired': float(f"{torque_req:.1f}")
    }
