use crate::AppState;
use axum::Json;
use axum::extract::{Path, Query, State};
use axum::http::StatusCode;
use axum::response::{IntoResponse, Response};
use std::sync::Arc;
#[cfg(feature = "desktop")]
use tauri::Emitter;

use super::types::*;
use super::{err_500, json_result, validate_repo_path};

pub(super) async fn list_worktrees_http(State(state): State<Arc<AppState>>) -> impl IntoResponse {
    let worktrees: Vec<serde_json::Value> = state
        .sessions
        .iter()
        .filter_map(|entry| {
            let session = entry.value().lock();
            session.worktree.as_ref().map(|wt| {
                serde_json::json!({
                    "session_id": entry.key(),
                    "name": wt.name,
                    "path": wt.path.to_string_lossy(),
                    "branch": wt.branch,
                    "base_repo": wt.base_repo.to_string_lossy(),
                })
            })
        })
        .collect();
    Json(worktrees)
}

pub(super) async fn get_worktrees_dir_http(
    State(state): State<Arc<AppState>>,
    Query(q): Query<OptionalRepoQuery>,
) -> impl IntoResponse {
    let dir = match q.repo_path {
        Some(rp) => crate::worktree::resolve_worktree_dir_for_repo(
            std::path::Path::new(&rp),
            &state.worktrees_dir,
        )
        .to_string_lossy()
        .to_string(),
        None => state.worktrees_dir.to_string_lossy().to_string(),
    };
    Json(serde_json::json!({"dir": dir}))
}

pub(super) async fn get_worktree_paths_http(Query(q): Query<PathQuery>) -> Response {
    if let Err(e) = validate_repo_path(&q.path) {
        return e.into_response();
    }
    let path = q.path;
    let result =
        tokio::task::spawn_blocking(move || crate::worktree::get_worktree_paths(path)).await;
    match result {
        Ok(r) => json_result(r),
        Err(e) => err_500(&format!("task panic: {e}")),
    }
}

pub(super) async fn create_worktree_http(
    State(state): State<Arc<AppState>>,
    Json(body): Json<CreateWorktreeRequest>,
) -> impl IntoResponse {
    // Model provides only branch_name and optionally base_ref (start point).
    // Storage path and strategy come entirely from user config via resolve_worktree_dir_for_repo.
    let config = crate::worktree::WorktreeConfig {
        task_name: body.branch_name.clone(),
        base_repo: body.base_repo.clone(),
        branch: Some(body.branch_name),
        create_branch: true, // Always create a new branch — model must not control this
    };
    let worktrees_dir = crate::worktree::resolve_worktree_dir_for_repo(
        std::path::Path::new(&config.base_repo),
        &state.worktrees_dir,
    );
    // Use the stale-recovery wrapper so MCP clients heal automatically when an
    // orphaned worktree directory is sitting where the new one should land.
    // Off-loaded onto spawn_blocking because git worktree add can take seconds.
    let base_ref = body.base_ref.clone();
    let config_bg = config.clone();
    let worktrees_dir_bg = worktrees_dir.clone();
    let result = match tokio::task::spawn_blocking(move || {
        crate::worktree::create_worktree_with_stale_recovery(
            &worktrees_dir_bg,
            &config_bg,
            base_ref.as_deref(),
        )
    })
    .await
    {
        Ok(r) => r,
        Err(e) => {
            return (
                StatusCode::INTERNAL_SERVER_ERROR,
                Json(serde_json::json!({"error": format!("task panic: {e}")})),
            );
        }
    };
    match result {
        Ok(wt) => {
            state.invalidate_repo_caches(&body.base_repo);
            let wt_path = wt.path.to_string_lossy().to_string();
            let branch_name = wt.branch.clone().unwrap_or_default();
            let _ = state
                .event_bus
                .send(crate::state::AppEvent::WorktreeCreated {
                    repo_path: body.base_repo.clone(),
                    branch: branch_name.clone(),
                    worktree_path: wt_path.clone(),
                });
            #[cfg(feature = "desktop")]
            if let Some(handle) = state.app_handle.read().as_ref() {
                let _ = handle.emit(
                    "worktree-created",
                    serde_json::json!({
                        "repo_path": &body.base_repo,
                        "branch": &branch_name,
                        "worktree_path": &wt_path,
                    }),
                );
            }
            let mut response = serde_json::json!({
                "name": wt.name,
                "path": &wt_path,
                "branch": wt.branch,
                "base_repo": wt.base_repo.to_string_lossy(),
            });
            let repo_for_script = body.base_repo.clone();
            let cwd_for_script = wt_path.clone();
            if let Some(script) = tokio::task::spawn_blocking(move || {
                crate::config::resolve_effective_setup_script(&repo_for_script)
            })
            .await
            .ok()
            .flatten()
            {
                match tokio::task::spawn_blocking(move || {
                    crate::worktree::run_setup_script(script, cwd_for_script)
                })
                .await
                {
                    Ok(Ok(result)) => {
                        response["setup_script"] = result;
                    }
                    Ok(Err(e)) => {
                        response["setup_script_error"] = serde_json::json!(e);
                    }
                    Err(e) => {
                        response["setup_script_error"] =
                            serde_json::json!(format!("task panic: {e}"));
                    }
                }
            }
            (StatusCode::CREATED, Json(response))
        }
        Err(e) => (
            StatusCode::INTERNAL_SERVER_ERROR,
            Json(serde_json::json!({"error": e})),
        ),
    }
}

pub(super) async fn remove_worktree_http(
    Path(branch): Path<String>,
    Query(q): Query<RemoveWorktreeQuery>,
) -> Response {
    if let Err(e) = validate_repo_path(&q.repo_path) {
        return e.into_response();
    }
    let repo_path = q.repo_path.clone();
    let delete_branch = q.delete_branch.unwrap_or(true);
    let force = q.force.unwrap_or(false);
    let result = tokio::task::spawn_blocking(move || {
        crate::worktree::remove_worktree_by_branch(&repo_path, &branch, delete_branch, None, force)
    })
    .await;
    match result {
        Ok(Ok(())) => (StatusCode::OK, Json(serde_json::json!({"ok": true}))).into_response(),
        Ok(Err(e)) => err_500(&e),
        Err(e) => err_500(&format!("task panic: {e}")),
    }
}

pub(super) async fn detect_orphan_worktrees_http(Query(q): Query<OptionalRepoQuery>) -> Response {
    let repo_path = match q.repo_path {
        Some(p) => p,
        None => {
            return (
                StatusCode::BAD_REQUEST,
                Json(serde_json::json!({"error": "repoPath required"})),
            )
                .into_response();
        }
    };
    if let Err(e) = validate_repo_path(&repo_path) {
        return e.into_response();
    }
    let result =
        tokio::task::spawn_blocking(move || crate::worktree::detect_orphan_worktrees(repo_path))
            .await;
    match result {
        Ok(r) => json_result(r),
        Err(e) => err_500(&format!("task panic: {e}")),
    }
}

pub(super) async fn remove_orphan_worktree_http(
    State(state): State<Arc<AppState>>,
    Json(body): Json<super::types::RemoveOrphanRequest>,
) -> Response {
    if let Err(e) = validate_repo_path(&body.repo_path) {
        return e.into_response();
    }
    let repo_path = body.repo_path.clone();
    let worktree_path = body.worktree_path.clone();
    let result = tokio::task::spawn_blocking(move || {
        crate::worktree::validate_worktree_path(&repo_path, &worktree_path)?;
        let worktree = crate::state::WorktreeInfo {
            name: std::path::Path::new(&worktree_path)
                .file_name()
                .map(|n| n.to_string_lossy().to_string())
                .unwrap_or_else(|| worktree_path.clone()),
            path: std::path::PathBuf::from(&worktree_path),
            branch: None,
            base_repo: std::path::PathBuf::from(&repo_path),
        };
        crate::worktree::remove_worktree_internal(&worktree, false)
    })
    .await;
    match result {
        Ok(Ok(())) => {
            state.invalidate_repo_caches(&body.repo_path);
            (StatusCode::OK, Json(serde_json::json!({"ok": true}))).into_response()
        }
        Ok(Err(e)) => err_500(&e),
        Err(e) => err_500(&format!("task panic: {e}")),
    }
}

pub(super) async fn generate_worktree_name_http(
    Json(body): Json<GenerateWorktreeNameRequest>,
) -> impl IntoResponse {
    Json(crate::worktree::generate_worktree_name_cmd(
        body.existing_names,
    ))
}

pub(super) async fn list_local_branches_http(Query(q): Query<PathQuery>) -> Response {
    if let Err(e) = validate_repo_path(&q.path) {
        return e.into_response();
    }
    let path = q.path;
    let result =
        tokio::task::spawn_blocking(move || crate::worktree::list_local_branches(path)).await;
    match result {
        Ok(r) => json_result(r),
        Err(e) => err_500(&format!("task panic: {e}")),
    }
}

pub(super) async fn checkout_remote_branch_http(
    State(state): State<Arc<AppState>>,
    Json(body): Json<super::types::CheckoutRemoteRequest>,
) -> Response {
    if let Err(e) = validate_repo_path(&body.repo_path) {
        return e.into_response();
    }
    let repo = std::path::PathBuf::from(&body.repo_path);
    let branch_name = body.branch_name.clone();
    let remote_ref = format!("origin/{}", body.branch_name);
    let result = tokio::task::spawn_blocking(move || {
        crate::git_cli::git_cmd(&repo)
            .args(["checkout", "-b", &branch_name, &remote_ref])
            .run()
            .map_err(|e| e.to_string())
    })
    .await;
    match result {
        Ok(Ok(_)) => {
            state.invalidate_repo_caches(&body.repo_path);
            (StatusCode::OK, Json(serde_json::json!(null))).into_response()
        }
        Ok(Err(e)) => err_500(&e),
        Err(e) => err_500(&format!("task panic: {e}")),
    }
}

pub(super) async fn merge_pr_via_github_http(
    State(state): State<Arc<AppState>>,
    Json(body): Json<MergePrRequest>,
) -> Response {
    if let Err(e) = validate_repo_path(&body.repo_path) {
        return e.into_response();
    }
    match crate::github::merge_pr_github_impl(
        &body.repo_path,
        body.pr_number,
        &body.merge_method,
        &state,
    )
    .await
    {
        Ok(sha) => (StatusCode::OK, Json(serde_json::json!({"sha": sha}))).into_response(),
        Err(e) => err_500(&e),
    }
}

pub(super) async fn finalize_merged_worktree_http(
    State(state): State<Arc<AppState>>,
    Json(body): Json<FinalizeMergeRequest>,
) -> Response {
    if let Err(e) = validate_repo_path(&body.repo_path) {
        return e.into_response();
    }
    let repo_path = body.repo_path.clone();
    let branch_name = body.branch_name.clone();
    let action = body.action.clone();
    let result = match tokio::task::spawn_blocking(move || {
        let base_repo = std::path::PathBuf::from(&repo_path);
        match action.as_str() {
            "archive" => crate::worktree::archive_worktree(&base_repo, &branch_name, None).map(
                |ap| serde_json::json!({"merged": true, "action": "archived", "archive_path": ap}),
            ),
            "delete" => crate::worktree::remove_worktree_by_branch(
                &repo_path,
                &branch_name,
                true,
                None,
                false,
            )
            .map(
                |_| serde_json::json!({"merged": true, "action": "deleted", "archive_path": null}),
            ),
            other => Err(format!(
                "Unknown action '{other}': expected 'archive' or 'delete'"
            )),
        }
    })
    .await
    {
        Ok(r) => r,
        Err(e) => Err(format!("task panic: {e}")),
    };
    let repo_path = body.repo_path.clone();
    match result {
        Ok(json) => {
            state.invalidate_repo_caches(&repo_path);
            (StatusCode::OK, Json(json)).into_response()
        }
        Err(e) => err_500(&e),
    }
}
