"""
Recruiting Outreach Agent  —  Stage 2 (Screening) action-taker
================================================================
WHAT THIS DOES
  After a HUMAN recruiter approves the qualified / not-qualified split, this agent:
    • sends a respectful decline note (email) to the NOT-QUALIFIED, and
    • sends interview outreach (email + text) to the QUALIFIED, with a booking link.

WHAT THIS DELIBERATELY DOES NOT DO
  It NEVER decides who is qualified. It only acts on rows a human marked
  human_approved = TRUE. No approval -> the agent skips the candidate. That single
  rule is what keeps you compliant with California's automated-decision rules.

HOW IT STAYS SAFE
  • DRY_RUN = True by default: it PRINTS what it would send, sends nothing. Test freely.
  • Human-approval gate enforced in code (see GATE checks below).
  • Text messages only go to candidates who gave consent (consent_to_contact = TRUE) -> TCPA.
  • Every action is written to an audit log (supports CA's 4-year recordkeeping rule).

You only need to edit the CONFIG section. The engine below it you can leave alone.
"""

import csv
import sys
from datetime import datetime, timezone

# =====================================================================
#  CONFIG  —  EDIT THIS SECTION
# =====================================================================

DRY_RUN = True                       # True = print only (safe). Flip to False to send for real.
INPUT_CSV = "approved_candidates_sample.csv"   # the human-approved list (any ATS can export this)
AUDIT_LOG = "outreach_audit_log.csv"           # the record of everything the agent did

COMPANY_NAME    = "Acme Talent"
FROM_NAME       = "Jordan from Acme Recruiting"
FROM_EMAIL      = "careers@acme.example"
REPLY_TO        = "careers@acme.example"
SCHEDULING_LINK = "https://calendly.com/acme-recruiting/first-round"   # candidate self-books here

# ---- Message templates. {placeholders} get filled in per candidate. Edit the wording freely. ----

DECLINE_EMAIL_SUBJECT = "Update on your application for {job_title} at {company}"
DECLINE_EMAIL_BODY = """Hi {first_name},

Thank you for taking the time to apply for the {job_title} role at {company}, and for your interest in joining us.

After careful review, we've decided not to move forward with your application for this particular role. This was not an easy decision, and it isn't a reflection of your abilities — we simply moved ahead with candidates whose experience more closely matched what this specific role needs right now.

We'd genuinely welcome you to apply for future openings that fit your background. Thank you again, and we wish you the very best in your search.

Warm regards,
{from_name}
{company}
"""

QUALIFIED_EMAIL_SUBJECT = "Great news — let's set up your interview for {job_title}"
QUALIFIED_EMAIL_BODY = """Hi {first_name},

Great news — we were impressed by your application for the {job_title} role at {company}, and we'd love to talk with you.

You can book a time for a first-round interview that works for you here:
{scheduling_link}

(We're also sending you a quick text so you can grab a slot from your phone.) If you have any questions before then, just reply to this email.

Looking forward to speaking with you,
{from_name}
{company}
"""

QUALIFIED_SMS = (
    "Hi {first_name}, it's {company} recruiting. We'd love to set up a first interview "
    "for the {job_title} role — pick a time here: {scheduling_link} "
    "Reply STOP to opt out."
)

# =====================================================================
#  SENDING HOOKS  —  plug your providers in here (examples commented)
# =====================================================================

def send_email(to_email, subject, body):
    """Replace the DRY_RUN block's 'else' with your email provider."""
    if DRY_RUN:
        print(f"   [DRY-RUN EMAIL] to {to_email}\n      subj: {subject}")
        return True
    # --- Example (SendGrid). Install: pip install sendgrid ---
    # import os
    # from sendgrid import SendGridAPIClient
    # from sendgrid.helpers.mail import Mail
    # message = Mail(from_email=FROM_EMAIL, to_emails=to_email, subject=subject, plain_text_content=body)
    # SendGridAPIClient(os.environ["SENDGRID_API_KEY"]).send(message)
    raise NotImplementedError("Add your email provider in send_email().")

def send_sms(to_phone, body):
    """Replace the DRY_RUN block's 'else' with your SMS provider."""
    if DRY_RUN:
        print(f"   [DRY-RUN TEXT]  to {to_phone}\n      msg:  {body}")
        return True
    # --- Example (Twilio). Install: pip install twilio ---
    # import os
    # from twilio.rest import Client
    # client = Client(os.environ["TWILIO_SID"], os.environ["TWILIO_AUTH"])
    # client.messages.create(to=to_phone, from_=os.environ["TWILIO_FROM"], body=body)
    raise NotImplementedError("Add your SMS provider in send_sms().")

# =====================================================================
#  ENGINE  —  you can leave this alone
# =====================================================================

def is_true(value):
    return str(value).strip().lower() in ("true", "yes", "y", "1")

def fill(template, row):
    return template.format(
        first_name=row.get("first_name", "there").strip() or "there",
        job_title=row.get("job_title", "the role").strip() or "the role",
        company=COMPANY_NAME,
        from_name=FROM_NAME,
        scheduling_link=SCHEDULING_LINK,
    )

def process(row, audit):
    cid = row.get("candidate_id", "").strip()
    name = f'{row.get("first_name","").strip()} {row.get("last_name","").strip()}'.strip()
    email = row.get("email", "").strip()
    phone = row.get("phone", "").strip()
    disposition = row.get("disposition", "").strip().lower()
    print(f"\n• {name or '(no name)'}  [{cid}]")

    # ---- GATE 1: a human must have approved this row ----
    if not is_true(row.get("human_approved", "")):
        print("   SKIPPED — no human approval on this record. Agent will not act.")
        audit.append([cid, email, disposition, "none", "SKIPPED: not human-approved", ""])
        return

    # ---- GATE 2: audit trail must name the approver ----
    approver = row.get("approver", "").strip()
    approved_at = row.get("approved_at", "").strip()
    if not approver:
        print("   SKIPPED — approved flag set but no approver named. Won't act without accountability.")
        audit.append([cid, email, disposition, "none", "SKIPPED: no approver named", ""])
        return

    channels = []

    if disposition == "not qualified":
        if email:
            send_email(email, fill(DECLINE_EMAIL_SUBJECT, row), fill(DECLINE_EMAIL_BODY, row))
            channels.append("decline-email")
        else:
            print("   No email on file — cannot send decline.")

    elif disposition == "qualified":
        if email:
            send_email(email, fill(QUALIFIED_EMAIL_SUBJECT, row), fill(QUALIFIED_EMAIL_BODY, row))
            channels.append("outreach-email")
        # Text only with consent (TCPA) and a phone number
        if phone and is_true(row.get("consent_to_contact", "")):
            send_sms(phone, fill(QUALIFIED_SMS, row))
            channels.append("outreach-sms")
        elif phone:
            print("   TEXT skipped — candidate has not consented to texts. Email only.")
        else:
            print("   No phone on file — email only.")

    else:
        print(f"   SKIPPED — unrecognized disposition '{disposition}'. Use 'Qualified' or 'Not qualified'.")
        audit.append([cid, email, disposition, "none", "SKIPPED: bad disposition", approver])
        return

    audit.append([cid, email, disposition, "; ".join(channels) or "none",
                  "SENT" if channels else "nothing to send", f"{approver} @ {approved_at}"])

def main():
    try:
        with open(INPUT_CSV, newline="", encoding="utf-8") as f:
            rows = list(csv.DictReader(f))
    except FileNotFoundError:
        sys.exit(f"Could not find {INPUT_CSV}. Put your approved list there and try again.")

    mode = "DRY-RUN (printing only, nothing sent)" if DRY_RUN else "LIVE (really sending!)"
    print(f"Recruiting Outreach Agent — {mode}\nReading {len(rows)} candidate(s) from {INPUT_CSV}")

    audit = []
    for row in rows:
        process(row, audit)

    stamp = datetime.now(timezone.utc).isoformat(timespec="seconds")
    write_header = True
    try:
        with open(AUDIT_LOG, "x", newline="", encoding="utf-8") as f:
            pass
    except FileExistsError:
        write_header = False
    with open(AUDIT_LOG, "a", newline="", encoding="utf-8") as f:
        w = csv.writer(f)
        if write_header:
            w.writerow(["run_at_utc", "candidate_id", "email", "disposition", "channels", "result", "approved_by"])
        for r in audit:
            w.writerow([stamp] + r)

    sent = sum(1 for r in audit if r[4] == "SENT")
    skipped = sum(1 for r in audit if str(r[4]).startswith("SKIPPED"))
    print(f"\nDone. Acted on {sent}, skipped {skipped}. Full record written to {AUDIT_LOG}.")

if __name__ == "__main__":
    main()
