import "server-only";

import { ChatOpenAI } from "@langchain/openai";
import { and, desc, eq } from "drizzle-orm";

import { getAiRuntimeConfig } from "@/modules/ai/server/config";
import { buildElderAiContext } from "@/modules/ai/server/elder-context";
import { retrieveKnowledge } from "@/modules/ai/server/knowledge";
import type {
  AiCitation,
  AiErrorCategory,
  ElderAiAnalysisHistoryItem,
  ElderAiAnalysisOutput,
} from "@/modules/ai/types";
import {
  canViewAnalysisScopes,
  parseDataScopes,
  validateElderAiAnalysisOutput,
} from "@/modules/ai/types";
import { recordAuditLog } from "@/modules/core/server/audit";
import { getDatabase } from "@/modules/core/server/db";
import { elderAiAnalyses } from "@/modules/core/server/schema";
import type { AuthContext } from "@/modules/core/types";

type ServiceResult<T> = { success: true; data: T } | { success: false; reason: string; status: number };
type AnalysisRow = typeof elderAiAnalyses.$inferSelect;

function createChatModel(config: { baseUrl: string; apiKey: string; chatModel: string }) {
  const configuration = config.baseUrl ? { baseURL: config.baseUrl } : undefined;
  return new ChatOpenAI({
    apiKey: config.apiKey,
    model: config.chatModel,
    maxTokens: 1800,
    temperature: 0.2,
    configuration,
  });
}

function toIsoString(value: Date): string {
  return value.toISOString();
}

function mapErrorCategory(error: unknown): AiErrorCategory {
  if (error instanceof Error && error.name.toLowerCase().includes("timeout")) {
    return "timeout";
  }
  return "provider_error";
}

function getBriefErrorReason(category: AiErrorCategory): string {
  if (category === "missing_config") {
    return "AI 服务未配置";
  }
  if (category === "schema_validation_failed") {
    return "AI 返回结构不符合固定分析格式";
  }
  if (category === "retrieval_failed") {
    return "知识库检索失败";
  }
  if (category === "timeout") {
    return "AI 服务响应超时";
  }
  return "AI 服务暂时不可用";
}

function safeJsonParse(value: string): unknown {
  try {
    return JSON.parse(value);
  } catch {
    return null;
  }
}

function extractJsonFromText(value: string): unknown {
  const trimmed = value.trim();
  const direct = safeJsonParse(trimmed);
  if (direct) {
    return direct;
  }
  const start = trimmed.indexOf("{");
  const end = trimmed.lastIndexOf("}");
  if (start < 0 || end <= start) {
    return null;
  }
  return safeJsonParse(trimmed.slice(start, end + 1));
}

function collectReferencedCitationIds(output: ElderAiAnalysisOutput): Set<string> {
  const referencedIds = new Set<string>();
  for (const finding of output.keyFindings) {
    for (const citationId of finding.citationIds) {
      referencedIds.add(citationId);
    }
  }
  for (const recommendation of output.recommendations) {
    for (const citationId of recommendation.citationIds) {
      referencedIds.add(citationId);
    }
  }
  return referencedIds;
}

function normalizeCitations(output: ElderAiAnalysisOutput, citations: AiCitation[]): ElderAiAnalysisOutput | null {
  const allowed = new Map(citations.map((citation) => [citation.id, citation]));
  for (const citation of output.citations) {
    if (!allowed.has(citation.id)) {
      return null;
    }
  }

  const referencedIds = collectReferencedCitationIds(output);
  for (const citationId of referencedIds) {
    if (!allowed.has(citationId)) {
      return null;
    }
  }

  return {
    ...output,
    citations: citations.filter((citation) => referencedIds.has(citation.id)),
  };
}

async function saveFailedAnalysis(input: {
  context: AuthContext;
  organizationId: string;
  elderId: string;
  dataScopes: string[];
  category: AiErrorCategory;
}): Promise<void> {
  const database = getDatabase();
  await database.insert(elderAiAnalyses).values({
    organizationId: input.organizationId,
    elderId: input.elderId,
    actorAccountId: input.context.account.id,
    status: "failed",
    dataScopes: input.dataScopes,
    errorCategory: input.category,
    errorReason: getBriefErrorReason(input.category),
  });
  await recordAuditLog({
    actor: input.context.account,
    organizationId: input.organizationId,
    action: "ai.elder_analysis.generate",
    targetType: "elder",
    targetId: input.elderId,
    result: "failure",
    reason: getBriefErrorReason(input.category),
  });
}

function rowToHistoryItem(row: AnalysisRow, permissions: AuthContext["permissions"]): ElderAiAnalysisHistoryItem {
  const scopes = parseDataScopes(row.dataScopes);
  const restricted = !canViewAnalysisScopes(scopes, permissions);
  if (restricted) {
    return {
      id: row.id,
      elderId: row.elderId,
      status: row.status,
      dataScopes: scopes,
      createdAt: toIsoString(row.createdAt),
      restricted: true,
    };
  }

  const result = validateElderAiAnalysisOutput(row.resultJson);
  const errorCategory = row.errorCategory as AiErrorCategory;
  return {
    id: row.id,
    elderId: row.elderId,
    status: row.status,
    dataScopes: scopes,
    createdAt: toIsoString(row.createdAt),
    restricted: false,
    result: result ?? undefined,
    errorCategory: errorCategory || undefined,
    errorReason: row.errorReason || undefined,
  };
}

export async function listElderAiAnalyses(
  context: AuthContext,
  elderId: string,
): Promise<ServiceResult<{ history: ElderAiAnalysisHistoryItem[] }>> {
  const organizationId = context.organization?.id;
  if (!organizationId) {
    return { success: false, reason: "请选择机构后查看 AI 分析", status: 400 };
  }
  const database = getDatabase();
  const rows = await database
    .select()
    .from(elderAiAnalyses)
    .where(and(eq(elderAiAnalyses.organizationId, organizationId), eq(elderAiAnalyses.elderId, elderId)))
    .orderBy(desc(elderAiAnalyses.createdAt))
    .limit(20);

  return {
    success: true,
    data: {
      history: rows.map((row) => rowToHistoryItem(row, context.permissions)),
    },
  };
}

export async function generateElderAiAnalysis(
  context: AuthContext,
  elderId: string,
): Promise<ServiceResult<{ analysis: ElderAiAnalysisHistoryItem }>> {
  const runtimeConfig = getAiRuntimeConfig();
  if (!runtimeConfig.success) {
    const organizationId = context.organization?.id;
    if (organizationId) {
      await saveFailedAnalysis({
        context,
        organizationId,
        elderId,
        dataScopes: ["elder"],
        category: "missing_config",
      });
    }
    return { success: false, reason: runtimeConfig.reason, status: 500 };
  }

  const residentContext = await buildElderAiContext(context, elderId);
  if (!residentContext.success) {
    return { success: false, reason: residentContext.reason, status: residentContext.status };
  }

  const knowledgeResults = context.permissions.includes("knowledge:read")
    ? await retrieveKnowledge(
        residentContext.context.organizationId,
        `${residentContext.context.elderName}\n${residentContext.context.promptContext}`,
      )
    : { success: true as const, data: { results: [] } };

  if (!knowledgeResults.success) {
    await recordAuditLog({
      actor: context.account,
      organizationId: residentContext.context.organizationId,
      action: "ai.knowledge.retrieve",
      targetType: "elder",
      targetId: elderId,
      result: "failure",
      reason: getBriefErrorReason("retrieval_failed"),
    });
  }

  const knowledgeItems = knowledgeResults.success ? knowledgeResults.data.results : [];
  const knowledgeUnavailableReason = knowledgeResults.success ? "" : getBriefErrorReason("retrieval_failed");
  const dataScopes = knowledgeItems.length > 0
    ? [...residentContext.context.dataScopes, "knowledge" as const]
    : residentContext.context.dataScopes;
  const knowledgeCitations: AiCitation[] = knowledgeItems.map((item, index) => ({
    id: `kb-${index + 1}`,
    sourceType: "knowledge",
    sourceId: item.chunkId,
    title: item.title,
    excerpt: item.content.slice(0, 240),
  }));
  const citations = [...residentContext.context.citations, ...knowledgeCitations];

  const model = createChatModel(runtimeConfig.config);
  const prompt = [
    "你是养老机构运营辅助分析智能体。只输出 JSON，不要 Markdown。",
    "分析必须是建议性、非诊断性，不能创建业务记录或承诺操作。",
    "只能使用提供的上下文和知识库片段，证据必须引用 citation id。",
    "输出 JSON 字段：overallRiskLevel(low|medium|high|critical|unknown), summary, keyFindings, recommendations, dataGaps, citations, confidence, modelSummary。",
    "keyFindings[] 字段：category, severity(info|warning|critical), evidence, citationIds。",
    "recommendations[] 字段：title, priority(low|normal|high|urgent), rationale, suggestedNextStep, citationIds。",
    "citations[] 只返回你实际引用过的 citation 对象，citationIds 必须来自可用 citations。",
    knowledgeUnavailableReason ? `知识库检索不可用：${knowledgeUnavailableReason}。请在 dataGaps 中包含“知识库检索不可用，分析未使用内部知识库”。` : "",
    `modelSummary 固定为 {"provider":"openai-compatible","chatModel":"${runtimeConfig.config.chatModel}","embeddingModel":"${runtimeConfig.config.embeddingModel}"}。`,
    `可用 citations：${JSON.stringify(citations)}`,
    `住民上下文：\n${residentContext.context.promptContext}`,
    `知识库片段：\n${knowledgeItems.map((item, index) => `[kb-${index + 1}] ${item.title}\n${item.content}`).join("\n\n") || "无"}`,
  ].join("\n\n");

  let rawOutput: unknown;
  try {
    const response = await model.invoke(prompt, {
      response_format: { type: "json_object" },
    });
    const content = Array.isArray(response.content)
      ? response.content.map((item) => (typeof item === "string" ? item : "")).join("\n")
      : String(response.content);
    rawOutput = extractJsonFromText(content);
  } catch (error) {
    const category = mapErrorCategory(error);
    await saveFailedAnalysis({
      context,
      organizationId: residentContext.context.organizationId,
      elderId,
      dataScopes,
      category,
    });
    return { success: false, reason: getBriefErrorReason(category), status: 502 };
  }

  const output = validateElderAiAnalysisOutput(rawOutput);
  if (!output) {
    await saveFailedAnalysis({
      context,
      organizationId: residentContext.context.organizationId,
      elderId,
      dataScopes,
      category: "schema_validation_failed",
    });
    return { success: false, reason: getBriefErrorReason("schema_validation_failed"), status: 502 };
  }

  const outputWithDataGaps = knowledgeUnavailableReason
    ? {
        ...output,
        dataGaps: [...output.dataGaps, "知识库检索不可用，分析未使用内部知识库"],
      }
    : output;
  const normalizedOutput = normalizeCitations(outputWithDataGaps, citations);
  if (!normalizedOutput) {
    await saveFailedAnalysis({
      context,
      organizationId: residentContext.context.organizationId,
      elderId,
      dataScopes,
      category: "schema_validation_failed",
    });
    return { success: false, reason: getBriefErrorReason("schema_validation_failed"), status: 502 };
  }
  const database = getDatabase();
  const rows = await database
    .insert(elderAiAnalyses)
    .values({
      organizationId: residentContext.context.organizationId,
      elderId,
      actorAccountId: context.account.id,
      status: "completed",
      dataScopes,
      resultJson: normalizedOutput,
      citationsJson: normalizedOutput.citations,
      modelSummaryJson: normalizedOutput.modelSummary,
    })
    .returning();
  const row = rows[0];
  if (!row) {
    return { success: false, reason: "AI 分析保存失败", status: 500 };
  }

  await recordAuditLog({
    actor: context.account,
    organizationId: residentContext.context.organizationId,
    action: "ai.elder_analysis.generate",
    targetType: "elder",
    targetId: elderId,
    result: "success",
    reason: "AI 分析已生成",
  });

  return {
    success: true,
    data: {
      analysis: rowToHistoryItem(row, context.permissions),
    },
  };
}
