//! Porta CLI - The gate to LLM providers
//!
//! Usage:
//!   cargo run --bin porta
//!
//! This will:
//! 1. Check for existing tokens in ~/.porta/tokens.json
//! 2. If none, run the OAuth flow (opens browser)
//! 3. Let you send a test message to Claude

use porta::anthropic::{AnthropicClient, AnthropicOAuth, AuthMode, OAuthTokens};
use std::fs;
use std::io::{self, BufRead, Write};
use std::path::PathBuf;

fn get_tokens_path() -> PathBuf {
    let home = dirs::home_dir().expect("Could not find home directory");
    home.join(".porta").join("tokens.json")
}

fn load_tokens() -> Option<OAuthTokens> {
    let path = get_tokens_path();
    if path.exists() {
        let content = fs::read_to_string(&path).ok()?;
        serde_json::from_str(&content).ok()
    } else {
        None
    }
}

fn save_tokens(tokens: &OAuthTokens) -> io::Result<()> {
    let path = get_tokens_path();
    if let Some(parent) = path.parent() {
        fs::create_dir_all(parent)?;
    }
    let content = serde_json::to_string_pretty(tokens)?;
    fs::write(&path, content)?;
    println!("Tokens saved to {:?}", path);
    Ok(())
}

async fn run_oauth_flow() -> Result<OAuthTokens, Box<dyn std::error::Error>> {
    let oauth = AnthropicOAuth::new();

    println!("\n=== Anthropic OAuth Flow ===\n");
    println!("Select authentication mode:");
    println!("  1. Claude Pro/Max (use your subscription)");
    println!("  2. Console (create API key)");
    print!("\nChoice [1]: ");
    io::stdout().flush()?;

    let stdin = io::stdin();
    let mut choice = String::new();
    stdin.lock().read_line(&mut choice)?;
    let choice = choice.trim();

    let mode = if choice == "2" {
        AuthMode::Console
    } else {
        AuthMode::Max
    };

    let (url, verifier) = oauth.authorize_url(mode)?;

    println!("\n1. Open this URL in your browser:\n");
    println!("   {}\n", url);

    // Try to open browser automatically on macOS
    #[cfg(target_os = "macos")]
    {
        let _ = std::process::Command::new("open").arg(&url).spawn();
        println!("   (Attempted to open browser automatically)\n");
    }

    println!("2. After authorizing, you'll get a code. Paste it here:");
    print!("\nCode: ");
    io::stdout().flush()?;

    let mut code = String::new();
    stdin.lock().read_line(&mut code)?;
    let code = code.trim();

    println!("\nExchanging code for tokens...");
    let tokens = oauth.exchange(code, &verifier).await?;

    println!("Success! Got access token.");
    save_tokens(&tokens)?;

    Ok(tokens)
}

async fn chat_loop(client: &AnthropicClient) -> Result<(), Box<dyn std::error::Error>> {
    let stdin = io::stdin();

    println!("\n=== Chat with Claude ===");
    println!("Type your message and press Enter. Type 'quit' to exit.\n");

    loop {
        print!("You: ");
        io::stdout().flush()?;

        let mut input = String::new();
        stdin.lock().read_line(&mut input)?;
        let input = input.trim();

        if input.eq_ignore_ascii_case("quit") || input.eq_ignore_ascii_case("exit") {
            println!("Goodbye!");
            break;
        }

        if input.is_empty() {
            continue;
        }

        let body = serde_json::json!({
            "model": "claude-sonnet-4-20250514",
            "max_tokens": 1024,
            "messages": [
                {
                    "role": "user",
                    "content": input
                }
            ]
        });

        print!("Claude: ");
        io::stdout().flush()?;

        match client.messages(&body.to_string()).await {
            Ok(response) => {
                // Parse response to extract text
                if let Ok(parsed) = serde_json::from_str::<serde_json::Value>(&response) {
                    if let Some(content) = parsed.get("content").and_then(|c| c.as_array()) {
                        for block in content {
                            if block.get("type").and_then(|t| t.as_str()) == Some("text") {
                                if let Some(text) = block.get("text").and_then(|t| t.as_str()) {
                                    println!("{}", text);
                                }
                            }
                        }
                    } else if let Some(error) = parsed.get("error") {
                        println!("Error: {}", error);
                    } else {
                        println!("{}", response);
                    }
                } else {
                    println!("{}", response);
                }
            }
            Err(e) => {
                println!("Error: {}", e);
            }
        }
        println!();
    }

    Ok(())
}

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    println!("Porta - The Gate to LLM Providers\n");

    // Check for existing tokens
    let tokens = if let Some(tokens) = load_tokens() {
        println!("Found existing tokens at {:?}", get_tokens_path());

        if tokens.is_expired() {
            println!("Tokens expired, refreshing...");
            let oauth = AnthropicOAuth::new();
            match oauth.refresh(&tokens.refresh_token).await {
                Ok(new_tokens) => {
                    save_tokens(&new_tokens)?;
                    new_tokens
                }
                Err(e) => {
                    println!("Refresh failed: {}. Running OAuth flow...", e);
                    run_oauth_flow().await?
                }
            }
        } else {
            println!("Tokens valid.");
            tokens
        }
    } else {
        println!("No existing tokens found.");
        run_oauth_flow().await?
    };

    // Create client and start chat
    let client = AnthropicClient::new(tokens);
    chat_loop(&client).await?;

    // Save potentially refreshed tokens
    save_tokens(&client.tokens().await)?;

    Ok(())
}
