use clap::{Args, Parser, Subcommand, ValueEnum};
use core_protocol::{InvocationKind, ToolExecutionMode};
use mnote_cli::{
    editor_ai_pipeline, editor_markdown_roundtrip, editor_session_demo, execute_output,
    plan_block_embed, plan_block_insert, plan_block_move, plan_block_patch, plan_mindmap_get,
    plan_mindmap_op, plan_mindmap_put, plan_page_create, plan_page_delete, plan_page_get,
    plan_page_move, plan_page_restore, plan_page_save, plan_page_title, plan_search_blocks,
    plan_search_documents, plan_sidebar_dataset, plan_tool_run, render_editor_plain_output,
    render_plain_output, BlockEmbedArgs, BlockMoveArgs, CliContext, CliError, CliJsonOutput,
    EditorAiScenarioArg, EditorCliOutput, EditorInputKindArg, PageCreateArgs, PageDeleteArgs,
    PageMoveArgs, PageRestoreArgs,
};

#[derive(Parser, Debug)]
#[command(name = "mnote-cli")]
#[command(about = "Phase 8 最小真实执行 CLI", long_about = None)]
struct Cli {
    #[command(flatten)]
    global: GlobalArgs,

    #[command(subcommand)]
    command: Commands,
}

#[derive(Args, Debug, Clone)]
struct GlobalArgs {
    #[arg(long)]
    json: bool,

    #[arg(long)]
    execute: bool,

    #[arg(long, default_value = "cli_user")]
    actor_id: String,

    #[arg(long, default_value = "human")]
    actor_type: String,

    #[arg(long)]
    session_id: Option<String>,

    #[arg(long)]
    reason: Option<String>,

    #[arg(long)]
    idempotency_key: Option<String>,

    #[arg(long)]
    validate_only: bool,

    #[arg(long)]
    dry_run: bool,
}

#[derive(Subcommand, Debug)]
enum Commands {
    Page(PageCommand),
    Block(BlockCommand),
    Mindmap(MindmapCommand),
    Search(SearchCommand),
    Sidebar(SidebarCommand),
    Editor(EditorCliCommand),
    Tool(ToolCommand),
}

#[derive(Args, Debug)]
struct PageCommand {
    #[command(subcommand)]
    action: PageAction,
}

#[derive(Subcommand, Debug)]
enum PageAction {
    Get(PageGetArgs),
    Title(PageTitleArgs),
    Save(PageSaveArgs),
    Create(PageCreateCliArgs),
    Move(PageMoveCliArgs),
    Delete(PageDeleteCliArgs),
    Restore(PageRestoreCliArgs),
}

#[derive(Args, Debug)]
struct PageGetArgs {
    #[arg(long)]
    page_id: String,

    #[arg(long)]
    workspace_id: Option<String>,
}

#[derive(Args, Debug)]
struct PageTitleArgs {
    #[arg(long)]
    page_id: String,

    #[arg(long)]
    workspace_id: Option<String>,

    #[arg(long)]
    title: String,
}

#[derive(Args, Debug)]
struct PageSaveArgs {
    #[arg(long)]
    page_id: String,

    #[arg(long)]
    workspace_id: Option<String>,

    #[arg(long)]
    revision: Option<u64>,

    #[arg(long)]
    content_json: String,

    #[arg(long)]
    conflict_detection_key: Option<String>,
}

#[derive(Args, Debug)]
struct PageCreateCliArgs {
    #[arg(long)]
    page_id: String,

    #[arg(long)]
    workspace_id: String,

    #[arg(long)]
    parent_id: Option<String>,

    #[arg(long)]
    title: String,

    #[arg(long, default_value = "private")]
    access_scope: String,

    #[arg(long, default_value = "[]")]
    content_json: String,
}

#[derive(Args, Debug)]
struct PageMoveCliArgs {
    #[arg(long)]
    page_id: String,

    #[arg(long)]
    workspace_id: Option<String>,

    #[arg(long)]
    parent_id: Option<String>,

    #[arg(long)]
    sort_order: i64,
}

#[derive(Args, Debug)]
struct PageDeleteCliArgs {
    #[arg(long)]
    page_id: String,

    #[arg(long)]
    workspace_id: Option<String>,
}

#[derive(Args, Debug)]
struct PageRestoreCliArgs {
    #[arg(long)]
    page_id: String,

    #[arg(long)]
    workspace_id: Option<String>,
}

#[derive(Args, Debug)]
struct BlockCommand {
    #[command(subcommand)]
    action: BlockAction,
}

#[derive(Subcommand, Debug)]
enum BlockAction {
    Insert(BlockInsertArgs),
    Patch(BlockPatchArgs),
    Move(BlockMoveCliArgs),
    Embed(BlockEmbedCliArgs),
}

#[derive(Args, Debug)]
struct BlockInsertArgs {
    #[arg(long)]
    workspace_id: String,

    #[arg(long)]
    page_id: String,

    #[arg(long)]
    content: String,

    #[arg(long, default_value = "paragraph")]
    block_type: String,

    #[arg(long)]
    parent_block_id: Option<String>,

    #[arg(long)]
    prev_block_id: Option<String>,
}

#[derive(Args, Debug)]
struct BlockPatchArgs {
    #[arg(long)]
    page_id: String,

    #[arg(long)]
    block_id: String,

    #[arg(long)]
    workspace_id: Option<String>,

    #[arg(long)]
    revision: Option<u64>,

    #[arg(long)]
    snapshot_json: String,

    #[arg(long)]
    conflict_detection_key: Option<String>,
}

#[derive(Args, Debug)]
struct BlockMoveCliArgs {
    #[arg(long)]
    source_document_id: String,

    #[arg(long)]
    block_id: String,

    #[arg(long)]
    target_document_id: String,
}

#[derive(Args, Debug)]
struct BlockEmbedCliArgs {
    #[arg(long)]
    source_document_id: String,

    #[arg(long)]
    block_id: String,

    #[arg(long)]
    target_document_id: String,

    #[arg(long)]
    target_block_id: Option<String>,
}

#[derive(Args, Debug)]
struct MindmapCommand {
    #[command(subcommand)]
    action: MindmapAction,
}

#[derive(Subcommand, Debug)]
enum MindmapAction {
    Get(MindmapGetArgs),
    Put(MindmapPutArgs),
    Op(MindmapOpArgs),
}

#[derive(Args, Debug)]
struct MindmapGetArgs {
    #[arg(long)]
    document_id: String,

    #[arg(long)]
    mindmap_id: String,

    #[arg(long)]
    workspace_id: Option<String>,
}

#[derive(Args, Debug)]
struct MindmapPutArgs {
    #[arg(long)]
    document_id: String,

    #[arg(long)]
    mindmap_id: String,

    #[arg(long)]
    workspace_id: Option<String>,

    #[arg(long)]
    data_json: String,

    #[arg(long, default_value_t = false)]
    create_only: bool,
}

#[derive(Args, Debug)]
struct MindmapOpArgs {
    #[arg(long)]
    document_id: String,

    #[arg(long)]
    mindmap_id: String,

    #[arg(long)]
    workspace_id: Option<String>,

    #[arg(long)]
    ops_json: String,
}

#[derive(Args, Debug)]
struct SearchCommand {
    #[command(subcommand)]
    action: SearchAction,
}

#[derive(Subcommand, Debug)]
enum SearchAction {
    Documents(SearchDocumentsArgs),
    Blocks(SearchBlocksArgs),
}

#[derive(Args, Debug)]
struct SearchDocumentsArgs {
    #[arg(long)]
    query: String,

    #[arg(long)]
    workspace_id: Option<String>,

    #[arg(long, default_value_t = 20)]
    limit: u32,

    #[arg(long)]
    cursor: Option<String>,
}

#[derive(Args, Debug)]
struct SearchBlocksArgs {
    #[arg(long)]
    query: String,

    #[arg(long)]
    page_id: Option<String>,

    #[arg(long, default_value_t = 20)]
    limit: u32,

    #[arg(long)]
    cursor: Option<String>,
}

#[derive(Args, Debug)]
struct SidebarCommand {
    #[command(subcommand)]
    action: SidebarAction,
}

#[derive(Subcommand, Debug)]
enum SidebarAction {
    Dataset(SidebarDatasetArgs),
}

#[derive(Args, Debug)]
struct SidebarDatasetArgs {
    #[arg(long)]
    workspace_id: String,
}

#[derive(Args, Debug)]
struct EditorCliCommand {
    #[command(subcommand)]
    action: EditorAction,
}

#[derive(Subcommand, Debug)]
enum EditorAction {
    MarkdownRoundtrip(EditorMarkdownRoundtripArgs),
    SessionDemo(EditorSessionDemoArgs),
    AiPipeline(EditorAiPipelineArgs),
}

#[derive(Args, Debug)]
struct EditorMarkdownRoundtripArgs {
    #[arg(long)]
    markdown: String,
}

#[derive(Args, Debug)]
struct EditorSessionDemoArgs {
    #[arg(long)]
    markdown: String,
}

#[derive(Args, Debug)]
struct EditorAiPipelineArgs {
    #[arg(long, value_enum, default_value_t = EditorInputKindArg::PlainText)]
    kind: EditorInputKindArg,

    #[arg(long, value_enum, default_value_t = EditorAiScenarioArg::MeetingNotesToTodos)]
    scenario: EditorAiScenarioArg,

    #[arg(long)]
    input: String,
}

#[derive(Args, Debug)]
struct ToolCommand {
    #[command(subcommand)]
    action: ToolAction,
}

#[derive(Subcommand, Debug)]
enum ToolAction {
    Run(ToolRunArgs),
}

#[derive(Args, Debug)]
struct ToolRunArgs {
    #[arg(long)]
    tool_name: String,

    #[arg(long, value_enum, default_value_t = ToolKindArg::Command)]
    kind: ToolKindArg,

    #[arg(long, value_enum, default_value_t = ToolModeArg::Plan)]
    mode: ToolModeArg,

    #[arg(long)]
    args_json: String,
}

#[derive(Copy, Clone, Debug, Eq, PartialEq, ValueEnum)]
enum ToolKindArg {
    Command,
    Query,
    Job,
}

#[derive(Copy, Clone, Debug, Eq, PartialEq, ValueEnum)]
enum ToolModeArg {
    Plan,
    Result,
    ExplainPlan,
}

fn main() {
    let cli = Cli::parse();
    let context = CliContext {
        actor_id: cli.global.actor_id,
        actor_type: cli.global.actor_type,
        session_id: cli.global.session_id,
        reason: cli.global.reason,
        idempotency_key: cli.global.idempotency_key,
        validate_only: cli.global.validate_only,
        dry_run: cli.global.dry_run,
    };

    enum CommandOutcome {
        Bridge(CliJsonOutput),
        Editor(EditorCliOutput),
    }

    let result = match cli.command {
        Commands::Page(command) => match command.action {
            PageAction::Get(args) => {
                plan_page_get(&context, &args.page_id, args.workspace_id.as_deref())
            }
            PageAction::Title(args) => plan_page_title(
                &context,
                &args.page_id,
                args.workspace_id.as_deref(),
                &args.title,
            ),
            PageAction::Save(args) => plan_page_save(
                &context,
                &args.page_id,
                args.workspace_id.as_deref(),
                args.revision,
                &args.content_json,
                args.conflict_detection_key.as_deref(),
            ),
            PageAction::Create(args) => plan_page_create(
                &context,
                &PageCreateArgs {
                    page_id: &args.page_id,
                    workspace_id: &args.workspace_id,
                    parent_id: args.parent_id.as_deref(),
                    title: &args.title,
                    access_scope: &args.access_scope,
                    content_json: &args.content_json,
                },
            ),
            PageAction::Move(args) => plan_page_move(
                &context,
                &PageMoveArgs {
                    page_id: &args.page_id,
                    workspace_id: args.workspace_id.as_deref(),
                    parent_id: args.parent_id.as_deref(),
                    sort_order: args.sort_order,
                },
            ),
            PageAction::Delete(args) => plan_page_delete(
                &context,
                &PageDeleteArgs {
                    page_id: &args.page_id,
                    workspace_id: args.workspace_id.as_deref(),
                },
            ),
            PageAction::Restore(args) => plan_page_restore(
                &context,
                &PageRestoreArgs {
                    page_id: &args.page_id,
                    workspace_id: args.workspace_id.as_deref(),
                },
            ),
        }
        .map(CommandOutcome::Bridge),
        Commands::Block(command) => match command.action {
            BlockAction::Insert(args) => plan_block_insert(
                &context,
                &args.workspace_id,
                &args.page_id,
                &args.content,
                &args.block_type,
                args.parent_block_id.as_deref(),
                args.prev_block_id.as_deref(),
            ),
            BlockAction::Patch(args) => plan_block_patch(
                &context,
                args.workspace_id.as_deref(),
                &args.page_id,
                &args.block_id,
                args.revision,
                &args.snapshot_json,
                args.conflict_detection_key.as_deref(),
            ),
            BlockAction::Move(args) => plan_block_move(
                &context,
                &BlockMoveArgs {
                    source_document_id: &args.source_document_id,
                    block_id: &args.block_id,
                    target_document_id: &args.target_document_id,
                },
            ),
            BlockAction::Embed(args) => plan_block_embed(
                &context,
                &BlockEmbedArgs {
                    source_document_id: &args.source_document_id,
                    block_id: &args.block_id,
                    target_document_id: &args.target_document_id,
                    target_block_id: args.target_block_id.as_deref(),
                },
            ),
        }
        .map(CommandOutcome::Bridge),
        Commands::Mindmap(command) => match command.action {
            MindmapAction::Get(args) => plan_mindmap_get(
                &context,
                args.workspace_id.as_deref(),
                &args.document_id,
                &args.mindmap_id,
            ),
            MindmapAction::Put(args) => plan_mindmap_put(
                &context,
                args.workspace_id.as_deref(),
                &args.document_id,
                &args.mindmap_id,
                &args.data_json,
                args.create_only,
            ),
            MindmapAction::Op(args) => plan_mindmap_op(
                &context,
                args.workspace_id.as_deref(),
                &args.document_id,
                &args.mindmap_id,
                &args.ops_json,
            ),
        }
        .map(CommandOutcome::Bridge),
        Commands::Search(command) => match command.action {
            SearchAction::Documents(args) => plan_search_documents(
                &context,
                &args.query,
                args.workspace_id.as_deref(),
                args.limit,
                args.cursor.as_deref(),
            ),
            SearchAction::Blocks(args) => plan_search_blocks(
                &context,
                &args.query,
                args.page_id.as_deref(),
                args.limit,
                args.cursor.as_deref(),
            ),
        }
        .map(CommandOutcome::Bridge),
        Commands::Sidebar(command) => match command.action {
            SidebarAction::Dataset(args) => plan_sidebar_dataset(&context, &args.workspace_id),
        }
        .map(CommandOutcome::Bridge),
        Commands::Editor(command) => match command.action {
            EditorAction::MarkdownRoundtrip(args) => editor_markdown_roundtrip(&args.markdown),
            EditorAction::SessionDemo(args) => editor_session_demo(&args.markdown),
            EditorAction::AiPipeline(args) => editor_ai_pipeline(
                to_editor_input_kind(args.kind),
                to_editor_ai_scenario(args.scenario),
                &args.input,
            ),
        }
        .map(CommandOutcome::Editor),
        Commands::Tool(command) => match command.action {
            ToolAction::Run(args) => plan_tool_run(
                &context,
                &args.tool_name,
                to_invocation_kind(args.kind),
                to_execution_mode(args.mode),
                &args.args_json,
            ),
        }
        .map(CommandOutcome::Bridge),
    }
    .and_then(|output| match output {
        CommandOutcome::Bridge(output) => {
            if cli.global.execute {
                execute_output(&output, &context).map(CommandOutcome::Bridge)
            } else {
                Ok(CommandOutcome::Bridge(output))
            }
        }
        CommandOutcome::Editor(output) => Ok(CommandOutcome::Editor(output)),
    });

    match result {
        Ok(CommandOutcome::Bridge(output)) => emit_success(&output, cli.global.json),
        Ok(CommandOutcome::Editor(output)) => emit_editor_success(&output, cli.global.json),
        Err(error) => emit_error(&error, cli.global.json),
    }
}

fn emit_success(output: &CliJsonOutput, json_mode: bool) {
    if json_mode {
        println!(
            "{}",
            serde_json::to_string_pretty(output).expect("CLI JSON 输出必须可序列化")
        );
    } else {
        println!("{}", render_plain_output(output));
    }
}

fn emit_editor_success(output: &EditorCliOutput, json_mode: bool) {
    if json_mode {
        println!(
            "{}",
            serde_json::to_string_pretty(output).expect("editor CLI JSON 输出必须可序列化")
        );
    } else {
        println!("{}", render_editor_plain_output(output));
    }
}

fn emit_error(error: &CliError, json_mode: bool) -> ! {
    if json_mode {
        println!(
            "{}",
            serde_json::to_string_pretty(&serde_json::json!({
                "ok": false,
                "entrypoint": "mnote-cli",
                "error": {
                    "code": error.code,
                    "message": error.message,
                }
            }))
            .expect("CLI 错误 JSON 输出必须可序列化")
        );
    } else {
        eprintln!("{}: {}", error.code, error.message);
    }
    std::process::exit(1);
}

fn to_invocation_kind(kind: ToolKindArg) -> InvocationKind {
    match kind {
        ToolKindArg::Command => InvocationKind::Command,
        ToolKindArg::Query => InvocationKind::Query,
        ToolKindArg::Job => InvocationKind::Job,
    }
}

fn to_execution_mode(mode: ToolModeArg) -> ToolExecutionMode {
    match mode {
        ToolModeArg::Plan => ToolExecutionMode::Plan,
        ToolModeArg::Result => ToolExecutionMode::Result,
        ToolModeArg::ExplainPlan => ToolExecutionMode::ExplainPlan,
    }
}

fn to_editor_input_kind(kind: EditorInputKindArg) -> mnote_editor_core::EditorInputKind {
    match kind {
        EditorInputKindArg::PlainText => mnote_editor_core::EditorInputKind::PlainText,
        EditorInputKindArg::Markdown => mnote_editor_core::EditorInputKind::Markdown,
    }
}

fn to_editor_ai_scenario(scenario: EditorAiScenarioArg) -> mnote_editor_core::EditorAiScenario {
    match scenario {
        EditorAiScenarioArg::MeetingNotesToTodos => {
            mnote_editor_core::EditorAiScenario::MeetingNotesToTodos
        }
        EditorAiScenarioArg::LongParagraphToTitle => {
            mnote_editor_core::EditorAiScenario::LongParagraphToTitle
        }
        EditorAiScenarioArg::PageReorder => mnote_editor_core::EditorAiScenario::PageReorder,
    }
}
