using System.Text.Json;
using System.Text.RegularExpressions;
using Health.Application.AI;
using Health.Application.Diets;
using Health.Application.Exercises;
using Health.Application.HealthArchives;
using Health.Application.HealthRecords;
using Health.Application.Medications;
using Health.Infrastructure.AI;
using Health.Infrastructure.AI.AgentHandlers;

namespace Health.WebApi.Endpoints;

/// <summary>
/// AI 对话 SSE 端点——支持 7 个 Agent
/// </summary>
public static class AiChatEndpoints
{
    private static readonly JsonSerializerOptions JsonOpts = new()
    {
        PropertyNamingPolicy = JsonNamingPolicy.CamelCase,
        PropertyNameCaseInsensitive = true,
    };

    public static void MapAiChatEndpoints(this WebApplication app)
    {
        // SSE 流式对话（GET 方式，token 通过 query string 传递）
        app.MapGet("/api/ai/{agentType}/chat", async (
            string message,
            string? conversationId,
            string token,
            string agentType,
            HttpContext http,
            DeepSeekClient llmClient,
            PromptManager promptManager,
            IAiToolExecutionService toolExecution,
            IAiWriteConfirmationStore confirmations,
            IAiConversationService conversations,
            IPatientContextService patientContexts,
            CancellationToken ct) =>
        {
            // 支持 token 通过 query string（浏览器 EventSource）或 header 传递
            var userId = GetUserId(http) ?? GetUserIdFromToken(token);
            if (userId == null)
            {
                http.Response.StatusCode = 401;
                http.Response.ContentType = "application/json";
                await http.Response.WriteAsync(JsonSerializer.Serialize(new { code = 40002, data = (object?)null, message = "未登录" }), ct);
                return;
            }

            if (!Enum.TryParse<AgentType>(agentType, ignoreCase: true, out var parsedType))
                parsedType = AgentType.Default;

            // SSE 响应头
            http.Response.ContentType = "text/event-stream";
            http.Response.Headers.CacheControl = "no-cache";
            http.Response.Headers.Connection = "keep-alive";
            http.Response.Headers["X-Accel-Buffering"] = "no";

            // 创建或获取对话。传入 conversationId 时必须校验归属，避免多账号切换或缓存异常导致串号。
            Guid? requestedConversationId = null;
            if (!string.IsNullOrWhiteSpace(conversationId))
            {
                if (!Guid.TryParse(conversationId, out var convId))
                {
                    await SseWriteAsync(http, new { action = "answer", data = "当前会话参数异常，请重新开始一次对话。" }, ct);
                    await SseWriteAsync(http, new { action = "status", data = "error" }, ct);
                    await http.Response.WriteAsync("data: [DONE]\n\n", ct);
                    return;
                }
                requestedConversationId = convId;
            }

            var opened = await conversations.OpenAsync(userId.Value, requestedConversationId, parsedType, message, ct);
            if (!opened.Found)
            {
                await SseWriteAsync(http, new { action = "answer", data = "当前会话不存在或不属于当前账号，请重新开始一次对话。" }, ct);
                await SseWriteAsync(http, new { action = "status", data = "error" }, ct);
                await http.Response.WriteAsync("data: [DONE]\n\n", ct);
                return;
            }
            var activeConversationId = opened.ConversationId;
            if (opened.Created)
                await SseWriteAsync(http, new { action = "conversation_id", data = activeConversationId.ToString() }, ct);

            await conversations.AddUserMessageAsync(activeConversationId, message, ct);

            var urgentWarning = DetectUrgentRisk(message);
            if (!string.IsNullOrEmpty(urgentWarning))
            {
                var urgentResponse = $"""
                    {urgentWarning}

                    这种情况需要把安全放在第一位。请先停止自行判断和等待 AI 分析，尽快联系医生、互联网医院或前往急诊评估；如果症状正在加重，建议立即拨打当地急救电话。若身边有人，请让家人或同伴陪同，不要独自开车就医。

                    以上为 AI 安全提醒，不能替代医生诊断和治疗建议。
                    """;

                await conversations.AddAssistantMessageAsync(activeConversationId, urgentResponse, ct);

                await SseWriteAsync(http, new { action = "notice", message = "检测到可能的危险信号，优先给出就医提醒" }, ct);
                await SseWriteAsync(http, new { action = "answer", data = urgentResponse, type = "text" }, ct);
                await SseWriteAsync(http, new { action = "status", data = "done" }, ct);
                await http.Response.WriteAsync("data: [DONE]\n\n", ct);
                return;
            }

            // 加载上下文
            var systemPrompt = promptManager.GetSystemPrompt(parsedType);
            var patientContext = await patientContexts.BuildAsync(userId.Value, ct);

            var messages = new List<ChatMessage>
            {
                new() { Role = "system", Content = systemPrompt + "\n\n当前患者信息：\n" + patientContext },
            };

            // 加载历史对话（最近 10 条）
            var history = await conversations.GetRecentMessagesAsync(activeConversationId, 12, ct);

            foreach (var h in history)
            {
                messages.Add(new ChatMessage
                {
                    Role = h.Role == MessageRole.User.ToString() ? "user" : "assistant",
                    Content = h.Content,
                });
            }

            // Tool Calling 循环
            var tools = GetToolsForAgent(parsedType);
            var maxIterations = 5;
            var fullResponse = "";
            var completedNormally = false;
            var messageType = "text";
            var metadata = new Dictionary<string, object>();

            for (int i = 0; i < maxIterations; i++)
            {
                await SseWriteAsync(http, new { action = "notice", message = i == 0 ? "正在分析..." : "正在处理..." }, ct);

                var response = await llmClient.ChatAsync(messages, tools: tools.Count > 0 ? tools : null, ct: ct);

                var choice = response.Choices?.FirstOrDefault();
                if (choice == null) break;

                if (choice.FinishReason == "stop")
                {
                    await foreach (var chunk in llmClient.ChatStreamAsync(messages, tools: null, ct: ct))
                    {
                        try
                        {
                            var delta = JsonSerializer.Deserialize<ChatCompletionResponse>(chunk, JsonOpts);
                            var content = delta?.Choices?.FirstOrDefault()?.Delta?.Content;
                            if (!string.IsNullOrEmpty(content))
                            {
                                fullResponse += content;
                                await SseWriteAsync(http, new { action = "answer", data = content, type = messageType, metadata = metadata.Count > 0 ? metadata : null }, ct);
                            }
                        }
                        catch (JsonException) { /* 跳过解析失败的 chunk */ }
                    }
                    completedNormally = true;
                    break;
                }
                else if (choice.FinishReason == "tool_calls" && choice.Message?.ToolCalls != null)
                {
                    messages.Add(new ChatMessage
                    {
                        Role = "assistant",
                        Content = choice.Message.Content ?? "",
                        ToolCalls = choice.Message.ToolCalls,
                    });

                    foreach (var tc in choice.Message.ToolCalls)
                    {
                        object toolResult;
                        try
                        {
                            toolResult = IsWriteToolCall(tc.Function.Name, tc.Function.Arguments)
                                ? await PreparePendingWriteAsync(confirmations, userId.Value, tc.Function.Name, tc.Function.Arguments, ct)
                                : await toolExecution.ExecuteAsync(tc.Function.Name, tc.Function.Arguments, userId.Value, ct);
                        }
                        catch (Exception ex)
                        {
                            toolResult = new { success = false, message = $"工具执行异常: {ex.Message}" };
                        }
                        await SseWriteAsync(http, new { action = "tool_result", tool = tc.Function.Name, data = toolResult }, ct);

                        _UpdateMessageTypeAndMetadata(tc.Function.Name, toolResult, ref messageType, ref metadata);

                        messages.Add(new ChatMessage { Role = "tool", Content = JsonSerializer.Serialize(toolResult, JsonOpts), ToolCallId = tc.Id });
                    }
                }
                else break;
            }

            // 保存 AI 回复
            if (!string.IsNullOrEmpty(fullResponse))
                await conversations.AddAssistantMessageAsync(activeConversationId, fullResponse, ct);

            await SseWriteAsync(http, new { action = "status", data = completedNormally ? "done" : "error" }, ct);
            await http.Response.WriteAsync("data: [DONE]\n\n", ct);
        });

        app.MapPost("/api/ai/confirm-write/{commandId:guid}", async (
            Guid commandId,
            HttpContext http,
            IAiToolExecutionService toolExecution,
            CancellationToken ct) =>
        {
            var userId = GetUserId(http);
            if (userId == null)
                return Results.Json(new { code = 40002, data = (object?)null, message = "未登录" }, statusCode: 401);

            var result = await toolExecution.ConfirmAsync(commandId, userId.Value, ct);
            return Results.Ok(new { code = result.Code, data = result.Data, message = result.Message });
        }).RequireAuthorization();

        // 获取对话列表
        app.MapGet("/api/ai/conversations", async (HttpContext http, IAiConversationService conversations, CancellationToken ct) =>
        {
            var userId = GetUserId(http);
            if (userId == null) return Results.Json(new { code = 40002, data = (object?)null, message = "未登录" }, statusCode: 401);

            var result = await conversations.ListAsync(userId.Value, ct);

            return Results.Ok(new { code = 0, data = result, message = (string?)null });
        });

        // 获取对话历史
        app.MapGet("/api/ai/conversations/{id:guid}", async (Guid id, HttpContext http, IAiConversationService conversations, CancellationToken ct) =>
        {
            var userId = GetUserId(http);
            if (userId == null) return Results.Json(new { code = 40002 }, statusCode: 401);

            var messages = await conversations.GetMessagesAsync(userId.Value, id, ct);

            return Results.Ok(new { code = 0, data = messages, message = (string?)null });
        });

        // 删除对话
        app.MapDelete("/api/ai/conversations/{id:guid}", async (Guid id, HttpContext http, IAiConversationService conversations, CancellationToken ct) =>
        {
            var userId = GetUserId(http);
            if (userId == null) return Results.Json(new { code = 40002 }, statusCode: 401);

            await conversations.DeleteAsync(userId.Value, id, ct);
            return Results.Ok(new { code = 0, data = new { success = true }, message = (string?)null });
        });

        app.MapPost("/api/ai/analyze-food-image", async (
            HttpRequest httpRequest,
            HttpContext http,
            IDietImageAnalysisCoordinator dietAnalysis,
            CancellationToken ct) =>
        {
            var userId = GetUserId(http);
            if (userId == null) return Results.Json(new { code = 40002 }, statusCode: 401);

            var form = await httpRequest.ReadFormAsync(ct);
            var files = form.Files.GetFiles("images");
            var uploads = files
                .Select(file => new DietImageUploadFile(file.FileName, file.Length, file.OpenReadStream()))
                .ToList();
            try
            {
                var result = await dietAnalysis.AnalyzeAsync(uploads, ct);
                return Results.Ok(new { code = result.Code, data = result.Data, message = result.Message });
            }
            finally
            {
                foreach (var upload in uploads)
                    await upload.Content.DisposeAsync();
            }
        });
    }

    // ── SSE / 认证辅助 ──

    private static async Task SseWriteAsync(HttpContext http, object data, CancellationToken ct)
    {
        var json = JsonSerializer.Serialize(data, JsonOpts);
        await http.Response.WriteAsync($"data: {json}\n\n", ct);
        await http.Response.Body.FlushAsync(ct);
    }

    private static Guid? GetUserId(HttpContext http) =>
        Guid.TryParse(http.User.FindFirst(System.Security.Claims.ClaimTypes.NameIdentifier)?.Value, out var id) ? id : null;

    private static Guid? GetUserIdFromToken(string? token)
    {
        if (string.IsNullOrEmpty(token)) return null;
        try
        {
            var handler = new System.IdentityModel.Tokens.Jwt.JwtSecurityTokenHandler();
            var jwt = handler.ReadJwtToken(token);
            var sub = jwt.Claims.FirstOrDefault(c => c.Type == System.Security.Claims.ClaimTypes.NameIdentifier)?.Value;
            return sub != null && Guid.TryParse(sub, out var id) ? id : null;
        }
        catch (Exception) { return null; }
    }

    // ── Agent / Tool 调度 ──

    private static List<ToolDefinition> GetToolsForAgent(AgentType agentType) => agentType switch
    {
        AgentType.Health => HealthDataAgentHandler.Tools,
        AgentType.Medication => MedicationAgentHandler.Tools,
        AgentType.Diet => DietAgentHandler.Tools,
        AgentType.Consultation => ConsultationAgentHandler.Tools,
        AgentType.Report => ReportAgentHandler.Tools,
        AgentType.Exercise => ExerciseAgentHandler.Tools,
        AgentType.Unified => [
            HealthDataAgentHandler.RecordHealthDataTool,
            CommonAgentHandler.QueryHealthRecordsTool,
            MedicationAgentHandler.ManageMedicationTool,
            ExerciseAgentHandler.ManageExerciseTool,
            CommonAgentHandler.CheckArchiveTool,
        ],
        _ => CommonAgentHandler.Tools,
    };

    private static bool IsWriteToolCall(string toolName, string arguments)
    {
        if (toolName == "record_health_data") return true;
        if (toolName == "manage_archive") return GetToolAction(arguments) != "query";
        if (toolName == "manage_medication") return GetToolAction(arguments) is "create" or "confirm";
        if (toolName == "manage_exercise") return GetToolAction(arguments) is "create" or "checkin";
        return false;
    }

    private static string GetToolAction(string arguments)
    {
        try
        {
            using var json = JsonDocument.Parse(arguments);
            return json.RootElement.TryGetProperty("action", out var action)
                ? action.GetString()?.ToLowerInvariant() ?? ""
                : "";
        }
        catch
        {
            return "";
        }
    }

    private static async Task<object> PreparePendingWriteAsync(
        IAiWriteConfirmationStore confirmations,
        Guid userId,
        string toolName,
        string arguments,
        CancellationToken ct)
    {
        var command = await confirmations.CreateAsync(userId, toolName, arguments, TimeSpan.FromMinutes(10), ct);
        using var json = JsonDocument.Parse(arguments);
        var args = json.RootElement;

        var preview = new Dictionary<string, object?>
        {
            ["success"] = true,
            ["pendingConfirmation"] = true,
            ["confirmationId"] = command.Id,
            ["message"] = "等待用户确认后写入",
        };

        switch (toolName)
        {
            case "record_health_data":
                AddHealthPreview(preview, args);
                break;
            case "manage_medication":
                preview["type"] = "medication";
                preview["name"] = GetString(args, "name") ?? "服药确认";
                preview["dosage"] = GetString(args, "dosage") ?? "";
                preview["frequency"] = GetString(args, "frequency") ?? "Daily";
                preview["time"] = GetStringArray(args, "time_of_day");
                preview["duration_days"] = GetInt(args, "duration_days") ?? 0;
                preview["start_date"] = GetString(args, "start_date") ?? DateOnly.FromDateTime(DateTime.UtcNow.AddHours(8)).ToString("yyyy-MM-dd");
                break;
            case "manage_exercise":
                preview["type"] = "exercise";
                preview["exercise_type"] = GetString(args, "exercise_type") ?? "运动";
                preview["duration_minutes"] = GetInt(args, "duration_minutes") ?? 30;
                preview["day_count"] = GetInt(args, "duration_days") ?? 7;
                preview["start_date"] = GetString(args, "start_date") ?? DateOnly.FromDateTime(DateTime.UtcNow.AddHours(8)).ToString("yyyy-MM-dd");
                preview["reminder_time"] = GetString(args, "reminder_time") ?? "19:00";
                break;
            case "manage_archive":
                preview["type"] = "archive";
                preview["value"] = "健康档案更新";
                preview["unit"] = "";
                break;
        }

        return preview;
    }

    private static void AddHealthPreview(Dictionary<string, object?> preview, JsonElement args)
    {
        var type = GetString(args, "type") ?? "";
        switch (type)
        {
            case "blood_pressure":
                var systolic = GetInt(args, "systolic");
                var diastolic = GetInt(args, "diastolic");
                preview["type"] = HealthMetricType.BloodPressure.ToString();
                preview["value"] = $"{systolic}/{diastolic}";
                preview["unit"] = "mmHg";
                preview["isAbnormal"] = systolic >= 140 || diastolic >= 90 || systolic <= 89 || diastolic <= 59;
                break;
            case "heart_rate":
                AddMetricPreview(preview, HealthMetricType.HeartRate, GetDecimal(args, "heart_rate"), "次/分", v => v > 100 || v < 60);
                break;
            case "glucose":
                AddMetricPreview(preview, HealthMetricType.Glucose, GetDecimal(args, "glucose"), "mmol/L", v => v >= 7.0m || v <= 3.8m);
                break;
            case "spo2":
                AddMetricPreview(preview, HealthMetricType.SpO2, GetDecimal(args, "spo2"), "%", v => v <= 94);
                break;
            case "weight":
                AddMetricPreview(preview, HealthMetricType.Weight, GetDecimal(args, "weight"), "kg", _ => false);
                break;
        }
    }

    private static void AddMetricPreview(
        Dictionary<string, object?> preview,
        HealthMetricType type,
        decimal? value,
        string unit,
        Func<decimal, bool> isAbnormal)
    {
        preview["type"] = type.ToString();
        preview["value"] = value?.ToString() ?? "";
        preview["unit"] = unit;
        preview["isAbnormal"] = value.HasValue && isAbnormal(value.Value);
    }

    private static string? GetString(JsonElement element, string name) =>
        element.TryGetProperty(name, out var value) && value.ValueKind == JsonValueKind.String ? value.GetString() : null;

    private static int? GetInt(JsonElement element, string name) =>
        element.TryGetProperty(name, out var value) && value.TryGetInt32(out var result) ? result : null;

    private static decimal? GetDecimal(JsonElement element, string name) =>
        element.TryGetProperty(name, out var value) && value.TryGetDecimal(out var result) ? result : null;

    private static string GetStringArray(JsonElement element, string name) =>
        element.TryGetProperty(name, out var value) && value.ValueKind == JsonValueKind.Array
            ? string.Join(", ", value.EnumerateArray().Select(x => x.GetString()).Where(x => !string.IsNullOrWhiteSpace(x)))
            : "";

    private static string? DetectUrgentRisk(string message)
    {
        var text = message.Trim();
        if (string.IsNullOrEmpty(text)) return null;

        if (ContainsAny(text, "剧烈胸痛", "胸口剧痛", "胸痛明显", "胸闷憋气", "喘不上气", "呼吸困难", "呼吸很困难", "意识模糊", "昏厥", "晕厥", "说话不清", "口角歪斜", "一侧无力"))
            return "你描述的症状可能属于需要紧急评估的危险信号。";

        var bp = Regex.Match(text, @"(?:血压|bp|BP)?\s*(?<sys>\d{2,3})\s*/\s*(?<dia>\d{2,3})");
        if (bp.Success &&
            int.TryParse(bp.Groups["sys"].Value, out var systolic) &&
            int.TryParse(bp.Groups["dia"].Value, out var diastolic) &&
            (systolic >= 180 || diastolic >= 120))
        {
            return $"你提到的血压 {systolic}/{diastolic} mmHg 已达到明显危险范围。";
        }

        var spo2 = MatchMetricValue(text, "血氧|氧饱和|SpO2|spo2");
        if (spo2.HasValue && spo2.Value <= 90)
            return $"你提到的血氧 {spo2.Value:0.#}% 明显偏低。";

        var glucose = MatchMetricValue(text, "血糖|葡萄糖|glucose");
        if (glucose.HasValue && (glucose.Value >= 16.7m || glucose.Value <= 3.0m))
            return $"你提到的血糖 {glucose.Value:0.#} mmol/L 属于需要尽快处理的异常范围。";

        var heartRate = MatchMetricValue(text, "心率|脉搏|heart rate|HR|hr");
        if (heartRate.HasValue && (heartRate.Value >= 130 || heartRate.Value <= 45))
            return $"你提到的心率 {heartRate.Value:0.#} 次/分属于明显异常范围。";

        return null;
    }

    private static bool ContainsAny(string text, params string[] keywords) =>
        keywords.Any(k => text.Contains(k, StringComparison.OrdinalIgnoreCase));

    private static decimal? MatchMetricValue(string text, string metricPattern)
    {
        var match = Regex.Match(text, $@"(?:{metricPattern})[^\d]{{0,8}}(?<value>\d{{1,3}}(?:\.\d+)?)", RegexOptions.IgnoreCase);
        return match.Success && decimal.TryParse(match.Groups["value"].Value, out var value) ? value : null;
    }

    // ── JSON 类型安全转换 ──
    private static int _ToInt(object? v) => v switch
    {
        JsonElement je => je.ValueKind == JsonValueKind.Number ? je.GetInt32() : (int.TryParse(je.GetRawText().Trim('"'), out var i) ? i : 0),
        int i => i,
        long l => (int)l,
        string s => int.TryParse(s, out var si) ? si : 0,
        _ => 0
    };
    private static bool _ToBool(object? v) => v switch
    {
        JsonElement je => je.ValueKind == JsonValueKind.True || je.ValueKind == JsonValueKind.False ? je.GetBoolean() : false,
        bool b => b,
        _ => false
    };

    // ── 消息类型判断 ──

    private static void _UpdateMessageTypeAndMetadata(string toolName, object toolResult, ref string messageType, ref Dictionary<string, object> metadata)
    {
        // 将匿名对象统一转成 Dictionary（toolResult 可能是匿名类型，不是 IDictionary）
        var resultDict = toolResult as IDictionary<string, object>;
        if (resultDict == null)
        {
            try
            {
                var json = JsonSerializer.Serialize(toolResult, JsonOpts);
                resultDict = JsonSerializer.Deserialize<Dictionary<string, object>>(json, JsonOpts);
            }
            catch (JsonException) { resultDict = new Dictionary<string, object>(); }
        }

        var isPendingConfirmation = resultDict != null
            && resultDict.TryGetValue("pendingConfirmation", out var pendingValue)
            && _ToBool(pendingValue);
        if (isPendingConfirmation && resultDict!.TryGetValue("confirmationId", out var confirmationIdValue))
        {
            var confirmationId = confirmationIdValue?.ToString()?.Trim('"');
            if (!string.IsNullOrWhiteSpace(confirmationId))
            {
                if (!metadata.TryGetValue("confirmationIds", out var idsValue) || idsValue is not List<string> ids)
                {
                    ids = [];
                    metadata["confirmationIds"] = ids;
                }
                ids.Add(confirmationId);
            }
        }

        switch (toolName)
        {
            case "record_health_data":
                if (!isPendingConfirmation) break;
                messageType = "data_confirm";
                if (resultDict != null)
                {
                    if (resultDict.TryGetValue("type", out var type)) metadata["type"] = type?.ToString() ?? "";
                    if (resultDict.TryGetValue("value", out var val)) metadata["value"] = val?.ToString() ?? "";
                    if (resultDict.TryGetValue("unit", out var unit)) metadata["unit"] = unit?.ToString() ?? "";
                    if (resultDict.TryGetValue("isAbnormal", out var abn)) metadata["abnormal"] = _ToBool(abn);
                    if (resultDict.TryGetValue("success", out var success)) metadata["success"] = _ToBool(success);
                    metadata["recordTime"] = DateTime.UtcNow.AddHours(8).ToString("MM月dd日 HH:mm");
                }
                break;
            case "manage_medication":
                if (!isPendingConfirmation) break;
                messageType = "medication_confirm";
                if (resultDict != null)
                {
                    if (resultDict.TryGetValue("name", out var name))
                        metadata["name"] = name?.ToString() ?? "";
                    if (resultDict.TryGetValue("dosage", out var dosage))
                        metadata["dosage"] = dosage?.ToString() ?? "";
                    if (resultDict.TryGetValue("time", out var time))
                        metadata["time"] = time?.ToString() ?? "";
                    if (resultDict.TryGetValue("frequency", out var freq))
                        metadata["frequency"] = freq?.ToString() ?? "";
                    if (resultDict.TryGetValue("duration_days", out var dd2))
                        metadata["duration_days"] = _ToInt(dd2);
                    if (resultDict.TryGetValue("start_date", out var sd))
                        metadata["startDate"] = sd?.ToString() ?? "";
                }
                break;
            case "manage_exercise":
                if (!isPendingConfirmation) break;
                messageType = "data_confirm";
                if (resultDict != null)
                {
                    metadata["type"] = "exercise";
                    resultDict.TryGetValue("exercise_type", out var et);
                    metadata["value"] = et?.ToString() ?? "";
                    resultDict.TryGetValue("duration_minutes", out var dm);
                    metadata["unit"] = dm != null ? $"每天{dm}分钟" : "";
                    resultDict.TryGetValue("day_count", out var dc);
                    if (dc != null) metadata["durationDays"] = dc.ToString() ?? "";
                    resultDict.TryGetValue("success", out var ok);
                    metadata["success"] = _ToBool(ok);
                    metadata["recordTime"] = DateTime.UtcNow.AddHours(8).ToString("MM月dd日 HH:mm");
                }
                break;
            case "manage_archive":
                if (!isPendingConfirmation) break;
                messageType = "data_confirm";
                metadata["type"] = "archive";
                metadata["value"] = "健康档案更新";
                metadata["unit"] = "";
                metadata["success"] = true;
                metadata["recordTime"] = DateTime.UtcNow.AddHours(8).ToString("MM月dd日 HH:mm");
                break;
            case "analyze_report":
                messageType = "report_analysis";
                break;
        }
    }

}

public sealed record ChatRequest(string Message, string? ConversationId);
