#!/usr/bin/env python3
"""
Automated Cazana login using the credential saved in macOS Passwords, so a
dead Cazana session (they expire unpredictably, see CLAUDE.md) can be
recovered without Mark sitting down to log in by hand.

The password is never printed, logged, written to a file, or returned from
any function here, only held in a local variable long enough to type it into
the page. It is read at run time from the macOS keychain via the `security`
command line tool, the same store the Passwords app uses, so it never lives
in this repo or anywhere Claude (or anyone reading this code) can see it.

One time setup, done by Mark, not by this script:
  1. Open the Passwords app (or System Settings > Passwords).
  2. Add (or confirm) an entry for the website trade.percayso-vehicle-intelligence.co.uk
     with the Cazana login email and password.
  3. The first time this script runs, macOS will prompt for permission to
     let it read that keychain item. Choose "Always Allow" so future
     scheduled runs do not need anyone at the keyboard.

Usage:   python3 auto_login_cazana.py            headless, for real use
         python3 auto_login_cazana.py --headed    a visible window, to watch it work

This only ever logs in. It never reads or bids on anything else.
"""

import sys
import subprocess
from playwright.sync_api import sync_playwright
from bidbrain import browser, db

SERVER = "trade.percayso-vehicle-intelligence.co.uk"
LOGIN_URL = "https://trade.percayso-vehicle-intelligence.co.uk/login"


def _keychain_lookup(server):
    """Return (account, password) for this server from the macOS login
    keychain, or (None, None) if nothing is saved. Never prints either
    value. Raises RuntimeError (with no secret in the message) if the
    `security` tool itself is missing, which should not happen on macOS."""
    try:
        pw = subprocess.run(
            ["security", "find-internet-password", "-s", server, "-w"],
            capture_output=True, text=True,
        )
    except FileNotFoundError:
        raise RuntimeError("The macOS `security` command line tool is not available.")
    if pw.returncode != 0:
        return None, None
    password = pw.stdout.rstrip("\n")

    meta = subprocess.run(
        ["security", "find-internet-password", "-s", server],
        capture_output=True, text=True,
    )
    account = None
    for line in meta.stdout.splitlines():
        line = line.strip()
        if line.startswith('"acct"'):
            # Format: "acct"<blob>="the-email@example.com"
            start = line.find('="')
            if start != -1:
                account = line[start + 2:-1]
            break
    return account, password


def login(headless=True):
    account, password = _keychain_lookup(SERVER)
    if not account or not password:
        raise RuntimeError(
            f"No macOS Passwords entry found for {SERVER}. Add one (email and "
            f"password) in the Passwords app first, matching that exact website, "
            f"then try again."
        )

    with sync_playwright() as p:
        b = p.chromium.launch(headless=headless, args=["--disable-blink-features=AutomationControlled"])
        ctx = b.new_context(viewport={"width": 1440, "height": 900})
        try:
            page = ctx.new_page()
            page.goto(LOGIN_URL, wait_until="domcontentloaded", timeout=30000)
            try:
                page.wait_for_selector('input[name="email"]', timeout=15000)
            except Exception:
                raise RuntimeError(
                    "Cazana's login page did not show the expected email field. "
                    "The page may have changed, check it by eye (python3 login.py cazana)."
                )
            # A cookie consent banner shows on each fresh session and can sit over
            # the form. Dismiss it, choosing the privacy preserving option, so it
            # cannot intercept the Continue click. Best effort, harmless if absent.
            for label in ("Reject All", "Reject all", "Decline"):
                try:
                    btn = page.query_selector(f'button:has-text("{label}")')
                    if btn:
                        btn.click(timeout=3000)
                        break
                except Exception:
                    pass
            page.fill('input[name="email"]', account)
            page.fill('input[name="password"]', password)
            password = None  # cleared as soon as it is no longer needed
            # The submit button reads "Continue" and carries no type=submit, so
            # match it by text (fall back to the old selector just in case).
            clicked = False
            for sel in ('button:has-text("Continue")', 'button.is-info', 'button[type="submit"]'):
                try:
                    page.click(sel, timeout=5000)
                    clicked = True
                    break
                except Exception:
                    continue
            if not clicked:
                raise RuntimeError("Could not find the Cazana login submit button.")

            try:
                page.wait_for_selector('input[name="value"]', timeout=20000)
            except Exception:
                # A wrong password, a 2FA prompt, or a changed page all land
                # here. Never guess which, just fail loudly (golden rule 5).
                err = page.query_selector('[class*="error" i], [role="alert"]')
                detail = err.inner_text().strip() if err else "no search box appeared after logging in"
                raise RuntimeError(f"Cazana login did not succeed: {detail}")

            ctx.storage_state(path=browser.state_path("cazana"))
            # A successful login is real, current evidence the site is
            # reachable, worth clearing any earlier real-read failure that
            # would otherwise sit there stale until the next successful
            # read happens to come along (Steven 2026-08-26, caught live:
            # DealerKit showed a red flag with "Logged in within the last
            # day" right next to it, confusing, since login and the
            # site_health flag were never connected before this).
            db.record_site_health("cazana", True, "")
            print("Logged in to Cazana. Session saved.")
        finally:
            ctx.close()
            b.close()


if __name__ == "__main__":
    headless = "--headed" not in sys.argv
    try:
        login(headless=headless)
    except Exception as e:
        print(f"FAILED: {e}")
        sys.exit(1)
