use portable_pty::{CommandBuilder, NativePtySystem, PtySize, PtySystem};
use std::io::{Read, Write};
use std::path::PathBuf;
use std::time::Duration;

const ROWS: u16 = 80;
const COLS: u16 = 120;

struct Args {
    debug: bool,
    raw: bool,
    no_trust: bool,
    workdir: Option<PathBuf>,
    use_cwd: bool,
}

fn parse_args() -> Args {
    let mut args = Args {
        debug: false,
        raw: false,
        no_trust: false,
        workdir: None,
        use_cwd: false,
    };
    let mut iter = std::env::args().skip(1);
    while let Some(arg) = iter.next() {
        match arg.as_str() {
            "--debug" => args.debug = true,
            "--raw" => args.raw = true,
            "--no-trust" => args.no_trust = true,
            "--cwd" => args.use_cwd = true,
            "--workdir" => {
                args.workdir = Some(PathBuf::from(
                    iter.next().expect("--workdir requires a path argument"),
                ));
            }
            other => {
                eprintln!("Unknown argument: {other}");
                eprintln!(
                    "Usage: anth_usage [--debug] [--raw] [--no-trust] [--workdir <path>] [--cwd]"
                );
                std::process::exit(2);
            }
        }
    }
    args
}

fn resolve_workdir(args: &Args) -> PathBuf {
    if let Some(ref dir) = args.workdir {
        return dir.clone();
    }
    if args.use_cwd {
        return std::env::current_dir().expect("failed to get current directory");
    }

    #[cfg(feature = "dirigent-paths")]
    {
        if let Ok(paths) = dirigent_config::DirigentPaths::resolve() {
            let noproject = paths.noproject_home_dir();
            if noproject.exists() {
                return noproject;
            }
        }
    }

    dirs::home_dir().expect("failed to resolve home directory")
}

fn grab_screen(parser: &vt100::Parser) -> String {
    let screen = parser.screen();
    let mut output = String::new();
    for line in screen.rows(0, COLS) {
        output.push_str(&line);
        output.push('\n');
    }
    output
}

macro_rules! debug {
    ($args:expr, $($tt:tt)*) => {
        if $args.debug {
            eprintln!($($tt)*);
        }
    };
}

fn main() {
    let args = parse_args();
    let workdir = resolve_workdir(&args);

    debug!(args, "Working directory: {}", workdir.display());

    let pty_system = NativePtySystem::default();
    let pair = pty_system
        .openpty(PtySize {
            rows: ROWS,
            cols: COLS,
            pixel_width: 0,
            pixel_height: 0,
        })
        .expect("failed to open pty");

    let mut cmd = CommandBuilder::new("claude");
    cmd.cwd(&workdir);
    let mut child = pair.slave.spawn_command(cmd).expect("failed to spawn claude");
    drop(pair.slave);

    let mut writer = pair.master.take_writer().expect("failed to get writer");
    let reader = pair.master.try_clone_reader().expect("failed to get reader");

    let (tx, rx) = std::sync::mpsc::channel();
    std::thread::spawn(move || {
        let mut reader = reader;
        let mut buf = [0u8; 4096];
        loop {
            match reader.read(&mut buf) {
                Ok(0) => break,
                Ok(n) => {
                    let _ = tx.send(buf[..n].to_vec());
                }
                Err(_) => break,
            }
        }
    });

    // Wait for claude to render
    std::thread::sleep(Duration::from_secs(5));

    debug!(
        args,
        "Child alive: {}",
        matches!(child.try_wait(), Ok(None))
    );

    // Grab screen
    let mut parser = vt100::Parser::new(ROWS, COLS, 0);
    while let Ok(data) = rx.try_recv() {
        parser.process(&data);
    }
    let output = grab_screen(&parser);
    debug!(args, "=== SCREEN ===\n{output}=== END ===");

    // Handle trust prompt
    if output.contains("Yes, I trust this folder") {
        if args.no_trust {
            eprintln!("Folder is not trusted: {}", workdir.display());
            eprintln!("Run claude in this folder manually to trust it, or omit --no-trust.");
            let _ = child.kill();
            std::process::exit(1);
        }
        debug!(args, "Sending enter for trust...");
        writer.write_all(b"\r").expect("failed to confirm trust");

        std::thread::sleep(Duration::from_secs(3));

        while let Ok(data) = rx.try_recv() {
            parser.process(&data);
        }
        debug!(
            args,
            "=== AFTER TRUST ===\n{}=== END ===",
            grab_screen(&parser)
        );
    }

    // Send /usage
    debug!(args, "Sending /usage...");
    writer
        .write_all(b"/usage\r")
        .expect("failed to send /usage");

    std::thread::sleep(Duration::from_secs(3));

    while let Ok(data) = rx.try_recv() {
        parser.process(&data);
    }
    let raw_output = grab_screen(&parser);

    let processed = dirigent_anth::anth_usage::process_usage_screen(&raw_output);

    if args.raw {
        println!("{}", processed.raw_screen);
    } else {
        println!(
            "{}",
            serde_json::to_string_pretty(&processed.data).expect("failed to serialize usage data")
        );
    }

    let _ = child.kill();
}
