//! L6 tests for the agent DTO (de)serialisation contract: camelCase on the
//! wire, embedded [`Agent`] shape preserved, `parse_agent_id` error behaviour,
//! and `From<LaunchAgentOutput>` for [`TerminalSessionDto`].

use app_tauri_lib::dto::{
    parse_agent_id, AgentDto, AgentListDto, ConversationDetailsDto, CreateAgentRequestDto,
    InspectConversationRequestDto, LaunchAgentRequestDto, LiveAgentListDto, TerminalSessionDto,
    UpdateAgentContextRequestDto,
};
use application::AppError;
use application::{
    AgentTicketState, AgentWorkState, CreateAgentOutput, InspectConversationOutput,
    LaunchAgentOutput, ListAgentsOutput, LiveSessionKind, LiveSessionSnapshot, LiveWorkSession,
    ProjectWorkState, TicketWorkSource, TicketWorkStatus,
};
use domain::ids::{AgentId, NodeId, ProfileId, SessionId};
use domain::ports::ConversationDetails;
use domain::terminal::{PtySize, SessionKind, SessionStatus, TerminalSession};
use domain::{Agent, AgentOrigin, ProjectPath};
use serde_json::json;
use uuid::Uuid;

/// Helper: build a minimal validated [`Agent`].
fn make_agent(agent_uuid: u128, profile_uuid: u128) -> Agent {
    Agent::new(
        AgentId::from_uuid(Uuid::from_u128(agent_uuid)),
        "My Agent",
        "agents/my-agent.md",
        ProfileId::from_uuid(Uuid::from_u128(profile_uuid)),
        AgentOrigin::Scratch,
        false,
    )
    .expect("valid agent")
}

// ---------------------------------------------------------------------------
// AgentDto / AgentListDto serialisation
// ---------------------------------------------------------------------------

#[test]
fn agent_dto_serialises_camelcase() {
    let agent = make_agent(1, 2);
    let dto = AgentDto(agent.clone());
    let v = serde_json::to_value(&dto).unwrap();

    assert_eq!(v["id"], agent.id.to_string());
    assert_eq!(v["name"], "My Agent");
    assert_eq!(v["contextPath"], "agents/my-agent.md", "camelCase key");
    assert_eq!(
        v["profileId"],
        ProfileId::from_uuid(Uuid::from_u128(2)).to_string()
    );
    assert_eq!(v["synchronized"], false);
    // origin: tagged `{ "type": "scratch" }`
    assert_eq!(v["origin"]["type"], "scratch");
    // no snake_case leak
    assert!(v.get("context_path").is_none());
    assert!(v.get("profile_id").is_none());
}

#[test]
fn agent_list_dto_is_transparent_array() {
    let out = ListAgentsOutput {
        agents: vec![make_agent(1, 2), make_agent(3, 4)],
    };
    let dto = AgentListDto::from(out);
    let v = serde_json::to_value(&dto).unwrap();
    let arr = v.as_array().expect("transparent array");
    assert_eq!(arr.len(), 2);
    assert_eq!(arr[0]["name"], "My Agent");
}

#[test]
fn create_agent_output_maps_to_agent_dto() {
    let agent = make_agent(5, 6);
    let out = CreateAgentOutput {
        agent: agent.clone(),
    };
    let dto = AgentDto::from(out);
    assert_eq!(dto.0.id, agent.id);
}

// ---------------------------------------------------------------------------
// Request DTO deserialisation
// ---------------------------------------------------------------------------

#[test]
fn create_agent_request_deserialises_camelcase() {
    let project_id = Uuid::from_u128(10).to_string();
    let profile_id = Uuid::from_u128(20).to_string();
    let raw = json!({
        "projectId": project_id,
        "name": "Backend Dev",
        "profileId": profile_id,
        "initialContent": "# Hello"
    });
    let dto: CreateAgentRequestDto = serde_json::from_value(raw).unwrap();
    assert_eq!(dto.project_id, project_id);
    assert_eq!(dto.name, "Backend Dev");
    assert_eq!(dto.profile_id, profile_id);
    assert_eq!(dto.initial_content.as_deref(), Some("# Hello"));
}

#[test]
fn create_agent_request_initial_content_defaults_to_none() {
    let raw = json!({
        "projectId": Uuid::nil().to_string(),
        "name": "Agent",
        "profileId": Uuid::nil().to_string()
    });
    let dto: CreateAgentRequestDto = serde_json::from_value(raw).unwrap();
    assert!(dto.initial_content.is_none());
}

#[test]
fn update_agent_context_request_deserialises_camelcase() {
    let raw = json!({
        "projectId": Uuid::nil().to_string(),
        "agentId": Uuid::nil().to_string(),
        "content": "# Updated"
    });
    let dto: UpdateAgentContextRequestDto = serde_json::from_value(raw).unwrap();
    assert_eq!(dto.content, "# Updated");
}

#[test]
fn launch_agent_request_deserialises_camelcase() {
    let project_id = Uuid::from_u128(1).to_string();
    let agent_id = Uuid::from_u128(2).to_string();
    let raw = json!({
        "projectId": project_id,
        "agentId": agent_id,
        "rows": 24,
        "cols": 80
    });
    let dto: LaunchAgentRequestDto = serde_json::from_value(raw).unwrap();
    assert_eq!(dto.rows, 24);
    assert_eq!(dto.cols, 80);
    assert_eq!(dto.project_id, project_id);
    assert_eq!(dto.agent_id, agent_id);
    // Omitting the resume id defaults to None (a fresh cell).
    assert_eq!(dto.conversation_id, None);
    // Omitting the node id defaults to None (a fresh node is minted backend-side).
    assert_eq!(dto.node_id, None);
}

#[test]
fn launch_agent_request_carries_node_id() {
    let node_id = Uuid::from_u128(33).to_string();
    let raw = json!({
        "projectId": Uuid::from_u128(1).to_string(),
        "agentId": Uuid::from_u128(2).to_string(),
        "rows": 24,
        "cols": 80,
        "nodeId": node_id
    });
    let dto: LaunchAgentRequestDto = serde_json::from_value(raw).unwrap();
    // The hosting cell reaches the backend so the singleton guard can enforce
    // "one live session per agent".
    assert_eq!(dto.node_id.as_deref(), Some(node_id.as_str()));
    // No snake_case leak on the wire.
}

// ---------------------------------------------------------------------------
// AGENT_ALREADY_RUNNING error code + LiveAgentListDto (T2/T3)
// ---------------------------------------------------------------------------

#[test]
fn agent_already_running_error_code_is_stable() {
    let err = AppError::AgentAlreadyRunning {
        agent_id: AgentId::from_uuid(Uuid::from_u128(2)),
        node_id: NodeId::from_uuid(Uuid::from_u128(3)),
    };
    let dto = app_tauri_lib::dto::ErrorDto::from(err);
    // Option A: a stable code + a text message (the node is NOT enriched into the
    // ErrorDto — the frontend branches on the code alone).
    assert_eq!(dto.code, "AGENT_ALREADY_RUNNING");
    assert!(
        dto.message.contains("already running"),
        "message: {}",
        dto.message
    );
}

#[test]
fn live_agent_list_dto_serialises_camelcase_array() {
    let agent_a = AgentId::from_uuid(Uuid::from_u128(11));
    let node_a = NodeId::from_uuid(Uuid::from_u128(21));
    let session_a = domain::SessionId::from_uuid(Uuid::from_u128(31));
    let dto = LiveAgentListDto::from_snapshots(vec![LiveSessionSnapshot {
        agent_id: agent_a,
        node_id: node_a,
        session_id: session_a,
        kind: LiveSessionKind::Pty,
    }]);
    let v = serde_json::to_value(&dto).unwrap();
    let arr = v.as_array().expect("transparent array");
    assert_eq!(arr.len(), 1);
    assert_eq!(arr[0]["agentId"], agent_a.to_string());
    assert_eq!(arr[0]["nodeId"], node_a.to_string());
    assert_eq!(arr[0]["sessionId"], session_a.to_string());
    assert_eq!(arr[0]["kind"], "pty");
    // No snake_case leak.
    assert!(arr[0].get("agent_id").is_none());
    assert!(arr[0].get("node_id").is_none());
}

#[test]
fn project_work_state_dto_serialises_live_and_busy_camelcase() {
    let agent = AgentId::from_uuid(Uuid::from_u128(11));
    let profile = ProfileId::from_uuid(Uuid::from_u128(12));
    let node = NodeId::from_uuid(Uuid::from_u128(13));
    let session = SessionId::from_uuid(Uuid::from_u128(14));
    let ticket = domain::TicketId::from_uuid(Uuid::from_u128(15));
    let dto = app_tauri_lib::dto::ProjectWorkStateDto::from(ProjectWorkState {
        agents: vec![AgentWorkState {
            agent_id: agent,
            name: "Worker".to_owned(),
            profile_id: profile,
            live: Some(LiveWorkSession {
                node_id: node,
                session_id: session,
                kind: LiveSessionKind::Structured,
            }),
            busy: domain::AgentBusyState::Busy {
                ticket,
                since_ms: 1_234,
            },
            tickets: vec![],
            background_tasks: vec![],
        }],
        conversations: vec![],
    });

    let v = serde_json::to_value(&dto).unwrap();
    assert_eq!(v["agents"][0]["agentId"], agent.to_string());
    assert_eq!(v["agents"][0]["profileId"], profile.to_string());
    assert_eq!(v["agents"][0]["live"]["nodeId"], node.to_string());
    assert_eq!(v["agents"][0]["live"]["sessionId"], session.to_string());
    assert_eq!(v["agents"][0]["live"]["kind"], "structured");
    assert_eq!(v["agents"][0]["busy"]["state"], "busy");
    assert_eq!(v["agents"][0]["busy"]["ticket"], ticket.to_string());
    assert_eq!(v["agents"][0]["busy"]["sinceMs"], 1_234);
    assert_eq!(v["agents"][0]["tickets"], json!([]));
    assert!(v["agents"][0].get("agent_id").is_none());
    assert!(v["agents"][0]["live"].get("session_id").is_none());
    assert!(v["agents"][0]["busy"].get("since_ms").is_none());
}

#[test]
fn project_work_state_dto_serialises_tickets_camelcase() {
    let agent = AgentId::from_uuid(Uuid::from_u128(11));
    let profile = ProfileId::from_uuid(Uuid::from_u128(12));
    let requester = AgentId::from_uuid(Uuid::from_u128(20));
    let head_ticket = domain::TicketId::from_uuid(Uuid::from_u128(30));
    let next_ticket = domain::TicketId::from_uuid(Uuid::from_u128(31));
    let conversation = domain::ConversationId::from_uuid(Uuid::from_u128(40));
    let dto = app_tauri_lib::dto::ProjectWorkStateDto::from(ProjectWorkState {
        agents: vec![AgentWorkState {
            agent_id: agent,
            name: "Worker".to_owned(),
            profile_id: profile,
            live: None,
            busy: domain::AgentBusyState::Idle,
            tickets: vec![
                AgentTicketState {
                    ticket_id: head_ticket,
                    conversation_id: conversation,
                    position: 0,
                    status: TicketWorkStatus::InProgress,
                    source: TicketWorkSource::Agent {
                        agent_id: requester,
                    },
                    requester_label: "Main".to_owned(),
                    task_preview: "Analyser le module".to_owned(),
                    task_len: 842,
                },
                AgentTicketState {
                    ticket_id: next_ticket,
                    conversation_id: conversation,
                    position: 1,
                    status: TicketWorkStatus::Queued,
                    source: TicketWorkSource::Human,
                    requester_label: "User".to_owned(),
                    task_preview: "Vérifier".to_owned(),
                    task_len: 8,
                },
            ],
            background_tasks: vec![],
        }],
        conversations: vec![],
    });

    let v = serde_json::to_value(&dto).unwrap();
    let head = &v["agents"][0]["tickets"][0];
    assert_eq!(head["ticketId"], head_ticket.to_string());
    assert_eq!(head["conversationId"], conversation.to_string());
    assert_eq!(head["position"], 0);
    assert_eq!(head["status"], "inProgress");
    assert_eq!(head["source"]["kind"], "agent");
    assert_eq!(head["source"]["agentId"], requester.to_string());
    assert_eq!(head["requesterLabel"], "Main");
    assert_eq!(head["taskPreview"], "Analyser le module");
    assert_eq!(head["taskLen"], 842);
    // snake_case must not leak.
    assert!(head.get("ticket_id").is_none());
    assert!(head.get("task_preview").is_none());
    assert!(head["source"].get("agent_id").is_none());

    let next = &v["agents"][0]["tickets"][1];
    assert_eq!(next["status"], "queued");
    assert_eq!(next["source"]["kind"], "human");
    assert!(next["source"].get("agentId").is_none());
    assert_eq!(next["requesterLabel"], "User");
}

#[test]
fn project_work_state_dto_serialises_conversations_camelcase() {
    use application::{
        ConversationPreviewStatus, ConversationTurnWorkPreview, ConversationWorkSummary,
    };
    use domain::{ConversationId, TurnId, TurnRole};

    let requester = AgentId::from_uuid(Uuid::from_u128(20));
    let ready_conv = ConversationId::from_uuid(Uuid::from_u128(40));
    let missing_conv = ConversationId::from_uuid(Uuid::from_u128(41));
    let up_to = TurnId::from_uuid(Uuid::from_u128(50));

    let dto = app_tauri_lib::dto::ProjectWorkStateDto::from(ProjectWorkState {
        agents: vec![],
        conversations: vec![
            ConversationWorkSummary {
                conversation_id: ready_conv,
                status: ConversationPreviewStatus::Ready,
                objective_preview: Some("Livrer le lot C".to_owned()),
                summary_preview: Some("Résumé du fil".to_owned()),
                summary_len: 1024,
                up_to: Some(up_to),
                recent_turns: vec![],
            },
            ConversationWorkSummary {
                conversation_id: missing_conv,
                status: ConversationPreviewStatus::Missing,
                objective_preview: None,
                summary_preview: None,
                summary_len: 0,
                up_to: None,
                recent_turns: vec![ConversationTurnWorkPreview {
                    role: TurnRole::Prompt,
                    source: TicketWorkSource::Agent {
                        agent_id: requester,
                    },
                    at_ms: 1_700,
                    text_preview: "Analyse le module".to_owned(),
                    text_len: 17,
                }],
            },
        ],
    });

    let v = serde_json::to_value(&dto).unwrap();
    let ready = &v["conversations"][0];
    assert_eq!(ready["conversationId"], ready_conv.to_string());
    assert_eq!(ready["status"], "ready");
    assert_eq!(ready["objectivePreview"], "Livrer le lot C");
    assert_eq!(ready["summaryPreview"], "Résumé du fil");
    assert_eq!(ready["summaryLen"], 1024);
    assert_eq!(ready["upTo"], up_to.to_string());
    assert_eq!(ready["recentTurns"], json!([]));
    // No snake_case leak.
    assert!(ready.get("conversation_id").is_none());
    assert!(ready.get("summary_preview").is_none());
    assert!(ready.get("up_to").is_none());

    let missing = &v["conversations"][1];
    assert_eq!(missing["status"], "missing");
    // Absent previews surface as null (predictable, not omitted).
    assert!(missing["summaryPreview"].is_null());
    assert!(missing["upTo"].is_null());
    let turn = &missing["recentTurns"][0];
    assert_eq!(turn["role"], "prompt");
    assert_eq!(turn["source"]["kind"], "agent");
    assert_eq!(turn["source"]["agentId"], requester.to_string());
    assert_eq!(turn["atMs"], 1_700);
    assert_eq!(turn["textPreview"], "Analyse le module");
    assert_eq!(turn["textLen"], 17);
    assert!(turn.get("at_ms").is_none());
    assert!(turn.get("text_preview").is_none());
}

#[test]
fn launch_agent_request_carries_conversation_id_for_resume() {
    let raw = json!({
        "projectId": Uuid::from_u128(1).to_string(),
        "agentId": Uuid::from_u128(2).to_string(),
        "rows": 24,
        "cols": 80,
        "conversationId": "resume-me"
    });
    let dto: LaunchAgentRequestDto = serde_json::from_value(raw).unwrap();
    // The leaf's persisted id reaches the backend so the launch resumes (T4b).
    assert_eq!(dto.conversation_id.as_deref(), Some("resume-me"));
}

// ---------------------------------------------------------------------------
// InspectConversation DTOs (T7)
// ---------------------------------------------------------------------------

#[test]
fn inspect_conversation_request_deserialises_camelcase() {
    let raw = json!({
        "projectId": Uuid::from_u128(1).to_string(),
        "agentId": Uuid::from_u128(2).to_string(),
        "conversationId": "conv-7"
    });
    let dto: InspectConversationRequestDto = serde_json::from_value(raw).unwrap();
    assert_eq!(dto.project_id, Uuid::from_u128(1).to_string());
    assert_eq!(dto.agent_id, Uuid::from_u128(2).to_string());
    assert_eq!(dto.conversation_id, "conv-7");
}

#[test]
fn conversation_details_dto_serialises_camelcase_when_present() {
    let out = InspectConversationOutput {
        details: ConversationDetails {
            last_topic: Some("refactor parser".to_owned()),
            token_count: Some(1234),
        },
    };
    let dto = ConversationDetailsDto::from(out);
    let v = serde_json::to_value(&dto).unwrap();
    assert_eq!(v["lastTopic"], "refactor parser");
    assert_eq!(v["tokenCount"], 1234);
    // No snake_case leak.
    assert!(v.get("last_topic").is_none());
    assert!(v.get("token_count").is_none());
}

#[test]
fn conversation_details_dto_omits_fields_when_none() {
    let out = InspectConversationOutput {
        details: ConversationDetails {
            last_topic: None,
            token_count: None,
        },
    };
    let dto = ConversationDetailsDto::from(out);
    let v = serde_json::to_value(&dto).unwrap();
    // Both optional fields are omitted from the wire (absent, not null).
    assert!(v.get("lastTopic").is_none(), "absent topic ⇒ field omitted");
    assert!(
        v.get("tokenCount").is_none(),
        "absent tokens ⇒ field omitted"
    );
    assert_eq!(v, json!({}), "fully degraded ⇒ empty object");
}

// ---------------------------------------------------------------------------
// parse_agent_id
// ---------------------------------------------------------------------------

#[test]
fn parse_agent_id_accepts_uuid() {
    let id = Uuid::from_u128(99);
    assert_eq!(
        parse_agent_id(&id.to_string()).unwrap(),
        AgentId::from_uuid(id)
    );
}

#[test]
fn parse_agent_id_rejects_garbage() {
    let err = parse_agent_id("not-a-uuid").expect_err("garbage rejected");
    assert_eq!(err.code, "INVALID");
}

// ---------------------------------------------------------------------------
// From<LaunchAgentOutput> for TerminalSessionDto
// ---------------------------------------------------------------------------

#[test]
fn launch_agent_output_maps_to_terminal_session_dto() {
    let session_id = SessionId::from_uuid(Uuid::from_u128(7));
    let node_id = NodeId::from_uuid(Uuid::from_u128(8));
    let agent_id = AgentId::from_uuid(Uuid::from_u128(9));
    let cwd = ProjectPath::new("/tmp/project".to_owned()).expect("valid path");
    let size = PtySize::new(24, 80).unwrap();

    let mut session = TerminalSession::starting(
        session_id,
        node_id,
        cwd,
        SessionKind::Agent { agent_id },
        size,
    );
    session.status = SessionStatus::Running;

    let out = LaunchAgentOutput {
        session,
        assigned_conversation_id: None,
        engine_session_id: None,
        structured: None,
        profile: None,
    };
    let dto = TerminalSessionDto::from(out);

    assert_eq!(dto.session_id, session_id.to_string());
    assert_eq!(dto.cwd, "/tmp/project");
    assert_eq!(dto.rows, 24);
    assert_eq!(dto.cols, 80);

    // Nothing assigned ⇒ no conversation id surfaced.
    assert_eq!(dto.assigned_conversation_id, None);

    // Also verify camelCase serialisation.
    let v = serde_json::to_value(&dto).unwrap();
    assert_eq!(v["sessionId"], session_id.to_string());
    assert_eq!(v["cwd"], "/tmp/project");
    assert!(v.get("session_id").is_none(), "no snake_case leak");
    // Absent assignment must be omitted from the wire payload entirely.
    assert!(
        v.get("assignedConversationId").is_none(),
        "no assignment ⇒ field omitted"
    );
}

#[test]
fn launch_agent_output_propagates_assigned_conversation_id() {
    let session_id = SessionId::from_uuid(Uuid::from_u128(70));
    let node_id = NodeId::from_uuid(Uuid::from_u128(80));
    let agent_id = AgentId::from_uuid(Uuid::from_u128(90));
    let cwd = ProjectPath::new("/tmp/project".to_owned()).expect("valid path");
    let size = PtySize::new(24, 80).unwrap();

    let mut session = TerminalSession::starting(
        session_id,
        node_id,
        cwd,
        SessionKind::Agent { agent_id },
        size,
    );
    session.status = SessionStatus::Running;

    let out = LaunchAgentOutput {
        session,
        assigned_conversation_id: Some("conv-xyz".to_owned()),
        engine_session_id: None,
        structured: None,
        profile: None,
    };
    let dto = TerminalSessionDto::from(out);

    // The id minted by the launch is carried through to the DTO …
    assert_eq!(dto.assigned_conversation_id.as_deref(), Some("conv-xyz"));
    // … and serialised in camelCase for the front to persist on the leaf.
    let v = serde_json::to_value(&dto).unwrap();
    assert_eq!(v["assignedConversationId"], "conv-xyz");
    assert!(
        v.get("assigned_conversation_id").is_none(),
        "no snake_case leak"
    );
}

// ---------------------------------------------------------------------------
// Lot D — attach/stop live agent DTOs (controlled actions)
// ---------------------------------------------------------------------------

#[test]
fn attach_live_agent_request_deserialises_camelcase() {
    use app_tauri_lib::dto::AttachLiveAgentRequestDto;

    let raw = json!({
        "projectId": Uuid::from_u128(1).to_string(),
        "agentId": Uuid::from_u128(2).to_string(),
        "nodeId": Uuid::from_u128(3).to_string()
    });
    let dto: AttachLiveAgentRequestDto = serde_json::from_value(raw).unwrap();
    assert_eq!(dto.project_id, Uuid::from_u128(1).to_string());
    assert_eq!(dto.agent_id, Uuid::from_u128(2).to_string());
    assert_eq!(dto.node_id, Uuid::from_u128(3).to_string());
}

#[test]
fn attach_live_agent_response_serialises_camelcase_with_kind() {
    use app_tauri_lib::dto::AttachLiveAgentResponseDto;
    use application::{AttachLiveAgentOutput, LiveSessionKind};

    let agent = AgentId::from_uuid(Uuid::from_u128(11));
    let node = NodeId::from_uuid(Uuid::from_u128(12));
    let session = SessionId::from_uuid(Uuid::from_u128(13));
    let dto = AttachLiveAgentResponseDto::from(AttachLiveAgentOutput {
        agent_id: agent,
        node_id: node,
        session_id: session,
        kind: LiveSessionKind::Pty,
    });

    let v = serde_json::to_value(&dto).unwrap();
    assert_eq!(v["agentId"], agent.to_string());
    assert_eq!(v["nodeId"], node.to_string());
    assert_eq!(v["sessionId"], session.to_string());
    assert_eq!(v["kind"], "pty");
    // No snake_case leak.
    assert!(v.get("agent_id").is_none());
    assert!(v.get("session_id").is_none());
}

#[test]
fn stop_live_agent_request_deserialises_camelcase() {
    use app_tauri_lib::dto::StopLiveAgentRequestDto;

    let raw = json!({
        "projectId": Uuid::from_u128(1).to_string(),
        "agentId": Uuid::from_u128(2).to_string()
    });
    let dto: StopLiveAgentRequestDto = serde_json::from_value(raw).unwrap();
    assert_eq!(dto.project_id, Uuid::from_u128(1).to_string());
    assert_eq!(dto.agent_id, Uuid::from_u128(2).to_string());
}

#[test]
fn stop_live_agent_response_serialises_camelcase_with_kind() {
    use app_tauri_lib::dto::StopLiveAgentResponseDto;
    use application::{LiveSessionKind, StopLiveAgentOutput};

    let agent = AgentId::from_uuid(Uuid::from_u128(11));
    let session = SessionId::from_uuid(Uuid::from_u128(13));
    let dto = StopLiveAgentResponseDto::from(StopLiveAgentOutput {
        agent_id: agent,
        session_id: session,
        kind: LiveSessionKind::Structured,
    });

    let v = serde_json::to_value(&dto).unwrap();
    assert_eq!(v["agentId"], agent.to_string());
    assert_eq!(v["sessionId"], session.to_string());
    assert_eq!(v["kind"], "structured");
    // Stop response carries no node id (the session is gone).
    assert!(v.get("nodeId").is_none());
    assert!(v.get("session_id").is_none());
}
