use std::path::PathBuf;

use anyhow::{Context, Result, bail};
use fabro_types::settings::CliSettings;
use fabro_types::settings::cli::{CliLayer, OutputFormat};
use fabro_util::printer::Printer;
use tokio::process::Command as TokioCommand;
use tokio::task::spawn_blocking;

use crate::args::{RepoInitArgs, ServerTargetArgs};
use crate::command_context::CommandContext;

#[expect(
    clippy::disallowed_methods,
    reason = "This is a shared synchronous git helper used by repo deinit; async callers should use spawn_blocking."
)]
pub(super) fn git_repo_root() -> Result<PathBuf> {
    let output = std::process::Command::new("git")
        .args(["rev-parse", "--show-toplevel"])
        .output()
        .context("failed to run git")?;
    if !output.status.success() {
        bail!("not a git repository — run `git init` first");
    }
    Ok(PathBuf::from(
        String::from_utf8(output.stdout)
            .context("git output was not valid UTF-8")?
            .trim(),
    ))
}

pub(crate) async fn run_init(
    args: &RepoInitArgs,
    cli: &CliSettings,
    cli_layer: &CliLayer,
    printer: Printer,
) -> Result<Vec<String>> {
    let repo_root = spawn_blocking(git_repo_root)
        .await
        .context("git repo root task panicked")??;
    let mut created = Vec::new();

    let fabro_dir = repo_root.join(".fabro");
    let project_toml = fabro_dir.join("project.toml");
    if project_toml.exists() {
        bail!(
            "already initialized — .fabro/project.toml exists at {}",
            project_toml.display()
        );
    }

    std::fs::create_dir_all(&fabro_dir)
        .with_context(|| format!("failed to create {}", fabro_dir.display()))?;

    // Create .fabro/project.toml
    std::fs::write(
        &project_toml,
        "\
# Fabro project configuration
# https://docs.fabro.computer/getting-started/quick-start

_version = 1

# Auto-create pull requests on successful workflow runs.
[run.pull_request]
enabled = true
draft = true
# auto_merge = true
",
    )
    .with_context(|| format!("failed to write {}", project_toml.display()))?;
    created.push(".fabro/project.toml".to_string());

    let green = console::Style::new().green();
    let bold = console::Style::new().bold();
    let dim = console::Style::new().dim();
    if cli.output.format != OutputFormat::Json {
        fabro_util::printerr!(
            printer,
            "  {} {}",
            green.apply_to("✔"),
            dim.apply_to(".fabro/project.toml")
        );
    }

    // Create hello workflow directory
    let workflow_dir = repo_root.join(".fabro/workflows/hello");
    std::fs::create_dir_all(&workflow_dir)
        .with_context(|| format!("failed to create {}", workflow_dir.display()))?;

    // Create workflow.fabro
    let dot_path = workflow_dir.join("workflow.fabro");
    std::fs::write(
        &dot_path,
        r#"digraph Hello {
    graph [goal="Say hello and demonstrate a basic Fabro workflow"]
    rankdir=LR

    start [shape=Mdiamond, label="Start"]
    exit  [shape=Msquare, label="Exit"]

    greet [label="Greet", prompt="Say hello! Introduce yourself and explain that this is a test of the Fabro workflow engine."]

    start -> greet -> exit
}
"#,
    )
    .with_context(|| format!("failed to write {}", dot_path.display()))?;
    created.push(".fabro/workflows/hello/workflow.fabro".to_string());
    if cli.output.format != OutputFormat::Json {
        fabro_util::printerr!(
            printer,
            "  {} {}",
            green.apply_to("✔"),
            dim.apply_to(".fabro/workflows/hello/workflow.fabro")
        );
    }

    // Create workflow.toml
    let toml_path = workflow_dir.join("workflow.toml");
    std::fs::write(
        &toml_path,
        "_version = 1\n\n[workflow]\ngraph = \"workflow.fabro\"\n\n[run.sandbox]\nprovider = \"local\"\n",
    )
    .with_context(|| format!("failed to write {}", toml_path.display()))?;
    created.push(".fabro/workflows/hello/workflow.toml".to_string());
    if cli.output.format != OutputFormat::Json {
        fabro_util::printerr!(
            printer,
            "  {} {}",
            green.apply_to("✔"),
            dim.apply_to(".fabro/workflows/hello/workflow.toml")
        );
    }

    if cli.output.format != OutputFormat::Json {
        fabro_util::printerr!(
            printer,
            "\n{} Run a workflow with:\n\n  {}",
            bold.apply_to("Project initialized!"),
            console::Style::new()
                .cyan()
                .bold()
                .apply_to("fabro run hello")
        );
    }

    if cli.output.format != OutputFormat::Json {
        check_github_app_installation(&args.target, cli, cli_layer, printer).await;
    }

    Ok(created)
}

async fn check_github_app_installation(
    target: &ServerTargetArgs,
    cli: &CliSettings,
    cli_layer: &CliLayer,
    printer: Printer,
) {
    // Get the git remote origin URL
    let output = match TokioCommand::new("git")
        .args(["remote", "get-url", "origin"])
        .output()
        .await
    {
        Ok(o) if o.status.success() => o,
        _ => {
            let yellow = console::Style::new().yellow();
            let dim = console::Style::new().dim();
            fabro_util::printerr!(
                printer,
                "\n  {} No git remote found — skipping GitHub check",
                yellow.apply_to("!")
            );
            fabro_util::printerr!(
                printer,
                "  {}",
                dim.apply_to(
                    "Run `git remote add origin <url>`, then `gh auth login` or `fabro install` to configure GitHub access"
                )
            );
            return;
        }
    };

    let remote_url = match String::from_utf8(output.stdout) {
        Ok(s) => s.trim().to_string(),
        Err(_) => return,
    };

    // Convert SSH URL to HTTPS and parse owner/repo
    let https_url = fabro_github::ssh_url_to_https(&remote_url);
    let Ok((owner, repo)) = fabro_github::parse_github_owner_repo(&https_url) else {
        return; // Not a GitHub repo — skip silently
    };

    let ctx = match CommandContext::for_target(target, printer, cli.clone(), cli_layer) {
        Ok(ctx) => ctx,
        Err(err) => {
            fabro_util::printerr!(
                printer,
                "\n  Warning: could not resolve fabro server settings: {err}"
            );
            return;
        }
    };

    let server = match ctx.server().await {
        Ok(server) => server,
        Err(err) => {
            fabro_util::printerr!(
                printer,
                "\n  Warning: could not connect to fabro server: {err}"
            );
            return;
        }
    };

    let check = match server
        .api()
        .get_github_repo()
        .owner(owner.clone())
        .name(repo.clone())
        .send()
        .await
    {
        Ok(response) => response.into_inner(),
        Err(err) => {
            fabro_util::printerr!(printer, "\n  Warning: could not check GitHub access: {err}");
            return;
        }
    };

    if check.accessible {
        let green = console::Style::new().green();
        fabro_util::printerr!(
            printer,
            "\n  {} GitHub access is configured for {owner}/{repo}",
            green.apply_to("✔")
        );
        return;
    }

    let yellow = console::Style::new().yellow();
    fabro_util::printerr!(
        printer,
        "\n  {} GitHub access is not available for {owner}/{repo}",
        yellow.apply_to("!")
    );
    if let Some(url) = &check.install_url {
        fabro_util::printerr!(printer, "  Install at: {url}");
    } else {
        fabro_util::printerr!(
            printer,
            "  Run `gh auth login` or `fabro install`, then try again."
        );
    }

    if std::io::IsTerminal::is_terminal(&std::io::stdin()) {
        fabro_util::printerr!(printer, "  Press Enter to continue after installing...");
        let _ = spawn_blocking(|| {
            let mut buf = String::new();
            let _ = std::io::stdin().read_line(&mut buf);
        })
        .await;

        match server
            .api()
            .get_github_repo()
            .owner(owner.clone())
            .name(repo.clone())
            .send()
            .await
        {
            Ok(response) => {
                let response = response.into_inner();
                if response.accessible {
                    let green = console::Style::new().green();
                    fabro_util::printerr!(
                        printer,
                        "  {} GitHub access is configured for {owner}/{repo}",
                        green.apply_to("✔")
                    );
                } else {
                    fabro_util::printerr!(printer, "  GitHub access is still unavailable.");
                    if let Some(url) = &check.install_url {
                        fabro_util::printerr!(printer, "  Install at: {url}");
                    }
                }
            }
            Err(err) => {
                fabro_util::printerr!(
                    printer,
                    "  Warning: could not re-check GitHub access: {err}"
                );
            }
        }
    }
}
