"""
Stage 7 — Verification Agent  (Reference / Background)
========================================================
WHAT THIS DOES
  After the human hire decision, this agent runs the verification step:
    1. requests the candidate's WRITTEN CONSENT first (nothing runs without it - FCRA),
    2. on consent, triggers the configured checks with the screening provider,
    3. tracks status, chases slow checks, and keeps the candidate updated, and
    4. on results:  CLEAR  -> advances toward offer,
                    FLAGGED -> hands the report to HR/recruiter for the human-led
                               FCRA adverse-action process. The agent NEVER acts on a flag.

THE TWO HARD COMPLIANCE GATES (do not soften these)
  GATE 1 — No consent, no check. Checks are only initiated after documented authorization.
  GATE 2 — No automated adverse action. A flagged result is routed to humans; FCRA requires a
           pre-adverse-action notice, a copy of the report, time to dispute, then a final human
           decision. The agent's job ends at the hand-off.

SAFETY (same posture as Stages 2-6)
  - DRY_RUN = True by default: prints what it WOULD do, sends/triggers nothing.
  - Texts only to candidates who consented to texting.
  - Every action written to an audit log.
"""

import csv, json, sys
from datetime import datetime, timezone

# =====================================================================
#  CONFIG  —  EDIT THIS SECTION
# =====================================================================
DRY_RUN = True
CANDIDATES_CSV    = "stage7_candidates_sample.csv"
VERIFICATION_JSON = "stage7_verification_sample.json"
AUDIT_LOG         = "stage7_verification_audit_log.csv"

COMPANY_NAME = "Acme Talent"
FROM_NAME    = "Arun from Acme Recruiting"

CONSENT_REQUEST = ("Hi {first_name}, congratulations again on moving forward for {job_title}! "
                   "The next step is our standard reference and background check. Before anything runs, "
                   "we need your written authorization — please review and sign here: {consent_link} "
                   "Nothing is checked until you approve. Questions? Just reply.")
CONSENT_NUDGE   = ("Hi {first_name}, a quick reminder to authorize your background check for {job_title} "
                   "so we can keep things moving: {consent_link}")
STATUS_UPDATE   = ("Hi {first_name}, a quick update: your checks for {job_title} are in progress and on track. "
                   "We'll let you know the moment they're done — typically a few business days.")
CLEAR_GOODNEWS  = ("Hi {first_name}, great news — your checks for {job_title} are complete. "
                   "Next step: your offer. You'll hear from us very soon!")

# =====================================================================
#  PROVIDER HOOKS  —  plug your services in here (examples commented)
# =====================================================================
def send_email(to, subject, body):
    if DRY_RUN: print(f"      [DRY-RUN EMAIL] to {to}  |  {subject}"); return True
    raise NotImplementedError("Add email provider in send_email().")

def send_sms(to, body):
    if DRY_RUN: print(f"      [DRY-RUN TEXT]  to {to}"); return True
    raise NotImplementedError("Add SMS provider in send_sms().")

def notify_person(name, email, subject, body):
    if DRY_RUN: print(f"      [DRY-RUN → {name}]  |  {subject}"); return True
    raise NotImplementedError("Route to recruiter/HR in notify_person().")

def generate_consent_link(cand):
    """E-signature authorization form (FCRA standalone disclosure)."""
    return f"https://consent.acme.example/a/{cand['candidate_id'].lower()}"

def initiate_checks(cand, check_types):
    """Kick off checks with your screening provider (e.g., Checkr, Certn, HireRight)."""
    if DRY_RUN:
        print(f"      [DRY-RUN SCREENING] initiating for {cand['candidate_id']}: {', '.join(check_types)}")
        return True
    # --- Example (Checkr): client.invitations.create(candidate_id=..., package=...) ---
    raise NotImplementedError("Wire your screening provider into initiate_checks().")

def advance_stage(cand, to_stage):
    if DRY_RUN: print(f"      [DRY-RUN ADVANCE] {cand['candidate_id']} -> {to_stage}"); return True
    raise NotImplementedError("Update the candidate's stage in your ATS in advance_stage().")

# =====================================================================
#  ENGINE  —  you can leave this alone
# =====================================================================
def is_true(v): return str(v).strip().lower() in ("true", "yes", "y", "1")
def to_int(v):
    try: return int(v or 0)
    except ValueError: return 0
def fill(t, **kw): return t.format(**kw)

def reach_candidate(cand, body, subject):
    send_email(cand["email"], subject, body)
    if cand["phone"] and is_true(cand["consent_to_contact"]):
        send_sms(cand["phone"], body)
    elif cand["phone"]:
        print("      (candidate text skipped — no texting consent)")

def process(cand, data, audit):
    cfg = data["config"]; recruiters = data.get("recruiters", {}); hr = data["hr_compliance"]
    reqcfg = data["reqs"].get(cand["job_req_id"])
    name = f'{cand["first_name"]} {cand["last_name"]}'.strip()
    rname = cand["recruiter_owner"]; remail = recruiters.get(rname, "")
    print(f"\n• {name}  [{cand['candidate_id']}]  →  {cand['job_title']}")
    if not reqcfg:
        print("   SKIPPED — no check config for this req."); audit.append([cand["candidate_id"], "skipped", "no config"]); return

    st = cand.get("verification_status", "").strip().lower()
    days = to_int(cand.get("days_in_state"))
    link = generate_consent_link(cand)

    # ---- 1) Consent not yet requested -> request it (GATE 1 begins) ----
    if st in ("consent_not_requested", ""):
        print("   Requesting written authorization FIRST. No check runs until it's signed (FCRA).")
        reach_candidate(cand, fill(CONSENT_REQUEST, first_name=cand["first_name"],
                                   job_title=cand["job_title"], consent_link=link),
                        subject=f"Authorization needed — next step for {cand['job_title']}")
        audit.append([cand["candidate_id"], "consent_requested", "FCRA disclosure sent"]); return

    # ---- 2) Consent pending -> nudge, then escalate. NEVER start checks. ----
    if st == "consent_pending":
        if days >= cfg["consent_escalate_days"]:
            print(f"   Consent unsigned for {days}d. Escalating to recruiter — agent will NOT start checks without it.")
            notify_person(rname, remail, f"Consent overdue — {name}",
                          f"{name} hasn't signed the background-check authorization in {days} days. "
                          "Please follow up personally. No checks have been or will be run without consent.")
            audit.append([cand["candidate_id"], "consent_escalated", f"{days}d unsigned"])
        elif days >= cfg["consent_nudge_days"]:
            print(f"   Consent unsigned for {days}d. Sending a friendly nudge.")
            reach_candidate(cand, fill(CONSENT_NUDGE, first_name=cand["first_name"],
                                       job_title=cand["job_title"], consent_link=link),
                            subject=f"Reminder: authorization for {cand['job_title']}")
            audit.append([cand["candidate_id"], "consent_nudged", f"{days}d"])
        else:
            print(f"   Consent out {days}d — within window. Waiting.")
            audit.append([cand["candidate_id"], "awaiting_consent", f"{days}d"])
        return

    # ---- 3) Consent received -> NOW (and only now) trigger the checks ----
    if st == "consent_received":
        types = reqcfg["check_types"]
        print(f"   Consent documented ✓ — initiating checks: {', '.join(types)}.")
        initiate_checks(cand, types)
        reach_candidate(cand, fill(STATUS_UPDATE, first_name=cand["first_name"], job_title=cand["job_title"]),
                        subject=f"Your checks for {cand['job_title']} are underway")
        audit.append([cand["candidate_id"], "checks_initiated", ",".join(types)]); return

    # ---- 4) In progress -> track, update candidate, chase slow checks ----
    if st == "checks_in_progress":
        since_update = to_int(cand.get("days_since_update"))
        if days >= cfg["check_overdue_days"]:
            print(f"   Checks running {days}d (over {cfg['check_overdue_days']}d). Chasing provider via recruiter.")
            notify_person(rname, remail, f"Checks slow — {name}",
                          f"Verification for {name} has been in progress {days} days. Please chase the provider.")
            audit.append([cand["candidate_id"], "checks_chased", f"{days}d"])
        if since_update >= cfg["status_update_every_days"]:
            print(f"   Candidate update due ({since_update}d since last). Sending status note.")
            reach_candidate(cand, fill(STATUS_UPDATE, first_name=cand["first_name"], job_title=cand["job_title"]),
                            subject=f"Update on your {cand['job_title']} checks")
            audit.append([cand["candidate_id"], "status_update", f"{since_update}d since last"])
        elif days < cfg["check_overdue_days"]:
            print(f"   In progress {days}d — on track, candidate recently updated. No action.")
            audit.append([cand["candidate_id"], "on_track", f"{days}d"])
        return

    # ---- 5) Complete -> CLEAR advances; FLAGGED goes to humans (GATE 2) ----
    if st == "checks_complete":
        result = cand.get("check_result", "").strip().lower()
        if result == "clear":
            print("   Result: CLEAR ✓ — advancing to Offer and sharing the good news.")
            advance_stage(cand, "Offer")
            reach_candidate(cand, fill(CLEAR_GOODNEWS, first_name=cand["first_name"], job_title=cand["job_title"]),
                            subject=f"Checks complete — {cand['job_title']}")
            audit.append([cand["candidate_id"], "advanced_clear", "to offer"])
        elif result == "flagged":
            print("   Result: FLAGGED. Routing to HR + recruiter for the human-led FCRA adverse-action process.")
            print("   The agent takes NO action on the flag and sends the candidate NO automated message about it.")
            notify_person(hr["name"], hr["email"], f"FLAGGED check — {name} ({cand['job_title']}) — human review required",
                          "A background check returned a flag. FCRA requires a human-led process from here: "
                          "review the report, send the pre-adverse-action notice with a copy of the report, "
                          "allow time to dispute, then make and communicate the final decision. "
                          "The agent has paused all automated candidate messaging for this person.")
            notify_person(rname, remail, f"FLAGGED check — {name}", "See HR compliance notice. Human review required.")
            audit.append([cand["candidate_id"], "flag_to_human", "FCRA adverse-action hand-off"])
        else:
            print(f"   Complete but result is '{result}' — escalating to recruiter to clarify.")
            notify_person(rname, remail, f"Unclear check result — {name}", "Status complete but no clear/flagged result recorded.")
            audit.append([cand["candidate_id"], "escalated", "unclear result"])
        return

    print(f"   Status '{st}' — no action."); audit.append([cand["candidate_id"], "noop", st])

def main():
    now = datetime.now(timezone.utc)
    try:
        with open(CANDIDATES_CSV, newline="", encoding="utf-8") as f: cands = list(csv.DictReader(f))
        with open(VERIFICATION_JSON, encoding="utf-8") as f: data = json.load(f)
    except FileNotFoundError as e: sys.exit(f"Missing input file: {e.filename}")

    print(f"Stage 7 Verification Agent — {'DRY-RUN (nothing sent or triggered)' if DRY_RUN else 'LIVE'}")
    audit = []
    for c in cands:
        if c.get("pipeline_stage", "").strip().lower() != "reference / background" or c.get("status", "").strip().lower() != "active":
            print(f"\n• {c.get('first_name','?')} — skipped (not an active verification-stage candidate)"); continue
        process(c, data, audit)

    stamp = now.isoformat(timespec="seconds"); new = True
    try: open(AUDIT_LOG, "x").close()
    except FileExistsError: new = False
    with open(AUDIT_LOG, "a", newline="", encoding="utf-8") as f:
        w = csv.writer(f)
        if new: w.writerow(["run_at_utc", "candidate_id", "action", "detail"])
        for r in audit: w.writerow([stamp] + r)
    print(f"\nDone. Processed {len(audit)} candidate(s). Log: {AUDIT_LOG}")

if __name__ == "__main__":
    main()
