"""
Stage 4 — Assessment Agent  (Engagement agent role)
=====================================================
WHAT THIS DOES
  Once a candidate passes the phone screen, this agent:
    1. auto-sends the assessment with instructions and a deadline,
    2. nudges as the deadline approaches,
    3. routes the RESULTS to the recruiter the moment it's completed, and
    4. escalates to the recruiter if the deadline passes — it never auto-rejects.
  It handles communication and routing ONLY. It never scores or decides pass/fail —
  that judgment always belongs to the recruiter.

SAFETY (same posture as Stage 2 & 3)
  - DRY_RUN = True by default: prints what it WOULD do, sends nothing.
  - Texts only to candidates who consented (consent_to_contact = TRUE).
  - Never decides an outcome; results and overdue cases go to a human.
  - Every action is written to an audit log.

You only edit the CONFIG section.
"""

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

# =====================================================================
#  CONFIG  —  EDIT THIS SECTION
# =====================================================================
DRY_RUN = True
CANDIDATES_CSV   = "stage4_candidates_sample.csv"
ASSESSMENTS_JSON = "stage4_assessments_sample.json"
AUDIT_LOG        = "stage4_assessment_audit_log.csv"

NUDGE_WINDOW_DAYS = 1     # nudge when this many days (or fewer) remain before the deadline
COMPANY_NAME = "Acme Talent"
FROM_NAME    = "Arun from Acme Recruiting"

# ---- Templates ({placeholders} fill in per candidate). Edit freely. ----
ASSESS_SUBJECT = "Next step for {job_title}: the {assessment_name}"
ASSESS_BODY = """Hi {first_name}, nice work on your phone screen for {job_title}!
The next step is the {assessment_name}. Please complete it within {deadline_days} days:
{assessment_link}
Questions? Just reply. — {from_name}"""

NUDGE_BODY = "Hi {first_name}, friendly reminder: your {assessment_name} for {job_title} is due in {remaining} day(s). Here's the link: {assessment_link}"

OVERDUE_CANDIDATE_BODY = "Hi {first_name}, the window for your {assessment_name} has closed. We'd still love your submission — reply if you need a little more time."

# =====================================================================
#  PROVIDER HOOKS  —  plug your services in here (examples commented)
# =====================================================================
def get_assessment_status(cand):
    """Return (status, score). DRY_RUN reads the CSV. Live: call your assessment platform API."""
    if DRY_RUN:
        return cand.get("assessment_status", "not_sent").strip().lower(), cand.get("assessment_score", "").strip()
    raise NotImplementedError("Wire your assessment platform into get_assessment_status().")

def send_email(to_email, subject, body):
    if DRY_RUN:
        print(f"      [DRY-RUN EMAIL] to {to_email}  |  {subject}"); return True
    raise NotImplementedError("Add your email provider in send_email().")

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

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

# =====================================================================
#  ENGINE  —  you can leave this alone
# =====================================================================
def is_true(v): return str(v).strip().lower() in ("true", "yes", "y", "1")

def fill(t, **kw): return t.format(**kw)

def reach_candidate(cand, body, subject=None):
    """Email always; text too if they consented."""
    send_email(cand["email"], subject or f"Your {COMPANY_NAME} assessment", body)
    if cand["phone"] and is_true(cand["consent_to_contact"]):
        send_sms(cand["phone"], body)
    elif cand["phone"]:
        print("      (text skipped — no consent)")

def process(cand, reqs, recruiters, audit):
    name = f'{cand["first_name"]} {cand["last_name"]}'.strip()
    cfg = reqs.get(cand["job_req_id"])
    rname = cand["recruiter_owner"]; remail = recruiters.get(rname, "")
    print(f"\n• {name}  [{cand['candidate_id']}]  →  {cand['job_title']}")

    if not cfg:
        print("   SKIPPED — no assessment config for this req.")
        audit.append([cand["candidate_id"], "skipped", "no req config"]); return

    if not cfg["assessment_required"]:
        print("   No assessment required for this role — advancing without one.")
        audit.append([cand["candidate_id"], "no_assessment", "role has none"]); return

    aname, alink, ddays = cfg["assessment_name"], cfg["assessment_link"], cfg["deadline_days"]
    status, score = get_assessment_status(cand)

    # 1) Completed -> route results to the recruiter (human decides pass/fail)
    if status == "completed":
        print(f"   Completed. Routing results to recruiter for the decision (score: {score or 'n/a'}).")
        notify_recruiter(rname, remail,
                         f"Assessment complete — {name} ({cand['job_title']})",
                         f"{name} finished the {aname}. Result: {score or 'see platform'}. "
                         f"Please review and decide the next step.")
        audit.append([cand["candidate_id"], "results_routed", f"score {score or 'n/a'}"]); return

    # 2) Not sent yet -> send it now with the deadline
    if status in ("not_sent", "", None):
        print(f"   Sending the {aname} (due in {ddays} days).")
        reach_candidate(cand,
            fill(ASSESS_BODY, first_name=cand["first_name"], job_title=cand["job_title"],
                 assessment_name=aname, deadline_days=ddays, assessment_link=alink, from_name=FROM_NAME),
            subject=fill(ASSESS_SUBJECT, job_title=cand["job_title"], assessment_name=aname))
        audit.append([cand["candidate_id"], "assessment_sent", aname]); return

    # 3) Sent, not done -> nudge or escalate based on the deadline
    try:
        days_since = int(cand.get("days_since_sent", "") or 0)
    except ValueError:
        days_since = 0
    remaining = ddays - days_since

    if remaining <= 0:
        print(f"   OVERDUE by {-remaining} day(s). Final note to candidate + escalating to recruiter (no auto-reject).")
        reach_candidate(cand, fill(OVERDUE_CANDIDATE_BODY, first_name=cand["first_name"], assessment_name=aname))
        notify_recruiter(rname, remail,
                         f"Assessment overdue — {name} ({cand['job_title']})",
                         f"{name} hasn't completed the {aname} ({days_since} days, past the {ddays}-day window). "
                         f"Your call: extend, follow up, or close. The agent will not auto-reject.")
        audit.append([cand["candidate_id"], "escalated", "overdue"]); return

    if remaining <= NUDGE_WINDOW_DAYS:
        print(f"   Deadline approaching ({remaining} day(s) left). Sending a nudge.")
        reach_candidate(cand, fill(NUDGE_BODY, first_name=cand["first_name"], job_title=cand["job_title"],
                                    assessment_name=aname, remaining=remaining, assessment_link=alink))
        audit.append([cand["candidate_id"], "nudged", f"{remaining}d left"]); return

    print(f"   In progress, {remaining} day(s) left — no action needed yet.")
    audit.append([cand["candidate_id"], "in_progress", f"{remaining}d left"])

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(ASSESSMENTS_JSON, encoding="utf-8") as f:
            data = json.load(f)
    except FileNotFoundError as e:
        sys.exit(f"Missing input file: {e.filename}")
    reqs, recruiters = data["reqs"], data.get("recruiters", {})

    print(f"Stage 4 Assessment Agent — {'DRY-RUN (nothing sent)' if DRY_RUN else 'LIVE'}")

    audit = []
    for c in cands:
        if c.get("pipeline_stage", "").strip().lower() != "assessment" or c.get("status", "").strip().lower() != "active":
            print(f"\n• {c.get('first_name','?')} — skipped (not an active assessment-stage candidate)")
            continue
        process(c, reqs, recruiters, 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()
