#!/usr/bin/env python3
"""
Automated LE Capital login using the credential saved in the macOS login
keychain, the same pattern as auto_login_cazana.py, auto_login_carwow.py,
auto_login_motorway.py and auto_login_dealerkit.py (Mark 2026-08-27: "update
DA, A4C, LE to use the auto login that DK, Cazana & MW & Carwow use").

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, so it never lives in this repo or anywhere Claude (or
anyone reading this code) can see it.

Like Carwow, DealerKit and Auction4Cars, LE Capital's own session does not
survive a fresh process launching its own Chrome, so this logs in through a
fresh throwaway context and saves the result to data/lecapital_state.json,
the same file every other LE Capital read (funding_sync.py, lecapital.py)
already uses via bidbrain.browser.open_reader_context. Note that daily_run.
lecapital_funding_pass() drives LE Capital AND DealerKit from one shared,
continuous browser context (a real business reason, the two are usually
worked one after the other by a person), this script is not that, it only
ever logs in to LE Capital on its own.

The real "logged in" signal reused here (off /auth/login, "Displaying all"
somewhere in the page's own text) is the same one daily_run.
lecapital_funding_pass() already relies on, not a new one invented for this
script; LE Capital gives no simpler DOM hook.

LE Capital's own login page is a plain username/password form (checked live
2026-08-27, no CAPTCHA visible), same shape as Carwow's, Motorway's and
DealerKit's; if a CAPTCHA or some other block ever does appear, this fails
loudly rather than guessing.

One time setup, done by Mark, not by this script:
    python3 setup_credential.py lecapital
  The first time this script runs, macOS may prompt for permission to read
  that keychain item. Choose "Always Allow" so future scheduled runs do not
  need anyone at the keyboard.

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

This only ever logs in. It never reads or syncs funding data.
"""

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

BASE_URL = browser.SITES["lecapital"]["base_url"]
SERVER = "stocktrak.lecapital.co.uk"
LOGIN_URL = browser.SITES["lecapital"]["login_url"]
CHECK_URL = f"{BASE_URL}/stock/current-stock"


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"'):
            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 saved login found for {SERVER}. Run: "
            f"python3 setup_credential.py lecapital, 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("#username", timeout=15000)
            except Exception:
                raise RuntimeError(
                    "LE Capital's login page did not show the expected username field. "
                    "The page may have changed, check it by eye (python3 login.py lecapital manual)."
                )
            page.fill("#username", account)
            page.fill("#Password", password)
            password = None  # cleared as soon as it is no longer needed
            try:
                page.click('button.de-auth-primary-button:has-text("Login")', timeout=5000)
            except Exception:
                raise RuntimeError("Could not find the LE Capital login submit button.")

            # Same real signal daily_run.lecapital_funding_pass() already
            # relies on: off the login page and the real stock list's own
            # "Displaying all" text, LE Capital gives no simpler DOM hook.
            logged_in = False
            waited = 0
            while waited < 20000:
                page.wait_for_timeout(500)
                waited += 500
                try:
                    if "/auth/login" not in page.url and "Displaying all" in page.inner_text("body"):
                        logged_in = True
                        break
                except Exception:
                    continue
                if waited == 500:
                    try:
                        page.goto(CHECK_URL, wait_until="domcontentloaded", timeout=30000)
                    except Exception:
                        pass
            if not logged_in:
                err = page.query_selector('[class*="error" i], [role="alert"]')
                detail = err.inner_text().strip() if err else (
                    "the real stock list never showed up, a wrong password or "
                    "a captcha/bot check may have blocked it")
                raise RuntimeError(f"LE Capital login did not succeed: {detail}")

            ctx.storage_state(path=browser.state_path("lecapital"))
            db.record_site_health("lecapital", True, "")
            print("Logged in to LE Capital. 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)
