#!/usr/bin/env python3
"""Small, single-process PDL starter. Fixture by default; sandbox only on approval."""
import argparse
from contextlib import contextmanager
from getpass import getpass
import json
import logging
from pathlib import Path
import subprocess
import sys
from unittest.mock import patch
import warnings

FIELDS = {
    "company": ("id", "name", "website", "industry"),
    "person": ("id", "full_name", "job_title", "job_company_name"),
}
# These are PDL's documented sandbox selectors, not customer leads.
SELECTORS = {
    "company": {"website": "adecco.com.pe"},
    "person": {"profile": ["linkedin.com/in/samantha_mccall"], "min_likelihood": 6},
}
HOST = "https://sandbox.api.peopledatalabs.com/v5/"

class SafeError(Exception):
    """Only fixed, non-sensitive error text reaches the terminal."""


def project(kind, records):
    result, seen = [], set()
    for record in records:
        if not isinstance(record, dict):
            raise SafeError("Unexpected record shape; stopped.")
        identifier = record.get("id")
        if not isinstance(identifier, str) or not identifier:
            raise SafeError("Missing record ID; stopped rather than guessing a match.")
        if identifier in seen:
            continue
        seen.add(identifier)
        result.append({field: record.get(field) for field in FIELDS[kind]})
    return result


def fixture(kind):
    data = json.loads(Path(__file__).with_name("synthetic.json").read_text())
    records = data[kind]
    return {"mode": "fixture", "synthetic": True, "api_calls": 0,
            "input_rows": len(records), "records": project(kind, records)}


def credential(source):
    if source == "keychain":
        if sys.platform != "darwin":
            raise SafeError("Keychain mode requires macOS. Use the hidden prompt instead.")
        # Never run this command in an agent terminal by itself: -w writes the secret.
        # Capture only inside this reviewed process; never print stdout or stderr.
        result = subprocess.run(
            ["/usr/bin/security", "find-generic-password", "-s", "skilltrade-pdl",
             "-a", "pdl-api", "-w"], capture_output=True, text=True, timeout=30,
            check=False)
        if result.returncode:
            raise SafeError("Keychain access failed or was declined; stopped.")
        value = result.stdout.rstrip("\r\n")
    else:
        if not sys.stdin.isatty():
            raise SafeError("Use your own interactive terminal for hidden entry.")
        with warnings.catch_warnings():
            warnings.simplefilter("error")
            value = getpass("PDL key (hidden; this process only): ")
    if not value.strip():
        raise SafeError("No credential supplied; stopped.")
    return value


@contextmanager
def guarded_transport():
    """Retain official SDK validation; replace its unbounded transport for this CLI."""
    import requests
    from peopledatalabs.requests import Request
    count = 0
    session = requests.Session()
    session.trust_env = False  # Do not inherit proxies or .netrc credentials.
    session.mount("https://", requests.adapters.HTTPAdapter(max_retries=0))

    def send(request, method):
        nonlocal count
        allowed = {HOST + path for path in ("person/enrich", "company/enrich")}
        if str(request.url) not in allowed or count:
            raise SafeError("Request boundary refused; stopped.")
        count += 1
        params = dict(request.params)
        params.pop("api_key", None)
        headers = {"X-Api-Key": request.api_key, "Accept": "application/json"}
        # Requests performs TLS verification by default. Never follow a redirect with a key.
        args = {"params": params} if method == "GET" else {"json": params}
        return session.request(method, str(request.url), headers=headers,
                               timeout=(5, 20), allow_redirects=False, **args)
    try:
        with patch.object(Request, "get", lambda request: send(request, "GET")), \
             patch.object(Request, "post", lambda request: send(request, "POST")):
            yield
    finally:
        session.close()


def sandbox(kind, key):
    # Single-purpose CLI: no SDK/debug/HTTP logging or raw exception output.
    logging.disable(logging.CRITICAL)
    from peopledatalabs import PDLPY
    client = PDLPY(api_key=key, sandbox=True)
    fields = ",".join(FIELDS[kind])
    with guarded_transport():
        response = getattr(client, kind).enrichment(
            **SELECTORS[kind], data_include=fields)
    if response.status_code == 404:
        return {"mode": "sandbox", "synthetic": True, "api_calls": 1, "records": []}
    if response.status_code != 200:
        raise SafeError("Provider request did not succeed; stopped without retry. Check your dashboard.")
    body = response.json()
    # Company enrichment is flat; person enrichment uses a data envelope.
    records = [body.get("data", body)]
    if len(records) > 1:
        raise SafeError("Response exceeded the one-record limit; stopped.")
    output = {"mode": "sandbox", "synthetic": True, "api_calls": 1,
              "records": project(kind, records)}
    # Fail closed if a provider ever reflects the credential in a selected field.
    if key in json.dumps(output):
        raise SafeError("Unexpected response content; stopped.")
    return output


def main(argv=None):
    parser = argparse.ArgumentParser(description=__doc__)
    parser.add_argument("kind", choices=FIELDS, nargs="?", default="company")
    parser.add_argument("--mode", choices=("fixture", "sandbox"), default="fixture")
    parser.add_argument("--approve-sandbox", action="store_true", help="Approve one sandbox request this run")
    parser.add_argument("--credential", choices=("prompt", "keychain"), default="prompt")
    args = parser.parse_args(argv)
    try:
        if args.mode == "fixture":
            output = fixture(args.kind)
        else:
            if not args.approve_sandbox:
                raise SafeError("Sandbox needs --approve-sandbox for one request. No credential read.")
            output = sandbox(args.kind, credential(args.credential))
        print(json.dumps(output, indent=2))
        return 0
    except SafeError as error:
        print(str(error), file=sys.stderr)
    except (Exception, KeyboardInterrupt):
        # Never print a traceback: third-party exceptions can contain request credentials.
        print("Operation stopped. No raw response or error was printed.", file=sys.stderr)
    return 1

if __name__ == "__main__":
    raise SystemExit(main())
