#!/usr/bin/env python3
from __future__ import annotations
import json
from dataclasses import dataclass, asdict
from typing import Dict, Optional

WEIGHTS = {
    'role_level_fit': 0.30,
    'salary_signal': 0.25,
    'company_health': 0.15,
    'referral_path': 0.15,
    'hiring_manager_identifiability': 0.10,
    'application_recency': 0.05,
}

STRATEGY_RULES = [
    (8.0, 'Referral First'),
    (6.5, 'Connect First'),
    (5.0, 'Direct Apply'),
    (0.0, 'Skip'),
]

@dataclass
class QualificationInput:
    role_title: Optional[str] = None
    company: Optional[str] = None
    salary: Optional[str] = None
    location: Optional[str] = None
    posted_date: Optional[str] = None
    recruiter_name: Optional[str] = None
    inbound_recruiter: bool = False
    company_signal: Optional[str] = None
    referral_signal: Optional[str] = None
    hiring_manager_signal: Optional[str] = None
    recency_signal: Optional[str] = None


def _score_bucket(signal: Optional[str], mapping: Dict[str, float], default: float = 5.0) -> float:
    if not signal:
        return default
    return mapping.get(signal.lower().strip(), default)


def score_role_level(role_title: Optional[str]) -> float:
    if not role_title:
        return 5.0
    t = role_title.lower()
    if any(x in t for x in ['director', 'head of', 'vice president', 'vp ', 'plant manager', 'general manager']):
        return 9.0
    if any(x in t for x in ['operations manager', 'senior manager', 'site leader']):
        return 7.0
    if any(x in t for x in ['coordinator', 'specialist', 'analyst', 'associate']):
        return 2.0
    return 5.0


def score_salary(salary: Optional[str]) -> float:
    if not salary:
        return 5.0
    s = salary.replace(',', '')
    import re
    nums = [int(x) for x in re.findall(r'\$?(\d{2,6})', s)]
    if not nums:
        return 5.0
    top = max(nums)
    if top >= 180000:
        return 10.0
    if top >= 150000:
        return 8.5
    if top >= 140000:
        return 7.0
    if top >= 120000:
        return 5.0
    return 2.5


def score_company_health(signal: Optional[str]) -> float:
    return _score_bucket(signal, {
        'strong': 8.5,
        'positive': 7.0,
        'neutral': 5.0,
        'weak': 3.0,
        'negative': 2.0,
    })


def score_referral_path(signal: Optional[str], inbound_recruiter: bool = False) -> float:
    if inbound_recruiter:
        return 8.0
    return _score_bucket(signal, {
        'strong': 9.0,
        'warm': 7.5,
        'possible': 6.0,
        'none': 3.0,
    })


def score_hiring_manager(signal: Optional[str]) -> float:
    return _score_bucket(signal, {
        'clear': 8.5,
        'likely': 6.5,
        'unknown': 4.5,
        'none': 3.0,
    })


def score_recency(signal: Optional[str]) -> float:
    return _score_bucket(signal, {
        'fresh': 8.5,
        'recent': 7.0,
        'aging': 5.0,
        'stale': 3.0,
    })


def decide_strategy(fit_score: float, inbound_recruiter: bool = False) -> str:
    if inbound_recruiter and fit_score >= 4.0:
        return 'Recruiter Response'
    for threshold, strategy in STRATEGY_RULES:
        if fit_score >= threshold:
            return strategy
    return 'Skip'


def qualify(inp: QualificationInput) -> Dict[str, object]:
    dim = {
        'role_level_fit': score_role_level(inp.role_title),
        'salary_signal': score_salary(inp.salary),
        'company_health': score_company_health(inp.company_signal),
        'referral_path': score_referral_path(inp.referral_signal, inp.inbound_recruiter),
        'hiring_manager_identifiability': score_hiring_manager(inp.hiring_manager_signal),
        'application_recency': score_recency(inp.recency_signal),
    }
    weighted = sum(dim[k] * WEIGHTS[k] for k in WEIGHTS)
    fit_score = round(weighted, 2)
    strategy = decide_strategy(fit_score, inp.inbound_recruiter)
    payload = {
        'summary': {
            'fit_score_10': fit_score,
            'strategy': strategy,
            'inbound_recruiter': inp.inbound_recruiter,
            'advance': fit_score >= (4.0 if inp.inbound_recruiter else 5.0),
        },
        'dimensions': dim,
        'weights': WEIGHTS,
        'inputs': asdict(inp),
        'notes': [
            'Weighted rubric from PRD §6 Phase B.',
            'Inbound recruiter roles use 4+ threshold per PRD hybrid qualification note.',
        ],
    }
    return payload


if __name__ == '__main__':
    sample = QualificationInput(
        role_title='Director of Operations',
        company='Skills Alliance',
        posted_date='April 17, 2026 at 4:27:40 PM PDT',
        inbound_recruiter=False,
        company_signal='neutral',
        referral_signal='possible',
        hiring_manager_signal='unknown',
        recency_signal='fresh',
    )
    print(json.dumps(qualify(sample), indent=2))
