from sqlalchemy import select from sqlalchemy.orm import Session from ..models import PhoneNumber, VoiceCall from .validators import format_bonvoice_destination, normalize_phone_digits def find_inbound_by_did(db: Session, did_value: str) -> PhoneNumber | None: did = normalize_phone_digits(did_value) if len(did) != 10: return None return db.scalar( select(PhoneNumber).where( PhoneNumber.phone_number == did, PhoneNumber.status == "assigned", PhoneNumber.user_id.is_not(None), ) ) def _phone_variants(value: str) -> set[str]: raw = value.strip() normalized = normalize_phone_digits(raw) variants = {raw, normalized, format_bonvoice_destination(raw)} if len(normalized) == 10: variants.add(f"91{normalized}") return {item for item in variants if item} def find_pending_outbound_call(db: Session, *, from_value: str, to_value: str) -> VoiceCall | None: """ Match a pending outbound call to Bonvoice media `start`. Outbound Bonvoice streams report: - `to` = leg-A caller ID (purchased DID, e.g. 7946350706) - `from` = destination/callee (e.g. 919149154337) """ did_candidates = _phone_variants(to_value) owned_dids = { normalize_phone_digits(value) for value in did_candidates if len(normalize_phone_digits(value)) == 10 } if not owned_dids: return None candidates = db.scalars( select(VoiceCall) .where( VoiceCall.direction == "outbound", VoiceCall.status == "initiated", VoiceCall.from_did.in_(owned_dids), ) .order_by(VoiceCall.created_at.desc()) .limit(5) ).all() if not candidates: return None callee_variants = _phone_variants(from_value) for call in candidates: if _phone_variants(call.to_number) & callee_variants: return call return None