"""Log into a web app on a schedule and confirm it still works.

A synthetic check: drive a real browser through the login flow, assert the app
reached the signed-in state, and capture a screenshot as evidence. Useful as a
cron job against your own staging or production app.

Fixes over the naive version:

  1. The target was https://herokuapp.com, which is Heroku's marketing site and
     has no login form. The public practice app is the-internet.herokuapp.com/login.
  2. time.sleep(3) is a guess. On a slow morning it is too short and the check
     fails for no reason; the rest of the time it is wasted. Explicit waits watch
     for the condition instead.
  3. Credentials were in the source. They come from the environment here.
  4. The exit code now reflects the result, so cron can alert on failure.

    export LOGIN_USER='tomsmith'
    export LOGIN_PASSWORD='SuperSecretPassword!'
    python3 login_check.py --headless

Requires: selenium, and a matching chromedriver on PATH
"""

from __future__ import annotations

import argparse
import os
import sys
from pathlib import Path

from selenium import webdriver
from selenium.common.exceptions import TimeoutException, WebDriverException
from selenium.webdriver.chrome.options import Options
from selenium.webdriver.common.by import By
from selenium.webdriver.support import expected_conditions as EC
from selenium.webdriver.support.ui import WebDriverWait

DEFAULT_URL = "https://the-internet.herokuapp.com/login"
DEFAULT_SUCCESS_TEXT = "You logged into a secure area!"


def build_driver(headless: bool) -> webdriver.Chrome:
    options = Options()
    if headless:
        options.add_argument("--headless=new")
    options.add_argument("--window-size=1440,900")
    options.add_argument("--disable-gpu")
    return webdriver.Chrome(options=options)


def run_check(args: argparse.Namespace, user: str, password: str) -> bool:
    driver = None
    try:
        driver = build_driver(args.headless)
        wait = WebDriverWait(driver, args.timeout)

        driver.get(args.url)

        # Wait for the field to exist rather than assuming the page has painted.
        username_input = wait.until(EC.presence_of_element_located((By.ID, args.user_field)))
        password_input = driver.find_element(By.ID, args.password_field)

        username_input.send_keys(user)
        password_input.send_keys(password)
        driver.find_element(By.CSS_SELECTOR, args.submit).click()

        # Wait for the signed-in signal, not for a fixed number of seconds.
        wait.until(EC.text_to_be_present_in_element((By.TAG_NAME, "body"), args.expect))

        if args.screenshot:
            driver.save_screenshot(str(args.screenshot))
            print(f"Screenshot saved to {args.screenshot}")

        print("Login check passed.")
        return True

    except TimeoutException:
        print(f"FAILED: did not see {args.expect!r} within {args.timeout}s", file=sys.stderr)
        if driver and args.screenshot:
            driver.save_screenshot(str(args.screenshot))
            print(f"Failure screenshot saved to {args.screenshot}", file=sys.stderr)
        return False
    except WebDriverException as exc:
        print(f"FAILED: browser error: {exc.msg or exc}", file=sys.stderr)
        return False
    finally:
        # Always quit, or a failed run leaks a browser process every night.
        if driver:
            driver.quit()


def main(argv: list[str] | None = None) -> int:
    parser = argparse.ArgumentParser(description="Synthetic login check.")
    parser.add_argument("--url", default=DEFAULT_URL)
    parser.add_argument("--user-field", default="username", help="id of the username input")
    parser.add_argument("--password-field", default="password", help="id of the password input")
    parser.add_argument("--submit", default="button[type='submit']", help="CSS selector for submit")
    parser.add_argument("--expect", default=DEFAULT_SUCCESS_TEXT,
                        help="Text that must appear once signed in")
    parser.add_argument("--timeout", type=int, default=15)
    parser.add_argument("--headless", action="store_true")
    parser.add_argument("--screenshot", type=Path, help="Save a PNG of the final state")
    args = parser.parse_args(argv)

    user = os.getenv("LOGIN_USER")
    password = os.getenv("LOGIN_PASSWORD")
    if not user or not password:
        print("Set LOGIN_USER and LOGIN_PASSWORD in the environment.", file=sys.stderr)
        return 2

    return 0 if run_check(args, user, password) else 1


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