#!/usr/bin/env python3
from __future__ import annotations

import argparse
import json
from dataclasses import dataclass
from datetime import datetime, timezone
from pathlib import Path
from typing import Dict, List, Optional
from urllib import error, request

NOTION_VERSION = "2025-09-03"
DEFAULT_NOTION_KEY_PATH = "/Users/openclaw/.config/notion/api_key"
PIPELINE_DATA_SOURCE_ID = "cbde3032-5e2c-4557-8a00-cd2d14d4ab83"
RESUME_SOURCE_PATH = "/Users/openclaw/.openclaw/workspace/job-search/RESUME-SOURCE.md"

# Verified overrides win over conflicting resume text when a specific fact has been
# corrected during review but the upstream source file has not been updated yet.
VERIFIED_FACTS = {
    "reconserve_employee_count": "40+",
}


@dataclass
class CandidateRecord:
    page_id: str
    page_url: str
    name: str
    role_title: str
    company: str
    fit_score: Optional[float]
    strategy: Optional[str]
    status: Optional[str]
    source: Optional[str]
    location: str
    salary: str
    role_url: Optional[str]
    recruiter_name: str
    research_payload_raw: str


PROFILE = {
    "name": "Braden McLeish",
    "email": "bradenmcleish@icloud.com",
    "phone": "714-794-9223",
    "summary": (
        "Operations, manufacturing, and logistics leader with 20+ years of experience "
        "overseeing multi-site manufacturing, distribution, and field operations."
    ),
    "triune": (
        "At Triune, I oversee full operational functions for a luxury pool construction firm with full "
        "P&L responsibility, cutting labor cost per hour by 27 percent, reducing waterproofing costs by "
        "60 percent, and completing projects 40 percent below budget."
    ),
    "reconserve": (
        "At Reconserve of California, I led multi-site manufacturing and transportation operations with "
        "full responsibility for the Los Angeles plant, oversight of the Ontario facility and sister "
        f"company Topnotch Foods, and management of a $35M asset portfolio and $15M P&L. I led {VERIFIED_FACTS['reconserve_employee_count']} "
        "employees through supervisors and department heads, directed production, food handling, "
        "transportation, and distribution for accounts including Bimbo, Thomas, Safeway, and Frito Lay, "
        "strengthened HACCP-aligned compliance, delivered a 20 percent reduction in electrical costs, and "
        "maintained more than five years with zero major injuries."
    ),
    "eagle": (
        "Earlier in my career, I held terminal and dispatch leadership roles with full P&L responsibility, "
        "improving on-time delivery metrics from 70 percent to 92 percent in one turnaround assignment "
        "and restoring another terminal from 90 percent to 98 percent."
    ),
}

PLANT_KEYWORDS = (
    "plant",
    "manufacturing",
    "operations",
    "site",
    "facility",
    "facilities",
    "director of operations",
    "general manager",
)


def notion_request(method: str, url: str, payload: Optional[dict] = None) -> dict:
    notion_key = Path(DEFAULT_NOTION_KEY_PATH).read_text(encoding="utf-8").strip()
    headers = {
        "Authorization": f"Bearer {notion_key}",
        "Notion-Version": NOTION_VERSION,
        "Content-Type": "application/json",
    }
    body = None if payload is None else json.dumps(payload).encode("utf-8")
    req = request.Request(url, data=body, headers=headers, method=method)
    try:
        with request.urlopen(req, timeout=20) as resp:
            text = resp.read().decode("utf-8")
            return json.loads(text) if text else {}
    except error.HTTPError as exc:
        detail = exc.read().decode("utf-8", errors="replace")
        raise RuntimeError(f"notion_http_error:{exc.code}:{detail}") from exc


def chunk_rich_text(text: str, limit: int = 2000) -> List[dict]:
    if not text:
        return []
    chunks: List[dict] = []
    current: List[str] = []
    size = 0
    for ch in text:
        units = len(ch.encode("utf-16-le")) // 2
        if current and size + units > limit:
            chunks.append({"text": {"content": "".join(current)}})
            current = []
            size = 0
        current.append(ch)
        size += units
    if current:
        chunks.append({"text": {"content": "".join(current)}})
    return chunks


def _plain_text(items: List[dict], field: str) -> str:
    return "".join(item.get("plain_text", "") for item in items or [])


def page_to_candidate(page: dict) -> CandidateRecord:
    props = page["properties"]
    title = _plain_text(props["Name"].get("title", []), "title")
    role_title = _plain_text(props["Role Title"].get("rich_text", []), "rich_text")
    company = _plain_text(props["Company"].get("rich_text", []), "rich_text")
    location = _plain_text(props["Location"].get("rich_text", []), "rich_text")
    salary = _plain_text(props["Salary"].get("rich_text", []), "rich_text")
    recruiter_name = _plain_text(props["Recruiter Name"].get("rich_text", []), "rich_text")
    research_payload_raw = _plain_text(props["Research Payload"].get("rich_text", []), "rich_text")
    return CandidateRecord(
        page_id=page["id"],
        page_url=page["url"],
        name=title,
        role_title=role_title,
        company=company,
        fit_score=props["Fit Score"].get("number"),
        strategy=(props["Strategy"].get("select") or {}).get("name"),
        status=(props["Status"].get("select") or {}).get("name"),
        source=(props["Source"].get("select") or {}).get("name"),
        location=location,
        salary=salary,
        role_url=props["Role URL"].get("url"),
        recruiter_name=recruiter_name,
        research_payload_raw=research_payload_raw,
    )


def fetch_candidate(page_id: str) -> CandidateRecord:
    page = notion_request("GET", f"https://api.notion.com/v1/pages/{page_id}")
    return page_to_candidate(page)


def parse_research_payload(raw: str) -> dict:
    if not raw.strip():
        return {}
    try:
        return json.loads(raw)
    except json.JSONDecodeError:
        return {"raw": raw}


def resume_pointer_summary() -> str:
    return Path(RESUME_SOURCE_PATH).read_text(encoding="utf-8").strip()


def is_eligible(candidate: CandidateRecord) -> bool:
    if candidate.source == "Job Board Digest":
        return False
    if not candidate.strategy or candidate.strategy == "Skip":
        return False
    if candidate.fit_score is None or candidate.fit_score < 5.0:
        return False
    return True


def choose_credential_block(candidate: CandidateRecord) -> str:
    role = candidate.role_title.lower()
    if any(token in role for token in PLANT_KEYWORDS):
        return PROFILE["reconserve"]
    return (
        f"{PROFILE['triune']} {PROFILE['reconserve']} {PROFILE['eagle']}"
    )


def relevance_bridge(candidate: CandidateRecord) -> str:
    company = candidate.company or "the company"
    role = candidate.role_title or "this role"
    if candidate.strategy == "Connect First":
        return (
            f"The scale, operating scope, and performance expectations behind the {role} role at "
            f"{company} align well with the environments where I have delivered measurable gains "
            "in cost control, safety, throughput, and operational discipline."
        )
    if candidate.strategy == "Direct Apply":
        return (
            f"{company} appears to need a leader who can stabilize execution quickly while improving "
            f"cost, accountability, and operating rhythm. That is the same lane where Braden has been "
            "most effective across construction, manufacturing, and logistics environments."
        )
    return (
        f"The opportunity at {company} lines up with Braden's background in operational leadership, "
        "regulated environments, and performance improvement."
    )


def generate_cover_draft(candidate: CandidateRecord) -> Optional[str]:
    if candidate.strategy not in {"Direct Apply", "Connect First"}:
        return None
    credential = choose_credential_block(candidate)
    bridge = relevance_bridge(candidate)
    company = candidate.company or "the company"
    role = candidate.role_title or "the role"
    return "\n\n".join([
        "Dear Hiring Team,",
        f"My background is closely aligned with the {role} opportunity at {company}, particularly in high-accountability operations leadership, cost control, safety, and performance improvement.",
        credential,
        bridge,
        (
            f"I would welcome the opportunity to discuss how my background in plant and operations leadership "
            f"could support {company}'s objectives. You can reach me at {PROFILE['email']} or {PROFILE['phone']}.\n\n"
            f"Best regards,\n{PROFILE['name']}"
        ),
    ])


def generate_connection_note(candidate: CandidateRecord) -> Optional[str]:
    if candidate.strategy not in {"Connect First", "Referral First"}:
        return None
    company = candidate.company or "your team"
    role = candidate.role_title or "the role"
    note = (
        f"Braden McLeish here. I lead multi-site operations with responsibility for a $35M asset portfolio and "
        f"$15M P&L, including regulated plant and logistics environments. The {role} role at {company} looks "
        "closely aligned with my background, and I'd value connecting."
    )
    if len(note) > 300:
        raise RuntimeError(f"connection_note_too_long:{len(note)}")
    return note


def generate_recruiter_response(candidate: CandidateRecord) -> Optional[str]:
    if candidate.strategy != "Recruiter Response":
        return None
    name = candidate.recruiter_name or "there"
    return "\n\n".join([
        f"Hi {name},",
        "Thank you for reaching out regarding the opportunity.",
        choose_credential_block(candidate),
        (
            "The role sounds potentially aligned with my experience, and I'd be glad to learn more. "
            "If the scope, reporting structure, and compensation range are in line with that level of responsibility, "
            "I'd be open to a conversation."
        ),
        (
            f"I've attached my resume for reference. You can reach me here or at {PROFILE['email']} / {PROFILE['phone']}.\n\n"
            f"Best,\n{PROFILE['name']}"
        ),
    ])


def generate_recommendation_note(candidate: CandidateRecord) -> str:
    company = candidate.company or "this company"
    role = candidate.role_title or "this role"
    fit = f"{candidate.fit_score:.2f}" if candidate.fit_score is not None else "n/a"
    if candidate.strategy == "Connect First":
        return (
            f"{role} at {company} is worth pursuing because it lines up with Braden's strongest plant and multi-site "
            f"operations credentials, and the compensation signal appears to clear the floor. Fit Score {fit}; "
            "best path is to open with a connection attempt before deciding whether to submit a full application."
        )
    if candidate.strategy == "Direct Apply":
        return (
            f"{role} at {company} is a viable direct-apply target because the role level is aligned and the operating "
            f"context matches Braden's resume. Fit Score {fit}; the draft emphasizes quantified leadership proof and "
            "leaves room to tighten company-specific language after review."
        )
    return (
        f"{role} at {company} is worth pursuing based on the current fit signal and should move through the approval lane "
        f"with strategy {candidate.strategy}. Fit Score {fit}; review the outreach draft for any company-specific edits before approval."
    )


def build_drafts(candidate: CandidateRecord) -> Dict[str, Optional[str]]:
    drafts = {
        "cover_draft": generate_cover_draft(candidate),
        "connection_draft": generate_connection_note(candidate),
        "recruiter_response": generate_recruiter_response(candidate),
        "recommendation_note": generate_recommendation_note(candidate),
    }
    if candidate.strategy == "Recruiter Response":
        drafts["cover_draft"] = drafts["recruiter_response"]
    return drafts


def draft_assets_summary(candidate: CandidateRecord, drafts: Dict[str, Optional[str]]) -> str:
    parts = [f"Phase 2 draft engine generated assets for {candidate.role_title or candidate.name}."]
    if drafts.get("cover_draft"):
        parts.append("Cover Draft ready.")
    if drafts.get("connection_draft"):
        parts.append("Connection Draft ready.")
    if drafts.get("recommendation_note"):
        parts.append("Recommendation note ready.")
    return " ".join(parts)


def update_page_with_drafts(candidate: CandidateRecord, drafts: Dict[str, Optional[str]], dry_run: bool) -> None:
    now = datetime.now(timezone.utc).date().isoformat()
    properties: Dict[str, dict] = {
        "Last Updated": {"date": {"start": now}},
        "Draft Assets": {"rich_text": chunk_rich_text(draft_assets_summary(candidate, drafts))},
    }
    if candidate.status != "Draft Ready":
        properties["Status"] = {"select": {"name": "Draft Ready"}}
    if drafts.get("cover_draft"):
        properties["Cover Draft"] = {"rich_text": chunk_rich_text(drafts["cover_draft"])}
    if drafts.get("connection_draft"):
        properties["Connection Draft"] = {"rich_text": chunk_rich_text(drafts["connection_draft"])}
    if drafts.get("recommendation_note"):
        properties["Notes"] = {"rich_text": chunk_rich_text(drafts["recommendation_note"])}
    if dry_run:
        return
    notion_request(
        "PATCH",
        f"https://api.notion.com/v1/pages/{candidate.page_id}",
        {"properties": properties},
    )


def build_digest(records: List[CandidateRecord]) -> str:
    lines = ["Draft-ready roles for review:"]
    for idx, record in enumerate(records, start=1):
        fit = f"{record.fit_score:.2f}" if record.fit_score is not None else "n/a"
        lines.append(
            f"{idx}. {record.role_title} | {record.company or 'Unknown company'} | fit {fit} | "
            f"{record.strategy} | {record.page_url}"
        )
    lines.append("Reply in natural language with approve / skip / hold instructions.")
    return "\n".join(lines)


def parse_approval_response(text: str, records: List[CandidateRecord]) -> Dict[str, List[str]]:
    lowered = text.lower()
    result = {"approve": [], "skip": [], "hold": []}
    if len(records) == 1 and ("this one" in lowered or "this role" in lowered or "this record" in lowered):
        if "approve" in lowered:
            result["approve"].append(records[0].page_id)
        if "skip" in lowered:
            result["skip"].append(records[0].page_id)
        if "hold" in lowered or "tweak" in lowered or "edit" in lowered:
            result["hold"].append(records[0].page_id)
    for idx, record in enumerate(records, start=1):
        ref_tokens = {
            str(idx),
            (record.role_title or "").lower(),
            (record.company or "").lower(),
        }
        if any(token and token in lowered for token in ref_tokens):
            if "approve" in lowered:
                result["approve"].append(record.page_id)
            if "skip" in lowered:
                result["skip"].append(record.page_id)
            if "hold" in lowered or "tweak" in lowered or "edit" in lowered:
                result["hold"].append(record.page_id)
    if "send them all" in lowered or "approve all" in lowered:
        result["approve"] = [record.page_id for record in records]
    return result


def main() -> None:
    parser = argparse.ArgumentParser()
    parser.add_argument("--page-id", required=True, help="Notion page id to draft against")
    parser.add_argument("--apply", action="store_true", help="Write drafts back to Notion")
    args = parser.parse_args()

    candidate = fetch_candidate(args.page_id)
    if not is_eligible(candidate):
        raise SystemExit(f"candidate_not_eligible:{candidate.page_id}")

    drafts = build_drafts(candidate)
    update_page_with_drafts(candidate, drafts, dry_run=not args.apply)

    result = {
        "candidate": candidate.__dict__,
        "resume_pointer": resume_pointer_summary(),
        "research_payload": parse_research_payload(candidate.research_payload_raw),
        "drafts": drafts,
        "digest_preview": build_digest([candidate]),
        "approval_parse_examples": {
            "approve 1": parse_approval_response("approve 1", [candidate]),
            "hold this one": parse_approval_response("hold this one", [candidate]),
        },
        "applied": bool(args.apply),
    }
    print(json.dumps(result, ensure_ascii=False, indent=2))


if __name__ == "__main__":
    main()
