#!/usr/bin/env python3
"""Pull your eBay purchases (Trading API GetOrders, OrderRole=Buyer) into a CSV.

Token: set EBAY_TOKEN in your environment (preferred), or paste it into TOKEN below
(don't commit the file if you do).
Usage: python ebay_buyer_orders.py [--days 90] [--from 2026-08-01] [--to 2026-10-14] [--out purchases.csv]
"""
import argparse, csv, os, sys
from datetime import datetime, timedelta, timezone
import xml.etree.ElementTree as ET
import requests

TOKEN = os.environ.get("EBAY_TOKEN", "")  # or paste: TOKEN = "v^1.1#..."
ENDPOINT = "https://api.ebay.com/ws/api.dll"
NS = {"e": "urn:ebay:apis:eBLBaseComponents"}


def iso(dt):
    return dt.strftime("%Y-%m-%dT%H:%M:%S.000Z")


def t(node, path):
    el = node.find(path, NS)
    return el.text.strip() if el is not None and el.text else ""


def fetch_page(start, end, page):
    body = f"""<?xml version="1.0" encoding="utf-8"?>
<GetOrdersRequest xmlns="urn:ebay:apis:eBLBaseComponents">
  <RequesterCredentials><eBayAuthToken>{TOKEN}</eBayAuthToken></RequesterCredentials>
  <OrderRole>Buyer</OrderRole>
  <OrderStatus>All</OrderStatus>
  <CreateTimeFrom>{iso(start)}</CreateTimeFrom>
  <CreateTimeTo>{iso(end)}</CreateTimeTo>
  <DetailLevel>ReturnAll</DetailLevel>
  <Pagination><EntriesPerPage>100</EntriesPerPage><PageNumber>{page}</PageNumber></Pagination>
</GetOrdersRequest>"""
    headers = {
        "X-EBAY-API-CALL-NAME": "GetOrders",
        "X-EBAY-API-SITEID": "0",
        "X-EBAY-API-COMPATIBILITY-LEVEL": "1349",
        "Content-Type": "text/xml",
    }
    r = requests.post(ENDPOINT, data=body.encode(), headers=headers, timeout=60)
    r.raise_for_status()
    root = ET.fromstring(r.content)
    if t(root, "e:Ack") == "Failure":
        errs = "; ".join(t(e, "e:LongMessage") for e in root.findall("e:Errors", NS))
        sys.exit(f"eBay error: {errs}")
    return root


def main():
    ap = argparse.ArgumentParser()
    ap.add_argument("--days", type=int, default=90)
    ap.add_argument("--from", dest="start")
    ap.add_argument("--to", dest="end")
    ap.add_argument("--out", default="ebay_purchases.csv")
    a = ap.parse_args()
    if not TOKEN:
        sys.exit("Set EBAY_TOKEN first (see instructions).")

    now = datetime.now(timezone.utc)
    end = datetime.fromisoformat(a.end).replace(tzinfo=timezone.utc) if a.end else now
    start = datetime.fromisoformat(a.start).replace(tzinfo=timezone.utc) if a.start else end - timedelta(days=a.days)
    if end - start > timedelta(days=90):
        sys.exit("eBay allows at most 90 days per request; narrow the range.")

    rows, page = [], 1
    while True:
        root = fetch_page(start, end, page)
        for o in root.findall("e:OrderArray/e:Order", NS):
            oid, created, seller = t(o, "e:OrderID"), t(o, "e:CreatedTime"), t(o, "e:SellerUserID")
            for tx in o.findall("e:TransactionArray/e:Transaction", NS):
                tracks = [
                    f"{t(d, 'e:ShippingCarrierUsed')} {t(d, 'e:ShipmentTrackingNumber')}".strip()
                    for d in tx.findall("e:ShippingDetails/e:ShipmentTrackingDetails", NS)
                ]
                rows.append({
                    "order number": oid,
                    "purchase date": created,
                    "item title": t(tx, "e:Item/e:Title"),
                    "price": t(tx, "e:TransactionPrice"),
                    "seller": seller or t(tx, "e:Item/e:Seller/e:UserID"),
                    "tracking number": " | ".join(x for x in tracks if x),
                    "delivery date": t(tx, "e:ShippingServiceSelected/e:ShippingPackageInfo/e:ActualDeliveryTime"),
                })
        if t(root, "e:HasMoreOrders").lower() != "true":
            break
        page += 1

    with open(a.out, "w", newline="", encoding="utf-8") as f:
        w = csv.DictWriter(f, fieldnames=list(rows[0].keys()) if rows else
                           ["order number", "purchase date", "item title", "price", "seller", "tracking number", "delivery date"])
        w.writeheader()
        w.writerows(rows)
    print(f"Wrote {len(rows)} rows to {a.out}")


if __name__ == "__main__":
    main()
