import { NextResponse } from "next/server";

import { requireApiAccess } from "../../../../lib/api/auth";
import type { ProcessSummaryStep } from "../../../../lib/cases/types";
import { buildArticleCaseSummary, createProcessStep } from "../../../../lib/cases/summaries";
import { getRepositoryFromRuntime } from "../../../../lib/db/repository";
import { optimizationFactCardSchema } from "../../../../lib/domain/validation";
import type { LlmAuditSummary } from "../../../../lib/llm/audit";
import { LlmValidationError } from "../../../../lib/llm/client";
import { getExportStoreFromRuntime } from "../../../../lib/workflow/export-store";
import { extractCandidateFactCard } from "../../../../lib/workflow/fact-extractor";
import {
  normalizeInput,
  type RawArticleInput,
} from "../../../../lib/workflow/input-normalizer";
import {
  encodeOptimizationStreamEvent,
  type OptimizationStreamEvent,
  type OptimizationStreamStage,
} from "../../../../lib/workflow/stream-events";
import { runStreamingOptimizationWorkflow } from "../../../../lib/workflow/streaming-optimizer";

interface OptimizeStreamPayload extends RawArticleInput {
  fact_card?: unknown;
}

export async function POST(request: Request) {
  const access = requireApiAccess(request);
  if (!access.ok) {
    return access.response;
  }

  let payload: OptimizeStreamPayload;
  try {
    payload = (await request.json()) as OptimizeStreamPayload;
  } catch {
    return NextResponse.json({ error: "请求体不是合法 JSON" }, { status: 400 });
  }

  if (!hasOptimizableBody(payload)) {
    return NextResponse.json(
      { error: "请输入需要优化的文章内容" },
      { status: 400 },
    );
  }

  let normalized: ReturnType<typeof normalizeInput>;
  try {
    normalized = normalizeInput(payload);
  } catch (error) {
    return jsonError(error, getErrorStatus(error));
  }

  const stream = new ReadableStream<Uint8Array>({
    async start(controller) {
      const encoder = new TextEncoder();
      const send = (event: OptimizationStreamEvent) => {
        controller.enqueue(encoder.encode(encodeOptimizationStreamEvent(event)));
      };

      let jobId: string | undefined;
      let caseId: string | undefined;
      let stage: OptimizationStreamStage = "job";
      const llmAuditSummary: LlmAuditSummary[] = [];
      const processSummary: ProcessSummaryStep[] = [];
      const requestStartedAt = Date.now();

      try {
        const repository = getRepositoryFromRuntime();
        const caseSummary = buildArticleCaseSummary({
          source_title: normalized.articleInput.title,
          source_body: normalized.articleInput.body,
          publish_platform: normalized.articleInput.platform,
        });
        const optimizationCase = await repository.createOptimizationCase({
          case_type: "article",
          ...caseSummary,
        });
        caseId = optimizationCase.id;
        const job = await repository.createArticleJob({
          case_id: optimizationCase.id,
          source_title: normalized.articleInput.title,
          source_body: normalized.articleInput.body,
          image_inputs: normalized.articleInput.images,
          publish_platform: normalized.articleInput.platform,
          user_instructions: normalized.articleInput.user_instructions,
        });
        jobId = job.id;
        await repository.saveCaseInput({
          case_id: optimizationCase.id,
          case_type: "article",
          article_job_id: job.id,
          payload: {
            source_title: normalized.articleInput.title,
            source_body: normalized.articleInput.body,
            image_inputs: normalized.articleInput.images,
            publish_platform: normalized.articleInput.platform,
            user_instructions: normalized.articleInput.user_instructions,
          },
        });
        send({
          type: "job_created",
          job: { id: job.id },
          case: { id: optimizationCase.id, case_type: "article" },
        });

        stage = "fact_card";
        const factCardStartedAt = Date.now();
        const factCard = optimizationFactCardSchema.parse(
          payload.fact_card ??
            (await extractCandidateFactCard(normalized.articleInput, {
              onAuditSummary: (summary) => {
                llmAuditSummary.push(summary);
              },
            })),
        );
        processSummary.push(
          createProcessStep({
            stage: "fact_card",
            startedAt: factCardStartedAt,
            endedAt: Date.now(),
            status: "success",
            producedResultVersion: false,
          }),
        );
        const savedFactCard = await repository.saveFactCard(job.id, factCard);
        send({
          type: "fact_card_ready",
          job_id: job.id,
          fact_card: savedFactCard,
        });

        const result = await runStreamingOptimizationWorkflow({
          jobId: job.id,
          input: normalized.articleInput,
          factCard: savedFactCard,
          onEvent: (event) => {
            stage = stageForEvent(event, stage);
            send(event);
          },
          onAuditSummary: (summary) => {
            llmAuditSummary.push(summary);
          },
        });

        stage = "final";
        const optimizedArticle = await repository.saveOptimizedArticle(
          job.id,
          result.article,
        );
        const qaReport = await repository.saveQaReport(
          job.id,
          optimizedArticle.revision ?? 1,
          result.qaReport,
        );
        const exportStore = getExportStoreFromRuntime();
        const exportPaths = await exportStore.writeJobExports({
          jobId: job.id,
          article: optimizedArticle,
          qaReport,
        });
        await repository.updateArticleJob(job.id, {
          status: "optimized",
          export_paths: exportPaths,
        });
        const resultVersion = await repository.createOptimizationResultVersion({
          case_id: optimizationCase.id,
          case_type: "article",
          status: "optimized",
          article_job_id: job.id,
          article_revision: optimizedArticle.revision ?? 1,
          result_summary: optimizedArticle.summary,
          payload: {
            article: optimizedArticle,
            qa_report: qaReport,
            export_paths: exportPaths,
          },
          process_summary: [...processSummary, ...result.processSummary],
          llm_audit_summary: llmAuditSummary,
          error_stage: null,
          error_summary: null,
        });
        send({
          type: "final_ready",
          job_id: job.id,
          case: { id: optimizationCase.id, case_type: "article" },
          result_version: { id: resultVersion.id, version: resultVersion.version },
          optimized_article: optimizedArticle,
          qa_report: qaReport,
          export_paths: exportPaths,
        });
      } catch (error) {
        const message = error instanceof Error ? error.message : "优化失败";
        let failedVersion:
          | { id: string; version: number }
          | undefined;
        if (caseId) {
          try {
            const repository = getRepositoryFromRuntime();
            const version = await repository.createOptimizationResultVersion({
              case_id: caseId,
              case_type: "article",
              status: "failed",
              article_job_id: jobId ?? null,
              article_revision: null,
              result_summary: "",
              payload: null,
              process_summary: [
                ...processSummary,
                createProcessStep({
                  stage,
                  startedAt: requestStartedAt,
                  endedAt: Date.now(),
                  status: "failed",
                  errorSummary: message,
                  producedResultVersion: false,
                }),
              ],
              llm_audit_summary: llmAuditSummary,
              error_stage: stage,
              error_summary: message,
            });
            failedVersion = { id: version.id, version: version.version };
          } catch {
            failedVersion = undefined;
          }
        }
        send({
          type: "failed",
          job_id: jobId,
          case: caseId ? { id: caseId, case_type: "article" } : undefined,
          result_version: failedVersion,
          stage,
          error: message,
        });
      } finally {
        controller.close();
      }
    },
  });

  return new Response(stream, {
    headers: {
      "content-type": "application/x-ndjson; charset=utf-8",
      "cache-control": "no-cache, no-transform",
    },
  });
}

function hasOptimizableBody(payload: unknown): payload is OptimizeStreamPayload {
  if (!payload || typeof payload !== "object" || Array.isArray(payload)) {
    return false;
  }

  const body = (payload as Partial<OptimizeStreamPayload>).body;
  return typeof body === "string" && body.trim().length > 0;
}

function stageForEvent(
  event: OptimizationStreamEvent,
  fallback: OptimizationStreamStage,
): OptimizationStreamStage {
  switch (event.type) {
    case "draft_started":
    case "draft_ready":
      return "draft";
    case "qa_started":
    case "qa_ready":
      return "qa";
    case "rewrite_started":
    case "rewrite_ready":
      return "rewrite";
    default:
      return fallback;
  }
}

function jsonError(error: unknown, status: number) {
  const message = error instanceof Error ? error.message : "Request failed";
  return NextResponse.json({ error: message }, { status });
}

function getErrorStatus(error: unknown) {
  if (error instanceof LlmValidationError) return 502;
  if (error instanceof Error && /^LLM\b|provider/i.test(error.message)) return 502;
  return 400;
}
