import { mkdtempSync, rmSync } from "node:fs";
import { tmpdir } from "node:os";
import { join } from "node:path";

import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";

const llmMocks = vi.hoisted(() => ({
  generateValidatedJson: vi.fn(),
}));

vi.mock("../../../lib/llm/client", async () => {
  const actual = await vi.importActual<typeof import("../../../lib/llm/client")>(
    "../../../lib/llm/client",
  );
  return {
    ...actual,
    generateValidatedJson: llmMocks.generateValidatedJson,
  };
});

import { createSqliteRepository } from "../../../lib/db/sqlite-repository";
import {
  HUMAN_COPY_RUBRIC_V1,
  scoreHumanCopyResult,
} from "../../../lib/calibration/scoring";
import { GET as listCases } from "../cases/route";
import { GET as getCase, PATCH as patchCase } from "../cases/[caseId]/route";
import { POST as archiveCase } from "../cases/[caseId]/archive/route";
import { POST as restoreCase } from "../cases/[caseId]/restore/route";
import { POST as rerunCase } from "../cases/[caseId]/rerun/route";
import {
  GET as listVersionPublications,
  POST as createVersionPublication,
} from "../cases/[caseId]/versions/[versionId]/publications/route";
import { POST as recordPerformance } from "../publications/[publicationId]/performance/route";

describe("case APIs", () => {
  let tempDir: string;
  const originalDataDir = process.env.APP_DATA_DIR;
  const originalApiKey = process.env.API_ACCESS_KEY;
  const originalAuthDisabled = process.env.API_AUTH_DISABLED;

  beforeEach(() => {
    tempDir = mkdtempSync(join(tmpdir(), "geo-cases-api-"));
    process.env.APP_DATA_DIR = tempDir;
    process.env.API_ACCESS_KEY = "test-key";
    process.env.API_AUTH_DISABLED = "false";
  });

  afterEach(() => {
    process.env.APP_DATA_DIR = originalDataDir;
    process.env.API_ACCESS_KEY = originalApiKey;
    process.env.API_AUTH_DISABLED = originalAuthDisabled;
    llmMocks.generateValidatedJson.mockReset();
    rmSync(tempDir, { recursive: true, force: true });
  });

  it("lists, reads, patches, archives, and restores cases", async () => {
    const repository = createSqliteRepository();
    const created = await repository.createOptimizationCase({
      case_type: "human_copy",
      title: "人味文案优化：朋友圈",
      summary: "原文",
      publish_target: "朋友圈",
      source_excerpt: "原文",
    });

    const patchResponse = await patchCase(
      request({
        customer_name: "客户A",
        brand_name: "品牌B",
        project_tags: ["朋友圈"],
        notes: "保留口语。",
      }),
      params({ caseId: created.id }),
    );
    expect(patchResponse.status).toBe(200);

    const archiveResponse = await archiveCase(
      request({}),
      params({ caseId: created.id }),
    );
    expect(archiveResponse.status).toBe(200);

    const listResponse = await listCases(request({}));
    const listBody = (await listResponse.json()) as { cases: unknown[] };
    expect(listBody.cases).toHaveLength(0);

    const restoreResponse = await restoreCase(
      request({}),
      params({ caseId: created.id }),
    );
    expect(restoreResponse.status).toBe(200);

    const detailResponse = await getCase(
      request({}),
      params({ caseId: created.id }),
    );
    const detailBody = (await detailResponse.json()) as {
      case: { customer_name: string; project_tags: string[] };
    };
    expect(detailBody.case.customer_name).toBe("客户A");
    expect(detailBody.case.project_tags).toEqual(["朋友圈"]);
  });

  it("creates publication records for result versions", async () => {
    const repository = createSqliteRepository();
    const created = await repository.createOptimizationCase({
      case_type: "human_copy",
      title: "人味文案优化：私域",
      summary: "原文",
      publish_target: "私域",
      source_excerpt: "原文",
    });
    const version = await repository.createOptimizationResultVersion({
      case_id: created.id,
      case_type: "human_copy",
      status: "optimized",
      article_job_id: null,
      article_revision: null,
      result_summary: "优化后文案",
      payload: {
        optimized_text: "优化后文案",
        change_notes: [],
        ai_taste_checks: [],
        warnings: [],
      },
      process_summary: [],
      llm_audit_summary: [],
      error_stage: null,
      error_summary: null,
    });

    const createResponse = await createVersionPublication(
      request({
        publish_target: "私域",
        url: "https://example.com/private",
        published_at: "2026-07-08T12:00:00.000Z",
        notes: "客户发布",
      }),
      params({ caseId: created.id, versionId: version.id }),
    );
    expect(createResponse.status).toBe(201);

    const listResponse = await listVersionPublications(
      request({}),
      params({ caseId: created.id, versionId: version.id }),
    );
    const listBody = (await listResponse.json()) as {
      publications: Array<{ publish_target: string }>;
    };
    expect(listBody.publications).toEqual([
      expect.objectContaining({ publish_target: "私域" }),
    ]);
  });

  it("records manual performance for a result-version publication without scoring", async () => {
    const repository = createSqliteRepository();
    const created = await repository.createOptimizationCase({
      case_type: "human_copy",
      title: "人味文案优化：私域",
      summary: "原文",
      publish_target: "私域",
      source_excerpt: "原文",
    });
    const version = await repository.createOptimizationResultVersion({
      case_id: created.id,
      case_type: "human_copy",
      status: "optimized",
      article_job_id: null,
      article_revision: null,
      result_summary: "优化后文案",
      payload: {
        optimized_text: "优化后文案",
        change_notes: [],
        ai_taste_checks: [],
        warnings: [],
      },
      process_summary: [],
      llm_audit_summary: [],
      error_stage: null,
      error_summary: null,
    });
    const publication = await repository.createPublicationRecord({
      result_version_id: version.id,
      job_id: null,
      revision: null,
      publish_target: "私域",
      url: "https://example.com/private",
      published_at: "2026-07-08T12:00:00.000Z",
      status: "published",
      notes: "",
    });

    const response = await recordPerformance(
      request({
        window_label: "T+7d",
        views: "1200",
        feedback_summary: "客户反馈更自然",
      }),
      params({ publicationId: publication.id }),
    );
    const body = (await response.json()) as {
      snapshot?: { metrics: { views: number } };
      calibrationEvent: null;
    };

    expect(response.status).toBe(201);
    expect(body.snapshot?.metrics.views).toBe(1200);
    expect(body.calibrationEvent).toBeNull();
  });

  it("creates calibration events for scored human-copy result-version performance", async () => {
    const repository = createSqliteRepository();
    const created = await repository.createOptimizationCase({
      case_type: "human_copy",
      title: "人味文案优化：私域",
      summary: "原文",
      publish_target: "私域",
      source_excerpt: "原文",
    });
    const result = {
      optimized_text: "优化后文案",
      change_notes: [],
      ai_taste_checks: [],
      warnings: [],
    };
    const version = await repository.createOptimizationResultVersion({
      case_id: created.id,
      case_type: "human_copy",
      status: "optimized",
      article_job_id: null,
      article_revision: null,
      result_summary: "优化后文案",
      payload: result,
      process_summary: [],
      llm_audit_summary: [],
      error_stage: null,
      error_summary: null,
    });
    await repository.saveRubricVersion(HUMAN_COPY_RUBRIC_V1);
    await repository.saveScoringRun(
      scoreHumanCopyResult({
        resultVersionId: version.id,
        result,
      }),
    );
    const publication = await repository.createPublicationRecord({
      result_version_id: version.id,
      job_id: null,
      revision: null,
      publish_target: "私域",
      url: "https://example.com/private",
      published_at: "2026-07-08T12:00:00.000Z",
      status: "published",
      notes: "",
    });

    const response = await recordPerformance(
      request({
        window_label: "T+7d",
        views: "1200",
        feedback_summary: "客户反馈更自然",
      }),
      params({ publicationId: publication.id }),
    );
    const body = (await response.json()) as {
      calibrationEvent: { observations: string[] } | null;
    };

    expect(response.status).toBe(201);
    expect(body.calibrationEvent?.observations.join(" ")).toContain("综合评分");
  });

  it("reruns an article case into a new result version and article job", async () => {
    const repository = createSqliteRepository();
    const created = await repository.createOptimizationCase({
      case_type: "article",
      title: "GEO 指南",
      summary: "原文摘要",
      publish_target: "official_site",
      source_excerpt: "原文摘要",
    });
    const sourceJob = await repository.createArticleJob({
      case_id: created.id,
      source_title: "GEO 指南",
      source_body: "Example Technology Co., Ltd. has 8 years of GEO experience.",
      image_inputs: [{ type: "description", content: "dashboard" }],
      publish_platform: "official_site",
      user_instructions: "保持事实准确",
    });
    await repository.saveFactCard(sourceJob.id, confirmedFactCard);
    await repository.saveCaseInput({
      case_id: created.id,
      case_type: "article",
      article_job_id: sourceJob.id,
      payload: {
        source_title: "GEO 指南",
        source_body: "Example Technology Co., Ltd. has 8 years of GEO experience.",
        image_inputs: [{ type: "description", content: "dashboard" }],
        publish_platform: "official_site",
        user_instructions: "保持事实准确",
      },
    });
    await repository.createOptimizationResultVersion({
      case_id: created.id,
      case_type: "article",
      status: "optimized",
      article_job_id: null,
      article_revision: null,
      result_summary: "第一版",
      payload: null,
      process_summary: [],
      llm_audit_summary: [],
      error_stage: null,
      error_summary: null,
    });

    llmMocks.generateValidatedJson
      .mockResolvedValueOnce({
        title: "第二版 GEO 指南",
        summary: "第二版 official site GEO optimization 摘要",
        body_markdown:
          "## 第二版\nExample Technology Co., Ltd. has 8 years of GEO optimization experience.",
        image_suggestions: [],
        changed_sections: ["标题"],
        requires_user_confirmation: [],
      })
      .mockResolvedValueOnce({
        overall_status: "pass",
        checks: [],
      });

    const response = await rerunCase(request({}), params({ caseId: created.id }));
    const body = (await response.json()) as {
      result_version: { version: number };
      optimizedArticle: { title: string; job_id: string };
      qaReport: { overall_status: string };
      exportPaths: Record<string, string>;
    };

    expect(response.status).toBe(201);
    expect(body.result_version.version).toBe(2);
    expect(body.optimizedArticle.title).toBe("第二版 GEO 指南");
    expect(body.qaReport.overall_status).toBe("pass");
    expect(body.exportPaths.markdown).toContain(body.optimizedArticle.job_id);
    await expect(repository.getArticleJob(body.optimizedArticle.job_id)).resolves.toMatchObject({
      case_id: created.id,
      status: "optimized",
    });
  });

  it("reruns a human-copy case into a new result version", async () => {
    const repository = createSqliteRepository();
    const created = await repository.createOptimizationCase({
      case_type: "human_copy",
      title: "人味文案优化：朋友圈",
      summary: "原文",
      publish_target: "朋友圈",
      source_excerpt: "原文",
    });
    await repository.saveCaseInput({
      case_id: created.id,
      case_type: "human_copy",
      article_job_id: null,
      payload: {
        source_text: "原文",
        goal: "自然一点",
        intensity: "light",
        user_instructions: "",
        publish_target: "朋友圈",
      },
    });
    await repository.createOptimizationResultVersion({
      case_id: created.id,
      case_type: "human_copy",
      status: "optimized",
      article_job_id: null,
      article_revision: null,
      result_summary: "第一版",
      payload: {
        optimized_text: "第一版",
        change_notes: [],
        ai_taste_checks: [],
        warnings: [],
      },
      process_summary: [],
      llm_audit_summary: [],
      error_stage: null,
      error_summary: null,
    });

    llmMocks.generateValidatedJson.mockResolvedValueOnce({
      optimized_text: "第二版",
      change_notes: [],
      ai_taste_checks: [],
      warnings: [],
    });

    const response = await rerunCase(request({}), params({ caseId: created.id }));
    const body = (await response.json()) as {
      result_version: { id: string; version: number };
      result: { optimized_text: string };
    };

    expect(response.status).toBe(201);
    expect(body.result_version.version).toBe(2);
    expect(body.result.optimized_text).toBe("第二版");
    await expect(
      repository.getLatestScoringRunForResultVersion(body.result_version.id),
    ).resolves.toMatchObject({
      case_type: "human_copy",
      rubric_version_id: HUMAN_COPY_RUBRIC_V1.id,
    });
  });
});

const confirmedFactCard = {
  company_full_name: "Example Technology Co., Ltd.",
  company_short_names: ["Example Tech"],
  brand_names: ["Example"],
  product_names: ["Example GEO"],
  target_industry: "GEO optimization",
  target_audience: "Marketing teams",
  experience_years: 8,
  core_claims: ["8 years of GEO optimization experience"],
  forbidden_claims: ["industry first"],
  image_topics: ["dashboard"],
  uncertain_items: [],
  is_ready_for_optimization: true,
  confirmed_by_user: true,
} as const;

function request(body: unknown, options: { apiKey?: string | null } = {}) {
  const headers: Record<string, string> = { "content-type": "application/json" };
  const apiKey = options.apiKey === undefined ? "test-key" : options.apiKey;
  if (apiKey) headers["x-api-key"] = apiKey;
  return new Request("http://localhost/api/cases", {
    method: "POST",
    body: JSON.stringify(body),
    headers,
  });
}

function params<T extends Record<string, string>>(values: T) {
  return { params: Promise.resolve(values) };
}
