#!/usr/bin/env python3
"""
GitHub OAuth Device Flow Authentication for ThrillWiki CI/CD
This script implements GitHub's device flow to securely obtain access tokens.
"""

import sys
import time
import requests
import argparse
from pathlib import Path

# GitHub OAuth App Configuration
CLIENT_ID = "Iv23liOX5Hp75AxhUvIe"
TOKEN_FILE = ".github-token"


def parse_response(response):
    """Parse HTTP response and handle errors."""
    if response.status_code in [200, 201]:
        return response.json()
    elif response.status_code == 401:
        print("You are not authorized. Run the `login` command.")
        sys.exit(1)
    else:
        print(f"HTTP {response.status_code}: {response.text}")
        sys.exit(1)


def request_device_code():
    """Request a device code from GitHub."""
    url = "https://github.com/login/device/code"
    data = {"client_id": CLIENT_ID}
    headers = {"Accept": "application/json"}

    response = requests.post(url, data=data, headers=headers)
    return parse_response(response)


def request_token(device_code):
    """Request an access token using the device code."""
    url = "https://github.com/login/oauth/access_token"
    data = {
        "client_id": CLIENT_ID,
        "device_code": device_code,
        "grant_type": "urn:ietf:params:oauth:grant-type:device_code",
    }
    headers = {"Accept": "application/json"}

    response = requests.post(url, data=data, headers=headers)
    return parse_response(response)


def poll_for_token(device_code, interval):
    """Poll GitHub for the access token after user authorization."""
    print("Waiting for authorization...")

    while True:
        response = request_token(device_code)
        error = response.get("error")
        access_token = response.get("access_token")

        if error:
            if error == "authorization_pending":
                # User hasn't entered the code yet
                print(".", end="", flush=True)
                time.sleep(interval)
                continue
            elif error == "slow_down":
                # Polling too fast
                time.sleep(interval + 5)
                continue
            elif error == "expired_token":
                print("\nThe device code has expired. Please run `login` again.")
                sys.exit(1)
            elif error == "access_denied":
                print("\nLogin cancelled by user.")
                sys.exit(1)
            else:
                print(f"\nError: {response}")
                sys.exit(1)

        # Success! Save the token
        token_path = Path(TOKEN_FILE)
        token_path.write_text(access_token)
        token_path.chmod(0o600)  # Read/write for owner only

        print(f"\nToken saved to {TOKEN_FILE}")
        break


def login():
    """Initiate the GitHub OAuth device flow login process."""
    print("Starting GitHub authentication...")

    device_response = request_device_code()
    verification_uri = device_response["verification_uri"]
    user_code = device_response["user_code"]
    device_code = device_response["device_code"]
    interval = device_response["interval"]

    print(f"\nPlease visit: {verification_uri}")
    print(f"and enter code: {user_code}")
    print("\nWaiting for you to complete authorization in your browser...")

    poll_for_token(device_code, interval)
    print("Successfully authenticated!")
    return True


def whoami():
    """Display information about the authenticated user."""
    token_path = Path(TOKEN_FILE)

    if not token_path.exists():
        print("You are not authorized. Run the `login` command.")
        sys.exit(1)

    try:
        token = token_path.read_text().strip()
    except Exception as e:
        print(f"Error reading token: {e}")
        print("You may need to run the `login` command again.")
        sys.exit(1)

    url = "https://api.github.com/user"
    headers = {
        "Accept": "application/vnd.github+json",
        "Authorization": f"Bearer {token}",
    }

    response = requests.get(url, headers=headers)
    user_data = parse_response(response)

    print(f"You are authenticated as: {user_data['login']}")
    print(f"Name: {user_data.get('name', 'Not set')}")
    print(f"Email: {user_data.get('email', 'Not public')}")

    return user_data


def get_token():
    """Get the current access token if available."""
    token_path = Path(TOKEN_FILE)

    if not token_path.exists():
        return None

    try:
        return token_path.read_text().strip()
    except Exception:
        return None


def validate_token():
    """Validate that the current token is still valid."""
    token = get_token()
    if not token:
        return False

    url = "https://api.github.com/user"
    headers = {
        "Accept": "application/vnd.github+json",
        "Authorization": f"Bearer {token}",
    }

    try:
        response = requests.get(url, headers=headers)
        return response.status_code == 200
    except Exception:
        return False


def ensure_authenticated():
    """Ensure user is authenticated, prompting login if necessary."""
    if validate_token():
        return get_token()

    print("GitHub authentication required.")
    login()
    return get_token()


def logout():
    """Remove the stored access token."""
    token_path = Path(TOKEN_FILE)

    if token_path.exists():
        token_path.unlink()
        print("Successfully logged out.")
    else:
        print("You are not currently logged in.")


def main():
    """Main CLI interface."""
    parser = argparse.ArgumentParser(
        description="GitHub OAuth authentication for ThrillWiki CI/CD"
    )
    parser.add_argument(
        "command",
        choices=["login", "logout", "whoami", "token", "validate"],
        help="Command to execute",
    )

    if len(sys.argv) == 1:
        parser.print_help()
        sys.exit(1)

    args = parser.parse_args()

    if args.command == "login":
        login()
    elif args.command == "logout":
        logout()
    elif args.command == "whoami":
        whoami()
    elif args.command == "token":
        token = get_token()
        if token:
            print(token)
        else:
            print("No token available. Run `login` first.")
            sys.exit(1)
    elif args.command == "validate":
        if validate_token():
            print("Token is valid.")
        else:
            print("Token is invalid or missing.")
            sys.exit(1)


if __name__ == "__main__":
    main()
