from __future__ import annotations

from flow_xhs import __version__
from flow_xhs.cli.spec.builders import (
    build_alias as _alias,
    build_command as _command,
    build_command_option as _command_option,
    build_global_option as _global_option,
    build_input_source_option as _input_source_option,
)
from flow_xhs.contracts.command_rules import (
    ACCOUNT_LOGIN_MODE_VALUES,
    ACCOUNT_LOGIN_TIMEOUT_DEFAULT,
    ACCOUNT_LOGIN_TIMEOUT_MIN,
    BOOLEAN_LITERAL_VALUES,
    NOTE_GET_COMMENT_SIZE_DEFAULT,
    NOTE_GET_COMMENT_SIZE_MAX,
    NOTE_GET_COMMENT_SIZE_MIN,
    NOTE_GET_SUB_COMMENT_SIZE_DEFAULT,
    NOTE_GET_SUB_COMMENT_SIZE_MAX,
    NOTE_GET_SUB_COMMENT_SIZE_MIN,
    NOTE_SEARCH_SIZE_DEFAULT,
    PUBLISH_TARGET_VALUES,
    SEARCH_SIZE_MAX,
    SEARCH_SIZE_MIN,
    USER_SEARCH_SIZE_DEFAULT,
)
from flow_xhs.platform.search_filters import (
    get_filter_aliases,
    get_filter_canonical_values,
    get_filter_label,
)
from flow_xhs.cli.spec.enums import (
    ConstraintSeverity,
    ConstraintType,
    DefaultKind,
    ExampleCategory,
    InputSourceKind,
    ParameterGroup,
    ParameterKind,
    ParameterScope,
    RequirednessMode,
    ValueKind,
)
from flow_xhs.cli.spec.model import (
    CliSpec,
    CommandHelpSpec,
    ConditionExpr,
    ConstraintSpec,
    DefaultSpec,
    DocTopicSpec,
    ExampleSpec,
    EventSpec,
    EventTypeSpec,
    InputSourceBinding,
    ParameterSpec,
    RequirednessSpec,
    ValueTypeSpec,
    AllowedValueSpec,
    OutputSpec,
)
from flow_xhs.cli.spec.validate import validate_cli_spec


SUCCESS_SCHEMA = "xhs/schemas/command-output-success.json"
ERROR_SCHEMA = "xhs/schemas/command-output-error.json"


FORMAT_OPTION = _global_option(
    id="format",
    cli_name="--format",
    summary="输出格式。",
    default=DefaultSpec(kind=DefaultKind.STATIC, value="table", display_value="table"),
    allowed_values=(
        AllowedValueSpec("json"),
        AllowedValueSpec("jsonl"),
        AllowedValueSpec("table"),
    ),
)
OUT_OPTION = _global_option(id="out", cli_name="--out", summary="将最终结果写入文件。")
QUIET_OPTION = _global_option(
    id="quiet",
    cli_name="--quiet",
    summary="静默日志，仅输出结果。",
    kind=ParameterKind.FLAG,
    value_kind=ValueKind.BOOLEAN,
)
TRACE_ID_OPTION = _global_option(
    id="trace_id",
    cli_name="--trace-id",
    summary="请求链路 ID。",
    default=DefaultSpec(kind=DefaultKind.DYNAMIC, display_value="auto-generated"),
)
TIMEOUT_OPTION = _global_option(
    id="timeout",
    cli_name="--timeout",
    summary="命令超时秒数。",
    value_kind=ValueKind.INTEGER,
    default=DefaultSpec(kind=DefaultKind.STATIC, value=120, display_value="120"),
)
VERSION_OPTION = _global_option(
    id="version",
    cli_name="--version",
    summary="显示版本并退出。",
    kind=ParameterKind.FLAG,
    value_kind=ValueKind.BOOLEAN,
)
NOTE_PUBLISH_TARGET = _command_option(
    id="target",
    cli_name="--target",
    group=ParameterGroup.REQUIRED_INPUTS,
    summary="发布目标类型。",
    value_kind=ValueKind.ENUM,
    required=True,
    allowed_values=tuple(AllowedValueSpec(value) for value in PUBLISH_TARGET_VALUES),
    nullable=False,
)

NOTE_GET_DESCRIPTION = (
    "优先直接传入 `note_url`，由系统自动解析 note_id、xsec_token、xsec_source。\n"
    "CLI 参数优先于 `--input` JSON，同名字段最终再回落到默认值。"
)

USER_GET_DESCRIPTION = (
    "优先直接传入 `user_url`，由系统自动解析 user_id、xsec_token、xsec_source。\n"
    "CLI 参数优先于 `--input` JSON，同名字段最终再回落到默认值。"
)


def _filter_allowed_values(field: str) -> tuple[AllowedValueSpec, ...]:
    return tuple(
        AllowedValueSpec(
            value=value,
            label=get_filter_label(field, value),
            aliases=get_filter_aliases(field, value),
        )
        for value in get_filter_canonical_values(field)
    )

FLOW_XHS_SPEC = CliSpec(
    spec_version="1.0",
    cli_version=__version__,
    cli_name="flow-xhs",
    product_name="Flow XHS CLI",
    summary="小红书账号、查询与发布命令行工具。",
    description="命令契约驱动的 CLI 规范定义。",
    canonical_syntax="flow-xhs [GLOBAL_OPTIONS] RESOURCE ACTION [ACTION_OPTIONS]",
    global_options=(
        FORMAT_OPTION,
        OUT_OPTION,
        QUIET_OPTION,
        TRACE_ID_OPTION,
        TIMEOUT_OPTION,
        VERSION_OPTION,
    ),
    commands=(
        _command(
            id="init",
            path=("init",),
            summary="初始化运行时目录与本地存储。",
            description="返回字段包括 `app_home`、`accounts_dir`、`settings_file`、`task_schedules_file`。",
            success_command="init",
            aliases=(_alias("init", "runtime", replacement="flow-xhs init"),),
            output=OutputSpec(
                success_command="init",
                success_schema_ref=SUCCESS_SCHEMA,
                error_schema_ref=ERROR_SCHEMA,
                data_shape_summary="运行时目录与基础配置路径对象。",
            ),
            examples=(
                ExampleSpec(
                    id="init_minimal",
                    title="初始化运行时",
                    category=ExampleCategory.MINIMAL,
                    command="flow-xhs init",
                    machine_friendly=True,
                ),
                ExampleSpec(
                    id="init_common",
                    title="JSON 模式初始化",
                    category=ExampleCategory.COMMON,
                    command="flow-xhs --format json init",
                    machine_friendly=True,
                ),
                ExampleSpec(
                    id="init_automation",
                    title="自动化系统初始化",
                    category=ExampleCategory.AUTOMATION,
                    command="flow-xhs --format json --trace-id req-001 init",
                    machine_friendly=True,
                ),
            ),
            related_commands=("flow-xhs doctor", "flow-xhs account login"),
            help=CommandHelpSpec(show_aliases=False),
        ),
        _command(
            id="doctor",
            path=("doctor",),
            summary="执行环境与依赖自检。",
            description="执行自检并输出可机读诊断结果。推荐配合 `--format json` 使用。",
            success_command="doctor",
            aliases=(_alias("doctor", "run", replacement="flow-xhs doctor"),),
            output=OutputSpec(
                success_command="doctor",
                success_schema_ref=SUCCESS_SCHEMA,
                error_schema_ref=ERROR_SCHEMA,
                data_shape_summary="诊断结果对象，包含整体状态和检查项。",
            ),
            examples=(
                ExampleSpec(
                    id="doctor_minimal",
                    title="执行自检",
                    category=ExampleCategory.MINIMAL,
                    command="flow-xhs doctor",
                    machine_friendly=True,
                ),
                ExampleSpec(
                    id="doctor_common",
                    title="JSON 模式自检",
                    category=ExampleCategory.COMMON,
                    command="flow-xhs --format json doctor",
                    machine_friendly=True,
                ),
                ExampleSpec(
                    id="doctor_automation",
                    title="自动化系统自检",
                    category=ExampleCategory.AUTOMATION,
                    command="flow-xhs --format json --trace-id req-001 doctor",
                    machine_friendly=True,
                ),
            ),
            related_commands=("flow-xhs init", "flow-xhs account login"),
            help=CommandHelpSpec(show_aliases=False),
        ),
        _command(
            id="account_login",
            path=("account", "login"),
            summary="启动账号登录流程并写入持久登录态。",
            description="支持 local 与 remote 两种登录模式；自动化调用推荐 `--format json` 并监听 stderr 事件流。",
            success_command="account.login",
            output=OutputSpec(
                success_command="account.login",
                success_schema_ref=SUCCESS_SCHEMA,
                error_schema_ref=ERROR_SCHEMA,
                data_shape_summary="登录结果对象，包含账号标识、模式和登录结果。",
                supports_events=True,
                event_channel="stderr",
                event_spec=EventSpec(
                    type_field="event",
                    events=(
                        EventTypeSpec(
                            name="login.qr_ready",
                            summary="二维码已准备好，可供扫码登录。",
                            payload_fields=("mode", "screenshot_path", "timeout_seconds"),
                        ),
                    ),
                ),
            ),
            parameters=(
                _command_option(
                    id="mode",
                    cli_name="--mode",
                    group=ParameterGroup.OPTIONAL_INPUTS,
                    summary="登录模式。",
                    value_kind=ValueKind.ENUM,
                    default=DefaultSpec(kind=DefaultKind.STATIC, value="local", display_value="local"),
                    allowed_values=tuple(AllowedValueSpec(value) for value in ACCOUNT_LOGIN_MODE_VALUES),
                ),
                _command_option(
                    id="login_timeout_seconds",
                    cli_name="--login-timeout-seconds",
                    group=ParameterGroup.EXECUTION_CONTROLS,
                    summary="登录超时秒数。",
                    value_kind=ValueKind.INTEGER,
                    min_value=ACCOUNT_LOGIN_TIMEOUT_MIN,
                    default=DefaultSpec(
                        kind=DefaultKind.STATIC,
                        value=ACCOUNT_LOGIN_TIMEOUT_DEFAULT,
                        display_value=str(ACCOUNT_LOGIN_TIMEOUT_DEFAULT),
                    ),
                ),
                _command_option(
                    id="screenshot_path",
                    cli_name="--screenshot-path",
                    group=ParameterGroup.EXECUTION_CONTROLS,
                    summary="remote 模式截图路径。",
                    value_kind=ValueKind.PATH,
                ),
            ),
            constraints=(
                ConstraintSpec(
                    id="account_login_remote_screenshot_recommended",
                    type=ConstraintType.WHEN_THEN_REQUIRES,
                    severity=ConstraintSeverity.WARNING,
                    targets=("mode", "screenshot_path"),
                    message="remote 模式推荐提供 --screenshot-path，便于上游消费扫码截图。",
                    help_text="当 --mode=remote 时，推荐显式提供 --screenshot-path。",
                    condition=ConditionExpr(op="eq", field="mode", value="remote"),
                ),
                ConstraintSpec(
                    id="account_login_timeout_minimum",
                    type=ConstraintType.RANGE,
                    severity=ConstraintSeverity.ERROR,
                    targets=("login_timeout_seconds",),
                    message="login_timeout_seconds 必须大于等于 30。",
                    help_text=f"`--login-timeout-seconds` 最小值为 {ACCOUNT_LOGIN_TIMEOUT_MIN} 秒。",
                ),
            ),
            examples=(
                ExampleSpec(
                    id="account_login_minimal",
                    title="本机扫码登录",
                    category=ExampleCategory.MINIMAL,
                    command="flow-xhs --format json account login",
                    machine_friendly=True,
                ),
                ExampleSpec(
                    id="account_login_remote",
                    title="远程扫码登录",
                    category=ExampleCategory.COMMON,
                    command="flow-xhs --format json account login --mode remote --screenshot-path /tmp/xhs-login.png",
                    machine_friendly=True,
                ),
                ExampleSpec(
                    id="account_login_automation",
                    title="自动化系统登录",
                    category=ExampleCategory.AUTOMATION,
                    command="flow-xhs --format json --trace-id req-001 account login --mode remote --login-timeout-seconds 300",
                    machine_friendly=True,
                ),
            ),
            related_commands=("flow-xhs account status", "flow-xhs account list"),
            help=CommandHelpSpec(show_aliases=False, verbose_sections=("constraints",)),
        ),
        _command(
            id="account_status",
            path=("account", "status"),
            summary="实时检查并刷新账号登录状态。",
            success_command="account.status",
            output=OutputSpec(
                success_command="account.status",
                success_schema_ref=SUCCESS_SCHEMA,
                error_schema_ref=ERROR_SCHEMA,
                data_shape_summary="账号状态列表对象。",
            ),
            examples=(
                ExampleSpec(
                    id="account_status_minimal",
                    title="检查全部账号状态",
                    category=ExampleCategory.MINIMAL,
                    command="flow-xhs --format json account status",
                    machine_friendly=True,
                ),
                ExampleSpec(
                    id="account_status_common",
                    title="检查账号状态并查看当前环境标记",
                    category=ExampleCategory.COMMON,
                    command="flow-xhs --format json account status",
                    machine_friendly=True,
                ),
                ExampleSpec(
                    id="account_status_automation",
                    title="自动化系统检查账号状态",
                    category=ExampleCategory.AUTOMATION,
                    command="flow-xhs --format json --trace-id req-001 account status",
                    machine_friendly=True,
                ),
            ),
            related_commands=("flow-xhs account login", "flow-xhs account list"),
            help=CommandHelpSpec(show_aliases=False),
        ),
        _command(
            id="account_list",
            path=("account", "list"),
            summary="列出账号池。",
            success_command="account.list",
            parameters=(
                _command_option(
                    id="only_active",
                    cli_name="--only-active",
                    group=ParameterGroup.OPTIONAL_INPUTS,
                    summary="是否仅展示当前可用账号。",
                    value_kind=ValueKind.ENUM,
                    default=DefaultSpec(kind=DefaultKind.STATIC, value="true", display_value="true"),
                    allowed_values=tuple(AllowedValueSpec(value) for value in BOOLEAN_LITERAL_VALUES),
                ),
            ),
            output=OutputSpec(
                success_command="account.list",
                success_schema_ref=SUCCESS_SCHEMA,
                error_schema_ref=ERROR_SCHEMA,
                data_shape_summary="账号池列表对象。",
            ),
            examples=(
                ExampleSpec(
                    id="account_list_minimal",
                    title="列出可用账号",
                    category=ExampleCategory.MINIMAL,
                    command="flow-xhs --format json account list",
                    machine_friendly=True,
                ),
                ExampleSpec(
                    id="account_list_common",
                    title="列出全部账号",
                    category=ExampleCategory.COMMON,
                    command="flow-xhs --format json account list --only-active false",
                    machine_friendly=True,
                ),
                ExampleSpec(
                    id="account_list_automation",
                    title="自动化系统列账号",
                    category=ExampleCategory.AUTOMATION,
                    command="flow-xhs --format json --trace-id req-001 account list",
                    machine_friendly=True,
                ),
            ),
            related_commands=("flow-xhs account status", "flow-xhs account activate"),
            help=CommandHelpSpec(show_aliases=False),
        ),
        _command(
            id="account_activate",
            path=("account", "activate"),
            summary="设置当前账号环境。",
            success_command="account.activate",
            parameters=(
                _command_option(
                    id="account",
                    cli_name="--account",
                    group=ParameterGroup.REQUIRED_INPUTS,
                    summary="账号唯一标识。",
                    required=True,
                    nullable=False,
                ),
            ),
            output=OutputSpec(
                success_command="account.activate",
                success_schema_ref=SUCCESS_SCHEMA,
                error_schema_ref=ERROR_SCHEMA,
                data_shape_summary="账号状态变更结果对象。",
            ),
            examples=(
                ExampleSpec(
                    id="account_activate_minimal",
                    title="切换当前账号",
                    category=ExampleCategory.MINIMAL,
                    command="flow-xhs --format json account activate --account u1",
                    machine_friendly=True,
                ),
                ExampleSpec(
                    id="account_activate_common",
                    title="切换当前账号并带 trace_id",
                    category=ExampleCategory.COMMON,
                    command="flow-xhs --format json --trace-id req-001 account activate --account u1",
                    machine_friendly=True,
                ),
                ExampleSpec(
                    id="account_activate_automation",
                    title="自动化系统切换当前账号",
                    category=ExampleCategory.AUTOMATION,
                    command="flow-xhs --format json account activate --account u1",
                    machine_friendly=True,
                ),
            ),
            related_commands=("flow-xhs account list", "flow-xhs user self"),
            help=CommandHelpSpec(show_aliases=False),
        ),
        _command(
            id="account_delete",
            path=("account", "delete"),
            summary="删除账号记录。",
            success_command="account.delete",
            parameters=(
                _command_option(
                    id="account",
                    cli_name="--account",
                    group=ParameterGroup.REQUIRED_INPUTS,
                    summary="账号唯一标识。",
                    required=True,
                    nullable=False,
                ),
            ),
            output=OutputSpec(
                success_command="account.delete",
                success_schema_ref=SUCCESS_SCHEMA,
                error_schema_ref=ERROR_SCHEMA,
                data_shape_summary="账号删除结果对象。",
            ),
            examples=(
                ExampleSpec(
                    id="account_delete_minimal",
                    title="删除账号",
                    category=ExampleCategory.MINIMAL,
                    command="flow-xhs --format json account delete --account u1",
                    machine_friendly=True,
                ),
                ExampleSpec(
                    id="account_delete_common",
                    title="删除账号并带 trace_id",
                    category=ExampleCategory.COMMON,
                    command="flow-xhs --format json --trace-id req-001 account delete --account u1",
                    machine_friendly=True,
                ),
                ExampleSpec(
                    id="account_delete_automation",
                    title="自动化系统删除账号",
                    category=ExampleCategory.AUTOMATION,
                    command="flow-xhs --format json account delete --account u1",
                    machine_friendly=True,
                ),
            ),
            related_commands=("flow-xhs account list", "flow-xhs account activate"),
            help=CommandHelpSpec(show_aliases=False),
        ),
        _command(
            id="note_get",
            path=("note", "get"),
            summary="获取单条笔记详情与评论。",
            description=NOTE_GET_DESCRIPTION,
            success_command="note.get",
            aliases=(_alias("note", replacement="flow-xhs note get"),),
            parameters=(
                _command_option(
                    id="note_url",
                    cli_name="--note-url",
                    group=ParameterGroup.CONDITIONAL_INPUTS,
                    summary="笔记详情 URL。",
                    description="推荐直接传入完整笔记链接，自动解析 note_id、xsec_token、xsec_source。",
                ),
                _command_option(
                    id="comment_size",
                    cli_name="--comment-size",
                    group=ParameterGroup.OPTIONAL_INPUTS,
                    summary="一级评论数量。",
                    value_kind=ValueKind.INTEGER,
                    min_value=NOTE_GET_COMMENT_SIZE_MIN,
                    max_value=NOTE_GET_COMMENT_SIZE_MAX,
                    default=DefaultSpec(
                        kind=DefaultKind.STATIC,
                        value=NOTE_GET_COMMENT_SIZE_DEFAULT,
                        display_value=str(NOTE_GET_COMMENT_SIZE_DEFAULT),
                    ),
                ),
                _command_option(
                    id="sub_comment_size",
                    cli_name="--sub-comment-size",
                    group=ParameterGroup.OPTIONAL_INPUTS,
                    summary="二级评论数量。",
                    value_kind=ValueKind.INTEGER,
                    min_value=NOTE_GET_SUB_COMMENT_SIZE_MIN,
                    max_value=NOTE_GET_SUB_COMMENT_SIZE_MAX,
                    default=DefaultSpec(
                        kind=DefaultKind.STATIC,
                        value=NOTE_GET_SUB_COMMENT_SIZE_DEFAULT,
                        display_value=str(NOTE_GET_SUB_COMMENT_SIZE_DEFAULT),
                    ),
                ),
                _input_source_option(
                    summary="JSON 输入文件路径或标准输入。",
                    fields="note_url,comment_size,sub_comment_size",
                    description=(
                        "字段示例：note_url,comment_size,sub_comment_size。"
                    ),
                ),
            ),
            constraints=(
                ConstraintSpec(
                    id="note_get_requires_note_url_from_cli_or_input",
                    type=ConstraintType.ONE_OF,
                    severity=ConstraintSeverity.ERROR,
                    targets=("note_url", "input"),
                    message="需要通过 --note-url 或 --input 提供 note_url。",
                ),
                ConstraintSpec(
                    id="note_get_comment_size_range",
                    type=ConstraintType.RANGE,
                    severity=ConstraintSeverity.ERROR,
                    targets=("comment_size",),
                    message="comment_size 必须在 1 到 100 之间。",
                    help_text=(
                        f"`--comment-size` 取值范围为 "
                        f"{NOTE_GET_COMMENT_SIZE_MIN}-{NOTE_GET_COMMENT_SIZE_MAX}。"
                    ),
                ),
                ConstraintSpec(
                    id="note_get_sub_comment_size_range",
                    type=ConstraintType.RANGE,
                    severity=ConstraintSeverity.ERROR,
                    targets=("sub_comment_size",),
                    message="sub_comment_size 必须在 1 到 50 之间。",
                    help_text=(
                        f"`--sub-comment-size` 取值范围为 "
                        f"{NOTE_GET_SUB_COMMENT_SIZE_MIN}-{NOTE_GET_SUB_COMMENT_SIZE_MAX}。"
                    ),
                ),
            ),
            output=OutputSpec(
                success_command="note.get",
                success_schema_ref=SUCCESS_SCHEMA,
                error_schema_ref=ERROR_SCHEMA,
                data_shape_summary="单条笔记详情对象，包含评论信息。",
            ),
            examples=(
                ExampleSpec(
                    id="note_get_minimal",
                    title="最小笔记详情示例",
                    category=ExampleCategory.MINIMAL,
                    command="flow-xhs --format json note get --note-url https://www.xiaohongshu.com/explore/n1?xsec_source=pc_feed",
                    machine_friendly=True,
                ),
                ExampleSpec(
                    id="note_get_common",
                    title="搜索结果回传笔记链接",
                    category=ExampleCategory.COMMON,
                    command="flow-xhs --format json note get --note-url https://www.xiaohongshu.com/explore/n1?xsec_token=token&xsec_source=pc_search",
                    machine_friendly=True,
                ),
                ExampleSpec(
                    id="note_get_automation",
                    title="从输入流获取笔记详情",
                    category=ExampleCategory.AUTOMATION,
                    command="cat payload.json | flow-xhs --format json note get --input -",
                    machine_friendly=True,
                ),
            ),
            related_commands=("flow-xhs note search", "flow-xhs user get"),
            help=CommandHelpSpec(show_aliases=False),
        ),
        _command(
            id="note_search",
            path=("note", "search"),
            summary="搜索笔记列表。",
            description="支持 canonical 枚举值与结构化 alias；输出帮助始终展示 canonical 值。",
            success_command="note.search",
            aliases=(_alias("search", "note", replacement="flow-xhs note search"),),
            parameters=(
                _command_option(
                    id="keyword",
                    cli_name="--keyword",
                    group=ParameterGroup.CONDITIONAL_INPUTS,
                    summary="搜索关键词。",
                ),
                _command_option(
                    id="size",
                    cli_name="--size",
                    group=ParameterGroup.OPTIONAL_INPUTS,
                    summary="返回数量。",
                    value_kind=ValueKind.INTEGER,
                    min_value=SEARCH_SIZE_MIN,
                    max_value=SEARCH_SIZE_MAX,
                    default=DefaultSpec(
                        kind=DefaultKind.STATIC,
                        value=NOTE_SEARCH_SIZE_DEFAULT,
                        display_value=str(NOTE_SEARCH_SIZE_DEFAULT),
                    ),
                ),
                _command_option(
                    id="sort_by",
                    cli_name="--sort-by",
                    group=ParameterGroup.OPTIONAL_INPUTS,
                    summary="排序方式。",
                    value_kind=ValueKind.ENUM,
                    allowed_values=_filter_allowed_values("sort_by"),
                ),
                _command_option(
                    id="note_type",
                    cli_name="--note-type",
                    group=ParameterGroup.OPTIONAL_INPUTS,
                    summary="内容类型。",
                    value_kind=ValueKind.ENUM,
                    allowed_values=_filter_allowed_values("note_type"),
                ),
                _command_option(
                    id="publish_time",
                    cli_name="--publish-time",
                    group=ParameterGroup.OPTIONAL_INPUTS,
                    summary="发布时间过滤。",
                    value_kind=ValueKind.ENUM,
                    allowed_values=_filter_allowed_values("publish_time"),
                ),
                _command_option(
                    id="search_scope",
                    cli_name="--search-scope",
                    group=ParameterGroup.OPTIONAL_INPUTS,
                    summary="搜索范围。",
                    value_kind=ValueKind.ENUM,
                    allowed_values=_filter_allowed_values("search_scope"),
                ),
                _command_option(
                    id="location",
                    cli_name="--location",
                    group=ParameterGroup.OPTIONAL_INPUTS,
                    summary="地理范围。",
                    value_kind=ValueKind.ENUM,
                    allowed_values=_filter_allowed_values("location"),
                ),
                _input_source_option(
                    summary="JSON 输入文件路径或标准输入。",
                    fields="keyword,size,sort_by,note_type,publish_time,search_scope,location",
                ),
            ),
            constraints=(
                ConstraintSpec(
                    id="note_search_requires_keyword_from_cli_or_input",
                    type=ConstraintType.ONE_OF,
                    severity=ConstraintSeverity.ERROR,
                    targets=("keyword", "input"),
                    message="需要通过 --keyword 或 --input 提供 keyword。",
                ),
                ConstraintSpec(
                    id="note_search_filters_support_aliases",
                    type=ConstraintType.RANGE,
                    severity=ConstraintSeverity.WARNING,
                    targets=("sort_by", "note_type", "publish_time", "search_scope", "location"),
                    message="搜索筛选参数支持中文别名，但帮助与契约始终展示 canonical 值。",
                    help_text="搜索筛选参数支持中文别名输入，输出帮助仅展示 canonical 值和结构化 aliases。",
                ),
                ConstraintSpec(
                    id="note_search_size_range",
                    type=ConstraintType.RANGE,
                    severity=ConstraintSeverity.ERROR,
                    targets=("size",),
                    message="size 必须在 1 到 100 之间。",
                    help_text=f"`--size` 取值范围为 {SEARCH_SIZE_MIN}-{SEARCH_SIZE_MAX}。",
                ),
            ),
            examples=(
                ExampleSpec(
                    id="note_search_minimal",
                    title="最小搜索示例",
                    category=ExampleCategory.MINIMAL,
                    command="flow-xhs --format json note search --keyword 摄影",
                    machine_friendly=True,
                ),
                ExampleSpec(
                    id="note_search_common",
                    title="带筛选的搜索示例",
                    category=ExampleCategory.COMMON,
                    command="flow-xhs --format json note search --keyword 摄影 --size 10 --sort-by latest --note-type image_text",
                    machine_friendly=True,
                ),
                ExampleSpec(
                    id="note_search_automation",
                    title="从输入文件搜索",
                    category=ExampleCategory.AUTOMATION,
                    command="cat payload.json | flow-xhs --format json note search --input -",
                    machine_friendly=True,
                ),
            ),
            related_commands=("flow-xhs note get", "flow-xhs user search"),
            help=CommandHelpSpec(show_aliases=False),
            output=OutputSpec(
                success_command="note.search",
                success_schema_ref=SUCCESS_SCHEMA,
                error_schema_ref=ERROR_SCHEMA,
                data_shape_summary="笔记搜索结果列表对象。",
            ),
        ),
        _command(
            id="note_publish",
            path=("note", "publish"),
            summary="发布图文或视频笔记。",
            description="CLI 参数优先于 `--input` JSON；发布账号始终来自当前 activate 账号环境，若未设置则回退到唯一已登录账号。",
            success_command="note.publish",
            aliases=(_alias("publish", replacement="flow-xhs note publish"),),
            parameters=(
                NOTE_PUBLISH_TARGET,
                ParameterSpec(
                    id="image_list",
                    name="image_list",
                    cli_names=("--image-list",),
                    scope=ParameterScope.COMMAND,
                    kind=ParameterKind.OPTION,
                    group=ParameterGroup.CONDITIONAL_INPUTS,
                    summary="图片路径或 URL 列表。",
                    description=None,
                    value_type=ValueTypeSpec(kind=ValueKind.ARRAY, item_kind=ValueKind.STRING, separator=",", nullable=True),
                    requiredness=RequirednessSpec(
                        mode=RequirednessMode.CONDITIONAL,
                        condition=ConditionExpr(op="eq", field="target", value="image"),
                        message="当 --target=image 时必须提供 --image-list。",
                    ),
                    input_bindings=(
                        InputSourceBinding(source=InputSourceKind.CLI, priority=1),
                        InputSourceBinding(source=InputSourceKind.INPUT_JSON, field_name="image_list", priority=2),
                    ),
                ),
                ParameterSpec(
                    id="video",
                    name="video",
                    cli_names=("--video",),
                    scope=ParameterScope.COMMAND,
                    kind=ParameterKind.OPTION,
                    group=ParameterGroup.CONDITIONAL_INPUTS,
                    summary="视频路径或 URL。",
                    description=None,
                    value_type=ValueTypeSpec(kind=ValueKind.STRING, nullable=True),
                    requiredness=RequirednessSpec(
                        mode=RequirednessMode.CONDITIONAL,
                        condition=ConditionExpr(op="eq", field="target", value="video"),
                        message="当 --target=video 时必须提供 --video。",
                    ),
                    input_bindings=(
                        InputSourceBinding(source=InputSourceKind.CLI, priority=1),
                        InputSourceBinding(source=InputSourceKind.INPUT_JSON, field_name="video", priority=2),
                    ),
                ),
                _command_option(
                    id="title",
                    cli_name="--title",
                    group=ParameterGroup.OPTIONAL_INPUTS,
                    summary="标题。",
                    default=DefaultSpec(kind=DefaultKind.STATIC, value="", display_value='""'),
                ),
                _command_option(
                    id="content",
                    cli_name="--content",
                    group=ParameterGroup.OPTIONAL_INPUTS,
                    summary="正文。",
                    default=DefaultSpec(kind=DefaultKind.STATIC, value="", display_value='""'),
                ),
                ParameterSpec(
                    id="tags",
                    name="tags",
                    cli_names=("--tags",),
                    scope=ParameterScope.COMMAND,
                    kind=ParameterKind.OPTION,
                    group=ParameterGroup.OPTIONAL_INPUTS,
                    summary="标签列表。",
                    description="支持 CSV 输入，`--input` 中也支持 JSON 数组。",
                    value_type=ValueTypeSpec(kind=ValueKind.ARRAY, item_kind=ValueKind.STRING, separator=",", nullable=True),
                    requiredness=RequirednessSpec(mode=RequirednessMode.OPTIONAL),
                    input_bindings=(
                        InputSourceBinding(source=InputSourceKind.CLI, priority=1),
                        InputSourceBinding(source=InputSourceKind.INPUT_JSON, field_name="tags", priority=2),
                    ),
                ),
                ParameterSpec(
                    id="schedule_at",
                    name="schedule_at",
                    cli_names=("--schedule-at",),
                    scope=ParameterScope.COMMAND,
                    kind=ParameterKind.OPTION,
                    group=ParameterGroup.OPTIONAL_INPUTS,
                    summary="定时发布时间。",
                    description="若提供，必须符合 RFC3339 并包含时区。",
                    value_type=ValueTypeSpec(kind=ValueKind.TIMESTAMP, format="rfc3339", nullable=True),
                    requiredness=RequirednessSpec(mode=RequirednessMode.OPTIONAL),
                    input_bindings=(
                        InputSourceBinding(source=InputSourceKind.CLI, priority=1),
                        InputSourceBinding(source=InputSourceKind.INPUT_JSON, field_name="schedule_at", priority=2),
                    ),
                ),
                _input_source_option(
                    summary="JSON 输入文件路径或标准输入。",
                    fields="target,image_list,video,title,content,tags,schedule_at",
                ),
            ),
            constraints=(
                ConstraintSpec(
                    id="note_publish_requires_image_list",
                    type=ConstraintType.WHEN_THEN_REQUIRES,
                    severity=ConstraintSeverity.ERROR,
                    targets=("target", "image_list"),
                    message="当 target=image 时必须提供 image_list。",
                    condition=ConditionExpr(op="eq", field="target", value="image"),
                ),
                ConstraintSpec(
                    id="note_publish_requires_video",
                    type=ConstraintType.WHEN_THEN_REQUIRES,
                    severity=ConstraintSeverity.ERROR,
                    targets=("target", "video"),
                    message="当 target=video 时必须提供 video。",
                    condition=ConditionExpr(op="eq", field="target", value="video"),
                ),
                ConstraintSpec(
                    id="note_publish_video_image_list_is_compat_only",
                    type=ConstraintType.WHEN_THEN_FORBIDS,
                    severity=ConstraintSeverity.WARNING,
                    targets=("target", "image_list", "video"),
                    message="当 target=video 且 video 缺失但 image_list 仅有一项时，视为兼容路径而非主路径。",
                    help_text="当 --target=video 时，应优先使用 --video；单项 --image-list 仅作为兼容输入路径。",
                    condition=ConditionExpr(op="eq", field="target", value="video"),
                ),
                ConstraintSpec(
                    id="note_publish_schedule_at_must_be_rfc3339",
                    type=ConstraintType.RANGE,
                    severity=ConstraintSeverity.ERROR,
                    targets=("schedule_at",),
                    message="schedule_at 若提供，必须符合 RFC3339 并包含时区。",
                    help_text="当提供 --schedule-at 时，值必须为 RFC3339 时间，例如 2025-03-04T10:30:00+08:00。",
                ),
            ),
            examples=(
                ExampleSpec(
                    id="note_publish_minimal_image",
                    title="发布最小图文示例",
                    category=ExampleCategory.MINIMAL,
                    command="flow-xhs --format json note publish --target image --image-list a.jpg",
                    machine_friendly=True,
                ),
                ExampleSpec(
                    id="note_publish_common_video",
                    title="发布视频示例",
                    category=ExampleCategory.COMMON,
                    command="flow-xhs --format json note publish --target video --video a.mp4 --title 标题",
                    machine_friendly=True,
                ),
                ExampleSpec(
                    id="note_publish_automation",
                    title="从输入流发布",
                    category=ExampleCategory.AUTOMATION,
                    command="cat payload.json | flow-xhs --format json note publish --input -",
                    machine_friendly=True,
                ),
            ),
            related_commands=("flow-xhs account login", "flow-xhs account status"),
            help=CommandHelpSpec(show_aliases=False),
            output=OutputSpec(
                success_command="note.publish",
                success_schema_ref=SUCCESS_SCHEMA,
                error_schema_ref=ERROR_SCHEMA,
                data_shape_summary="发布结果对象，至少包含内容 ID 或发布任务结果。",
            ),
        ),
        _command(
            id="user_get",
            path=("user", "get"),
            summary="获取单个用户主页详情。",
            description=USER_GET_DESCRIPTION,
            success_command="user.get",
            aliases=(_alias("user", replacement="flow-xhs user get"),),
            parameters=(
                _command_option(
                    id="user_url",
                    cli_name="--user-url",
                    group=ParameterGroup.CONDITIONAL_INPUTS,
                    summary="用户主页 URL。",
                    description="推荐直接传入完整用户链接，自动解析 user_id、xsec_token、xsec_source。",
                ),
                _input_source_option(
                    summary="JSON 输入文件路径或标准输入。",
                    fields="user_url",
                    description="字段示例：user_url。",
                ),
            ),
            constraints=(
                ConstraintSpec(
                    id="user_get_requires_user_url_from_cli_or_input",
                    type=ConstraintType.ONE_OF,
                    severity=ConstraintSeverity.ERROR,
                    targets=("user_url", "input"),
                    message="需要通过 --user-url 或 --input 提供 user_url。",
                ),
            ),
            examples=(
                ExampleSpec(
                    id="user_get_minimal",
                    title="最小用户详情示例",
                    category=ExampleCategory.MINIMAL,
                    command="flow-xhs --format json user get --user-url https://www.xiaohongshu.com/user/profile/u1?xsec_source=pc_feed",
                    machine_friendly=True,
                ),
                ExampleSpec(
                    id="user_get_common",
                    title="笔记作者详情示例",
                    category=ExampleCategory.COMMON,
                    command="flow-xhs --format json user get --user-url https://www.xiaohongshu.com/user/profile/u1?xsec_token=token&xsec_source=pc_note",
                    machine_friendly=True,
                ),
                ExampleSpec(
                    id="user_get_automation",
                    title="从输入文件获取用户详情",
                    category=ExampleCategory.AUTOMATION,
                    command="cat payload.json | flow-xhs --format json user get --input -",
                    machine_friendly=True,
                ),
            ),
            related_commands=("flow-xhs user search", "flow-xhs note get"),
            help=CommandHelpSpec(show_aliases=False),
            output=OutputSpec(
                success_command="user.get",
                success_schema_ref=SUCCESS_SCHEMA,
                error_schema_ref=ERROR_SCHEMA,
                data_shape_summary="单个用户主页详情对象。",
            ),
        ),
        _command(
            id="user_search",
            path=("user", "search"),
            summary="搜索用户列表。",
            description="CLI 参数优先于 `--input` JSON；帮助展示 canonical 参数和约束。",
            success_command="user.search",
            aliases=(_alias("search", "user", replacement="flow-xhs user search"),),
            parameters=(
                _command_option(
                    id="keyword",
                    cli_name="--keyword",
                    group=ParameterGroup.CONDITIONAL_INPUTS,
                    summary="搜索关键词。",
                ),
                _command_option(
                    id="size",
                    cli_name="--size",
                    group=ParameterGroup.OPTIONAL_INPUTS,
                    summary="返回数量。",
                    value_kind=ValueKind.INTEGER,
                    min_value=SEARCH_SIZE_MIN,
                    max_value=SEARCH_SIZE_MAX,
                    default=DefaultSpec(
                        kind=DefaultKind.STATIC,
                        value=USER_SEARCH_SIZE_DEFAULT,
                        display_value=str(USER_SEARCH_SIZE_DEFAULT),
                    ),
                ),
                _input_source_option(
                    summary="JSON 输入文件路径或标准输入。",
                    fields="keyword,size",
                ),
            ),
            constraints=(
                ConstraintSpec(
                    id="user_search_requires_keyword_from_cli_or_input",
                    type=ConstraintType.ONE_OF,
                    severity=ConstraintSeverity.ERROR,
                    targets=("keyword", "input"),
                    message="需要通过 --keyword 或 --input 提供 keyword。",
                ),
                ConstraintSpec(
                    id="user_search_size_range",
                    type=ConstraintType.RANGE,
                    severity=ConstraintSeverity.ERROR,
                    targets=("size",),
                    message="size 必须在 1 到 100 之间。",
                    help_text=f"`--size` 取值范围为 {SEARCH_SIZE_MIN}-{SEARCH_SIZE_MAX}。",
                ),
            ),
            examples=(
                ExampleSpec(
                    id="user_search_minimal",
                    title="最小用户搜索示例",
                    category=ExampleCategory.MINIMAL,
                    command="flow-xhs --format json user search --keyword 摄影",
                    machine_friendly=True,
                ),
                ExampleSpec(
                    id="user_search_common",
                    title="常见用户搜索示例",
                    category=ExampleCategory.COMMON,
                    command="flow-xhs --format json user search --keyword 摄影 --size 10",
                    machine_friendly=True,
                ),
                ExampleSpec(
                    id="user_search_automation",
                    title="从输入流搜索用户",
                    category=ExampleCategory.AUTOMATION,
                    command="cat payload.json | flow-xhs --format json user search --input -",
                    machine_friendly=True,
                ),
            ),
            related_commands=("flow-xhs note search", "flow-xhs user get"),
            help=CommandHelpSpec(show_aliases=False),
            output=OutputSpec(
                success_command="user.search",
                success_schema_ref=SUCCESS_SCHEMA,
                error_schema_ref=ERROR_SCHEMA,
                data_shape_summary="用户搜索结果列表对象。",
            ),
        ),
        _command(
            id="user_self",
            path=("user", "self"),
            summary="查询当前登录账号对应的用户信息。",
            description="适用于通过当前 activate 账号环境反查自身用户主页信息。",
            success_command="user.self",
            examples=(
                ExampleSpec(
                    id="user_self_minimal",
                    title="查询当前账号自身信息",
                    category=ExampleCategory.MINIMAL,
                    command="flow-xhs --format json user self",
                    machine_friendly=True,
                ),
                ExampleSpec(
                    id="user_self_common",
                    title="带 trace_id 查询自身信息",
                    category=ExampleCategory.COMMON,
                    command="flow-xhs --format json --trace-id req-001 user self",
                    machine_friendly=True,
                ),
                ExampleSpec(
                    id="user_self_automation",
                    title="自动化系统查询自身信息",
                    category=ExampleCategory.AUTOMATION,
                    command="flow-xhs --format json user self",
                    machine_friendly=True,
                ),
            ),
            related_commands=("flow-xhs account status", "flow-xhs user get"),
            help=CommandHelpSpec(show_aliases=False),
            output=OutputSpec(
                success_command="user.self",
                success_schema_ref=SUCCESS_SCHEMA,
                error_schema_ref=ERROR_SCHEMA,
                data_shape_summary="当前登录账号对应的用户信息对象。",
            ),
        ),
    ),
    topics=(
        DocTopicSpec(
            id="input-precedence",
            title="Input Precedence",
            summary="解释 CLI、--input 和默认值的优先级。",
            content_markdown="CLI 参数优先于 `--input` JSON，同名字段最终再回落到默认值。",
        ),
        DocTopicSpec(
            id="compatibility-aliases",
            title="Compatibility Aliases",
            summary="说明历史路径与 canonical 路径的关系。",
            content_markdown="历史 alias 继续兼容，但主帮助和参考文档只推荐 canonical 路径。",
        ),
        DocTopicSpec(
            id="json-output",
            title="JSON Output",
            summary="说明 `--format json` 与事件输出通道。",
            content_markdown="自动化调用推荐 `--format json`，过程事件仍通过 `stderr` 输出。",
        ),
    ),
    metadata={
        "quick_start": (
            "flow-xhs init",
            "flow-xhs --format json doctor",
            "flow-xhs --format json note search --keyword 摄影 --size 10",
            "flow-xhs --format json note get --note-url <url>",
        ),
        "groups": {
            ("account",): {
                "summary": "账号管理命令。",
                "description": "包含登录、状态检查、账号列表以及启停删除操作。",
            },
            ("note",): {
                "summary": "笔记查询与发布命令。",
                "description": "包含笔记详情、笔记搜索和笔记发布能力。",
            },
            ("search",): {
                "summary": "历史兼容搜索入口。",
                "description": "推荐改用 `flow-xhs note search` 或 `flow-xhs user search`。",
                "canonical": False,
                "deprecated": True,
            },
            ("user",): {
                "summary": "用户查询命令。",
                "description": "包含用户详情、用户搜索和当前登录用户信息。",
            },
        }
    },
)

validate_cli_spec(FLOW_XHS_SPEC)
