use clap::Args;
use serde_json::{Value, json};

use crate::util::{
    RawApiResponse, exit_error, print_json_stderr, print_json_stdout,
    raw_api_request_json_with_options, read_json_from_file, resolve_token,
};

const RECORD_ACTIVITY_ENDPOINT: &str = "/v4/agent/record-activity";
const RECORD_RECOVERY_ENDPOINT: &str = "/v4/agent/record-recovery";
const REPORT_HEALTH_ENDPOINT: &str = "/v4/agent/report-health";
const RECORD_NUTRITION_ENDPOINT: &str = "/v4/agent/record-nutrition";
const RECORD_MEASUREMENT_ENDPOINT: &str = "/v4/agent/record-measurement";
const RECORD_BENCHMARK_ENDPOINT: &str = "/v4/agent/record-benchmark";
const PLAN_ACTIVITY_ENDPOINT: &str = "/v4/agent/plan-activity";
const UPDATE_PROFILE_ENDPOINT: &str = "/v4/agent/update-profile";
const UPDATE_PLAN_ENDPOINT: &str = "/v4/agent/update-plan";

const RECORD_ACTIVITY_HELP: &str = r#"Minimal examples:

  If a successful result contains follow_ups, ask those active questions and stay on record_activity for the answer. Do not treat them as passive help text.

  record_activity is for executed training only. If the user mentions a future date, use plan_activity unless they explicitly say it already happened; then send occurrence_status=executed.

  Record one mixed session:
    kura record_activity --data '{"date":"2026-04-05","blocks":[{"kind":"repetition_sets","label":"Back Squat","exercise_identity":"back_squat","measurements":{"sets":5,"reps_per_set":5,"load_kg":130}}]}'

  Record and close a session with effort context:
    kura record_activity --data '{"date":"2026-04-05","close_session":true,"session_follow_up":{"perceived_exertion":8},"blocks":[{"kind":"repetition_sets","label":"Back Squat","exercise_identity":"back_squat","measurements":{"sets":5,"reps_per_set":5,"load_kg":130,"rir":2}}]}'

  Record one explicit benchmark block inside the same session:
    kura record_activity --data '{"date":"2026-04-05","blocks":[{"kind":"benchmark_result","label":"CMJ","exercise_identity":"cmj","measurements":{"result_value":43,"result_unit":"cm","result_attempts":[42,41,43]},"execution_context":{"project_to_performance_test":true,"test_type":"cmj","protocol_variant":"free_arms","measured_property":"jump_height_cm"}}]}'

  Create a protocol profile only after the user confirms this is a recurring comparable setup:
    kura record_activity --data '{"date":"2026-04-05","blocks":[{"kind":"distance_intervals","label":"Flying 30m","measurements":{"repeats":3,"distance_m":30},"execution_context":{"run_in_distance_m":20}}],"protocol_profile":{"action":"create","apply_to_block_index":0,"profile_label":"Flying 30m, 20m run-in","block_kind":"distance_intervals","defaults":{"primary_label":"Flying 30m","measurement_defaults":{"distance_m":30},"execution_context_defaults":{"run_in_distance_m":20}},"comparability":{"required_measurement_keys":["distance_m"],"required_execution_context_keys":["run_in_distance_m"]}}}'

  Create a protocol profile for one already saved block after the user confirms it is recurring:
    kura record_activity --data '{"date":"2026-04-05","operation":"replace","target":{"session_id":"activity_session_abc123","item_id":"activity_block_hang_clean","reason":"confirm_recurring_protocol_profile"},"replacement_patch":{"execution_context":{"start_position":"hip"},"analysis_grade":"comparable"},"protocol_profile":{"action":"create","apply_to_block_id":"activity_block_hang_clean","profile_label":"Hang Power Clean von der Huefte","block_kind":"repetition_sets","defaults":{"primary_label":"Hang Power Clean von der Huefte","exercise_identity":"hang_power_clean","execution_context_defaults":{"start_position":"hip"}},"comparability":{"required_measurement_keys":["load_kg"],"required_execution_context_keys":["start_position"]}}}'

  Correct one saved activity block on the same task:
    kura record_activity --data '{"operation":"replace","target":{"session_id":"activity_session_abc123","item_id":"activity_block_top_speed"},"replacement_patch":{"measurements":{"repeats":5,"distance_m":40}}}'
"#;

const RECORD_RECOVERY_HELP: &str = r#"Minimal examples:

  Record one recovery check-in:
    kura record_recovery --data '{"sleep_hours":8,"sleep_quality":7,"energy_level":6,"soreness":3,"soreness_areas":["legs"]}'

  Record recovery plus bodyweight in one task:
    kura record_recovery --data '{"sleep_hours":8,"sleep_quality":7,"energy_level":6,"soreness":3,"bodyweight_kg":85.5}'

  Record a second same-day check-in as a separate observation:
    kura record_recovery --data '{"timestamp":"2026-04-05T18:30:00Z","energy_level":7,"soreness":2,"soreness_areas":["legs","lower_back"]}'

  For same-day follow-up check-ins, send only fields the user explicitly mentioned. Do not copy sleep_hours or bodyweight_kg from an earlier check-in unless the user confirmed a nap/new sleep entry or a new weigh-in. If Kura flags a duplicate same-day value and the user confirms it, retry with same_day_confirmed_signals such as ["sleep_hours"].
"#;

const REPORT_HEALTH_HELP: &str = r#"Minimal examples:

  Report one injury episode:
    kura report_health --data '{"kind":"injury","affected_area":"right knee","description":"pain on stairs","severity":5,"since":"2026-04-05"}'

  Update one active episode on the same task:
    kura report_health --data '{"operation":"update","kind":"injury","affected_area":"right knee","severity":3}'
"#;

const RECORD_NUTRITION_HELP: &str = r#"Minimal examples:

  Record one meal:
    kura record_nutrition --data '{"kind":"meal","description":"chicken rice bowl","calories":720,"protein_g":48,"carbs_g":82,"fat_g":18}'

  Record one nutrition target:
    kura record_nutrition --data '{"kind":"target","target_calories":2600,"target_protein_g":180,"target_carbs_g":300,"target_fat_g":70}'
"#;

const RECORD_MEASUREMENT_HELP: &str = r#"Minimal examples:

  Record one stature update:
    kura record_measurement --data '{"kind":"measurement","type":"height","value_cm":191}'

  Record one bodyweight snapshot:
    kura record_measurement --data '{"kind":"bodyweight","weight_kg":85.5,"time_of_day":"morning"}'
"#;

const RECORD_BENCHMARK_HELP: &str = r#"Minimal examples:

  Record one CMJ baseline:
    kura record_benchmark --data '{"test_type":"cmj","protocol_variant":"free_arms","measured_property":"jump_height_cm","result":{"kind":"numeric","value":43,"unit":"cm","attempts":[42,41,43]}}'

  Record one flying 30m test:
    kura record_benchmark --data '{"test_type":"flying_30m","protocol_variant":"20m_run_in","result":{"kind":"numeric","value":3.78,"unit":"s"}}'
"#;

const PLAN_ACTIVITY_HELP: &str = r#"Minimal examples:

  Use plan_activity for future or not-yet-executed training. Do not use record_activity unless the user says the session already happened.

  Plan one future workout with training-shaped blocks:
    kura plan_activity --data '{"date":"2026-04-14","session_name":"Power Clean Technik","blocks":[{"kind":"repetition_sets","label":"Hang Power Clean below knee","exercise_identity":"hang_power_clean","measurements":{"sets":4,"reps_per_set":3,"load_kg":70},"execution_context":{"start_position":"below_knee"}}],"reason":"user planned next week"}'

  Plan a named placeholder session when the exact blocks are not known yet:
    kura plan_activity --data '{"date":"2026-04-14","session_name":"Speed + jumps","notes":"details still open"}'
"#;

const UPDATE_PROFILE_HELP: &str = r#"Minimal examples:

  Update durable profile context only:
    kura update_profile --data '{"age":28,"experience_level":"intermediate","training_modality":"strength","training_frequency_per_week":4,"available_equipment":["barbell","rack"],"primary_location":"gym"}'

  Update profile, stature, goal, and one baseline benchmark in one task:
    kura update_profile --data '{"age":28,"experience_level":"intermediate","training_modality":"strength","stature_cm":191,"goal":{"kind":"athletic","label":"dunk"},"baseline_benchmark":{"test_type":"cmj","protocol_variant":"free_arms","result_value":60,"result_unit":"cm"}}'
"#;

const UPDATE_PLAN_HELP: &str = r#"Minimal examples:

  Update one active plan:
    kura update_plan --data '{"kind":"update","plan_id":"default","name":"Spring Block","notes":"focus on volume"}'

  Move one schedule exception:
    kura update_plan --data '{"kind":"schedule_exception_upsert","exception_id":"travel-week-a","operation":"move","selector":{"local_date":"2026-04-10","session_name":"Heavy Lower"},"target":{"local_date":"2026-04-08"},"reason":"travel"}'
"#;

#[derive(Args, Clone)]
pub struct TaskPayloadArgs {
    /// Inline JSON payload for one public task write
    #[arg(long, short = 'd', conflicts_with = "request_file")]
    pub data: Option<String>,

    /// Read the public task payload from file (use '-' for stdin)
    #[arg(long, short = 'f', conflicts_with = "data")]
    pub request_file: Option<String>,
}

#[derive(Args)]
#[command(after_long_help = RECORD_ACTIVITY_HELP)]
pub struct RecordActivityArgs {
    #[command(flatten)]
    pub inner: TaskPayloadArgs,
}

#[derive(Args)]
#[command(after_long_help = RECORD_RECOVERY_HELP)]
pub struct RecordRecoveryArgs {
    #[command(flatten)]
    pub inner: TaskPayloadArgs,
}

#[derive(Args)]
#[command(after_long_help = REPORT_HEALTH_HELP)]
pub struct ReportHealthArgs {
    #[command(flatten)]
    pub inner: TaskPayloadArgs,
}

#[derive(Args)]
#[command(after_long_help = RECORD_NUTRITION_HELP)]
pub struct RecordNutritionArgs {
    #[command(flatten)]
    pub inner: TaskPayloadArgs,
}

#[derive(Args)]
#[command(after_long_help = RECORD_MEASUREMENT_HELP)]
pub struct RecordMeasurementArgs {
    #[command(flatten)]
    pub inner: TaskPayloadArgs,
}

#[derive(Args)]
#[command(after_long_help = RECORD_BENCHMARK_HELP)]
pub struct RecordBenchmarkArgs {
    #[command(flatten)]
    pub inner: TaskPayloadArgs,
}

#[derive(Args)]
#[command(after_long_help = PLAN_ACTIVITY_HELP)]
pub struct PlanActivityArgs {
    #[command(flatten)]
    pub inner: TaskPayloadArgs,
}

#[derive(Args)]
#[command(after_long_help = UPDATE_PROFILE_HELP)]
pub struct UpdateProfileArgs {
    #[command(flatten)]
    pub inner: TaskPayloadArgs,
}

#[derive(Args)]
#[command(after_long_help = UPDATE_PLAN_HELP)]
pub struct UpdatePlanArgs {
    #[command(flatten)]
    pub inner: TaskPayloadArgs,
}

pub async fn run_record_activity(api_url: &str, no_auth: bool, args: RecordActivityArgs) -> i32 {
    run_task(
        api_url,
        no_auth,
        "record_activity",
        RECORD_ACTIVITY_ENDPOINT,
        args.inner,
    )
    .await
}

pub async fn run_record_recovery(api_url: &str, no_auth: bool, args: RecordRecoveryArgs) -> i32 {
    run_task(
        api_url,
        no_auth,
        "record_recovery",
        RECORD_RECOVERY_ENDPOINT,
        args.inner,
    )
    .await
}

pub async fn run_report_health(api_url: &str, no_auth: bool, args: ReportHealthArgs) -> i32 {
    run_task(
        api_url,
        no_auth,
        "report_health",
        REPORT_HEALTH_ENDPOINT,
        args.inner,
    )
    .await
}

pub async fn run_record_nutrition(api_url: &str, no_auth: bool, args: RecordNutritionArgs) -> i32 {
    run_task(
        api_url,
        no_auth,
        "record_nutrition",
        RECORD_NUTRITION_ENDPOINT,
        args.inner,
    )
    .await
}

pub async fn run_record_measurement(
    api_url: &str,
    no_auth: bool,
    args: RecordMeasurementArgs,
) -> i32 {
    run_task(
        api_url,
        no_auth,
        "record_measurement",
        RECORD_MEASUREMENT_ENDPOINT,
        args.inner,
    )
    .await
}

pub async fn run_record_benchmark(api_url: &str, no_auth: bool, args: RecordBenchmarkArgs) -> i32 {
    run_task(
        api_url,
        no_auth,
        "record_benchmark",
        RECORD_BENCHMARK_ENDPOINT,
        args.inner,
    )
    .await
}

pub async fn run_plan_activity(api_url: &str, no_auth: bool, args: PlanActivityArgs) -> i32 {
    run_task(
        api_url,
        no_auth,
        "plan_activity",
        PLAN_ACTIVITY_ENDPOINT,
        args.inner,
    )
    .await
}

pub async fn run_update_profile(api_url: &str, no_auth: bool, args: UpdateProfileArgs) -> i32 {
    run_task(
        api_url,
        no_auth,
        "update_profile",
        UPDATE_PROFILE_ENDPOINT,
        args.inner,
    )
    .await
}

pub async fn run_update_plan(api_url: &str, no_auth: bool, args: UpdatePlanArgs) -> i32 {
    run_task(
        api_url,
        no_auth,
        "update_plan",
        UPDATE_PLAN_ENDPOINT,
        args.inner,
    )
    .await
}

async fn run_task(
    api_url: &str,
    no_auth: bool,
    task_name: &'static str,
    endpoint: &str,
    args: TaskPayloadArgs,
) -> i32 {
    let token = resolve_or_exit(api_url, no_auth).await;
    let body = read_task_json_object(task_name, &args);
    let response = post_task_request(api_url, token.as_deref(), endpoint, body).await;
    emit_task_response(
        task_name,
        response,
        Some("Check API availability/auth and retry."),
    )
}

fn read_task_json_object(task_name: &str, args: &TaskPayloadArgs) -> Value {
    match (args.data.as_deref(), args.request_file.as_deref()) {
        (Some(raw), None) => serde_json::from_str(raw).unwrap_or_else(|error| {
            exit_error(
                &format!("Invalid JSON in --data: {error}"),
                Some(&format!(
                    "Provide one valid JSON object for `kura {task_name}`."
                )),
            )
        }),
        (None, Some(path)) => read_json_from_file(path).unwrap_or_else(|error| {
            exit_error(
                &error,
                Some(&format!(
                    "Provide one valid JSON object for `kura {task_name}`."
                )),
            )
        }),
        (None, None) => exit_error(
            &format!("{task_name} needs --data or --request-file."),
            Some(&format!(
                "Run `kura {task_name} --help` for canonical examples."
            )),
        ),
        (Some(_), Some(_)) => unreachable!("clap enforces conflicts"),
    }
}

async fn post_task_request(
    api_url: &str,
    token: Option<&str>,
    endpoint: &str,
    body: Value,
) -> Result<RawApiResponse, String> {
    raw_api_request_json_with_options(
        api_url,
        reqwest::Method::POST,
        endpoint,
        token,
        Some(body),
        &[],
        &[],
        true,
    )
    .await
}

fn emit_task_response(
    task_name: &'static str,
    response: Result<RawApiResponse, String>,
    docs_hint: Option<&str>,
) -> i32 {
    let response = response.unwrap_or_else(|error| exit_error(&error, docs_hint));
    if (200..=299).contains(&response.status) {
        print_json_stdout(&response.body);
        return 0;
    }

    let transformed = flatten_error_response(task_name, response.body);
    print_json_stderr(&transformed);
    if (400..=499).contains(&response.status) {
        1
    } else {
        2
    }
}

fn flatten_error_response(task_name: &str, body: Value) -> Value {
    json!({
        "status": "error",
        "task": task_name,
        "summary": body.get("message")
            .cloned()
            .or_else(|| body.get("error").cloned())
            .unwrap_or_else(|| json!("The task failed.")),
        "refs": body.get("refs").cloned().unwrap_or_else(|| json!([])),
        "next_step": body.get("docs_hint").cloned().unwrap_or(Value::Null),
        "warnings": body.get("warnings").cloned().unwrap_or_else(|| json!([])),
    })
}

async fn resolve_or_exit(api_url: &str, no_auth: bool) -> Option<String> {
    if no_auth {
        return None;
    }
    match resolve_token(api_url).await {
        Ok(token) => Some(token),
        Err(error) => exit_error(
            &error.to_string(),
            Some(
                "Run `kura admin login`, set KURA_API_KEY, or use --no-auth behind an auth-injecting proxy.",
            ),
        ),
    }
}

pub fn public_record_activity_payload_contract() -> Value {
    json!({
        "schema_version": "kura.public_record_activity_payload_contract.v1",
        "entrypoint": "kura record_activity",
        "endpoint": "/v4/agent/record-activity",
        "allowed_modes": [
            "fresh_write",
            "session_follow_up",
            "import_assignment_resolution",
            "protocol_profile_create_or_revise",
            "saved_block_protocol_profile_confirmation",
            "saved_block_correction_or_retract"
        ],
        "examples": {
            "fresh_write": {
                "date": "2026-04-05",
                "blocks": [{
                    "kind": "repetition_sets",
                    "label": "Back Squat",
                    "exercise_identity": "back_squat",
                    "measurements": {
                        "sets": 5,
                        "reps_per_set": 5,
                        "load_kg": 130
                    }
                }]
            },
            "future_date_that_already_happened": {
                "date": "2026-04-12",
                "occurrence_status": "executed",
                "blocks": [{
                    "kind": "repetition_sets",
                    "label": "Back Squat",
                    "exercise_identity": "back_squat",
                    "measurements": {
                        "sets": 5,
                        "reps_per_set": 5,
                        "load_kg": 130
                    }
                }]
            },
            "close_with_effort_context": {
                "date": "2026-04-05",
                "close_session": true,
                "session_follow_up": {
                    "perceived_exertion": 8
                },
                "blocks": [{
                    "kind": "repetition_sets",
                    "label": "Back Squat",
                    "exercise_identity": "back_squat",
                    "measurements": {
                        "sets": 5,
                        "reps_per_set": 5,
                        "load_kg": 130,
                        "rir": 2
                    }
                }]
            },
            "benchmark_projection": {
                "date": "2026-04-05",
                "blocks": [{
                    "kind": "benchmark_result",
                    "label": "CMJ",
                    "exercise_identity": "cmj",
                    "measurements": {
                        "result_value": 43,
                        "result_unit": "cm",
                        "result_attempts": [42, 41, 43]
                    },
                    "execution_context": {
                        "project_to_performance_test": true,
                        "test_type": "cmj",
                        "protocol_variant": "free_arms",
                        "measured_property": "jump_height_cm"
                    }
                }]
            },
            "protocol_profile_create_after_user_confirmation": {
                "date": "2026-04-05",
                "blocks": [{
                    "kind": "distance_intervals",
                    "label": "Flying 30m",
                    "measurements": {
                        "repeats": 3,
                        "distance_m": 30
                    },
                    "execution_context": {
                        "run_in_distance_m": 20
                    }
                }],
                "protocol_profile": {
                    "action": "create",
                    "apply_to_block_index": 0,
                    "profile_label": "Flying 30m, 20m run-in",
                    "block_kind": "distance_intervals",
                    "defaults": {
                        "primary_label": "Flying 30m",
                        "measurement_defaults": {
                            "distance_m": 30
                        },
                        "execution_context_defaults": {
                            "run_in_distance_m": 20
                        }
                    },
                    "comparability": {
                        "required_measurement_keys": ["distance_m"],
                        "required_execution_context_keys": ["run_in_distance_m"]
                    }
                }
            },
            "saved_block_correction_or_retract": {
                "operation": "replace",
                "target": {
                    "session_id": "activity_session_abc123",
                    "item_id": "activity_block_top_speed"
                },
                "replacement_patch": {
                    "measurements": {
                        "repeats": 5,
                        "distance_m": 40
                    }
                }
            },
            "saved_block_protocol_profile_confirmation": {
                "date": "2026-04-05",
                "operation": "replace",
                "target": {
                    "session_id": "activity_session_abc123",
                    "item_id": "activity_block_hang_clean",
                    "reason": "confirm_recurring_protocol_profile"
                },
                "replacement_patch": {
                    "execution_context": {
                        "start_position": "hip"
                    },
                    "analysis_grade": "comparable"
                },
                "protocol_profile": {
                    "action": "create",
                    "apply_to_block_id": "activity_block_hang_clean",
                    "profile_label": "Hang Power Clean von der Huefte",
                    "block_kind": "repetition_sets",
                    "defaults": {
                        "primary_label": "Hang Power Clean von der Huefte",
                        "exercise_identity": "hang_power_clean",
                        "execution_context_defaults": {
                            "start_position": "hip"
                        }
                    },
                    "comparability": {
                        "required_measurement_keys": ["load_kg"],
                        "required_execution_context_keys": ["start_position"]
                    }
                }
            }
        }
    })
}

pub fn public_record_recovery_payload_contract() -> Value {
    json!({
        "schema_version": "kura.public_record_recovery_payload_contract.v1",
        "entrypoint": "kura record_recovery",
        "endpoint": "/v4/agent/record-recovery",
        "examples": {
            "basic": {
                "sleep_hours": 8,
                "sleep_quality": 7,
                "energy_level": 6,
                "soreness": 3,
                "soreness_areas": ["legs"]
            },
            "with_bodyweight": {
                "sleep_hours": 8,
                "sleep_quality": 7,
                "energy_level": 6,
                "soreness": 3,
                "bodyweight_kg": 85.5
            },
            "second_same_day_checkin": {
                "timestamp": "2026-04-05T18:30:00Z",
                "energy_level": 7,
                "soreness": 2,
                "soreness_areas": ["legs", "lower_back"]
            }
        },
        "same_day_carryover_policy": {
            "send_only_explicitly_mentioned_fields": true,
            "do_not_copy_prior_sleep_or_weight": true,
            "sleep_fields_remain_allowed_for_explicit_naps_or_new_sleep_entries": true,
            "same_day_confirmed_signals_retry_allowed": true
        }
    })
}

pub fn public_report_health_payload_contract() -> Value {
    json!({
        "schema_version": "kura.public_report_health_payload_contract.v1",
        "entrypoint": "kura report_health",
        "endpoint": "/v4/agent/report-health",
        "examples": {
            "injury": {
                "kind": "injury",
                "affected_area": "right knee",
                "description": "pain on stairs",
                "severity": 5,
                "since": "2026-04-05"
            },
            "update": {
                "operation": "update",
                "kind": "injury",
                "affected_area": "right knee",
                "severity": 3
            }
        }
    })
}

pub fn public_record_nutrition_payload_contract() -> Value {
    json!({
        "schema_version": "kura.public_record_nutrition_payload_contract.v1",
        "entrypoint": "kura record_nutrition",
        "endpoint": "/v4/agent/record-nutrition",
        "examples": {
            "meal": {
                "kind": "meal",
                "description": "chicken rice bowl",
                "calories": 720,
                "protein_g": 48,
                "carbs_g": 82,
                "fat_g": 18
            },
            "target": {
                "kind": "target",
                "target_calories": 2600,
                "target_protein_g": 180,
                "target_carbs_g": 300,
                "target_fat_g": 70
            }
        }
    })
}

pub fn public_record_measurement_payload_contract() -> Value {
    json!({
        "schema_version": "kura.public_record_measurement_payload_contract.v1",
        "entrypoint": "kura record_measurement",
        "endpoint": "/v4/agent/record-measurement",
        "examples": {
            "stature": {
                "kind": "measurement",
                "type": "height",
                "value_cm": 191
            },
            "bodyweight": {
                "kind": "bodyweight",
                "weight_kg": 85.5,
                "time_of_day": "morning"
            }
        }
    })
}

pub fn public_record_benchmark_payload_contract() -> Value {
    json!({
        "schema_version": "kura.public_record_benchmark_payload_contract.v1",
        "entrypoint": "kura record_benchmark",
        "endpoint": "/v4/agent/record-benchmark",
        "examples": {
            "cmj": {
                "test_type": "cmj",
                "protocol_variant": "free_arms",
                "measured_property": "jump_height_cm",
                "result": {
                    "kind": "numeric",
                    "value": 43,
                    "unit": "cm",
                    "attempts": [42, 41, 43]
                }
            },
            "flying_30m": {
                "test_type": "flying_30m",
                "protocol_variant": "20m_run_in",
                "result": {
                    "kind": "numeric",
                    "value": 3.78,
                    "unit": "s"
                }
            }
        }
    })
}

pub fn public_plan_activity_payload_contract() -> Value {
    json!({
        "schema_version": "kura.public_plan_activity_payload_contract.v1",
        "entrypoint": "kura plan_activity",
        "endpoint": "/v4/agent/plan-activity",
        "required_shape": "single_object_with_date_and_session_or_blocks",
        "purpose": "Plan future or not-yet-executed training without creating an open activity log.",
        "examples": {
            "planned_blocks": {
                "date": "2026-04-14",
                "session_name": "Power Clean Technik",
                "blocks": [{
                    "kind": "repetition_sets",
                    "label": "Hang Power Clean below knee",
                    "exercise_identity": "hang_power_clean",
                    "measurements": {
                        "sets": 4,
                        "reps_per_set": 3,
                        "load_kg": 70
                    },
                    "execution_context": {
                        "start_position": "below_knee"
                    }
                }]
            },
            "planned_placeholder": {
                "date": "2026-04-14",
                "session_name": "Speed + jumps",
                "notes": "details still open"
            }
        }
    })
}

pub fn public_update_profile_payload_contract() -> Value {
    json!({
        "schema_version": "kura.public_update_profile_payload_contract.v1",
        "entrypoint": "kura update_profile",
        "endpoint": "/v4/agent/update-profile",
        "examples": {
            "profile_only": {
                "age": 28,
                "experience_level": "intermediate",
                "training_modality": "strength",
                "training_frequency_per_week": 4
            },
            "profile_measurement_goal_and_baseline": {
                "age": 28,
                "stature_cm": 191,
                "goal": {
                    "kind": "athletic",
                    "label": "dunk"
                },
                "baseline_benchmark": {
                    "test_type": "cmj",
                    "protocol_variant": "free_arms",
                    "result_value": 60,
                    "result_unit": "cm"
                }
            }
        }
    })
}

pub fn public_update_plan_payload_contract() -> Value {
    json!({
        "schema_version": "kura.public_update_plan_payload_contract.v1",
        "entrypoint": "kura update_plan",
        "endpoint": "/v4/agent/update-plan",
        "required_shape": "single_object_with_kind",
        "allowed_kinds": [
            "create",
            "update",
            "replace",
            "archive",
            "schedule_exception_upsert",
            "schedule_exception_clear"
        ],
        "examples": {
            "update": {
                "kind": "update",
                "plan_id": "default",
                "name": "Spring Block",
                "notes": "focus on volume"
            },
            "schedule_exception_upsert": {
                "kind": "schedule_exception_upsert",
                "exception_id": "travel-week-a",
                "operation": "move",
                "selector": {
                    "local_date": "2026-04-10",
                    "session_name": "Heavy Lower"
                },
                "target": {
                    "local_date": "2026-04-08"
                }
            }
        }
    })
}
