import "dotenv/config";
import { describe, it, expect, beforeEach, vi } from "vitest";
import { signAccessToken } from "@/features/auth/authCrypto";

// /api/dashboard/overview TDD
// - 로그인한 사용자가 자신의 프로젝트/폼 제출 통계를 조회할 수 있어야 한다.
// - 로그인하지 않은 경우 401 을 반환해야 한다.
// - 프로젝트/제출 내역이 없는 경우 0 값과 빈 배열을 반환해야 한다.

const BASE_URL = "http://localhost";

const inMemoryProjects: any[] = [];
const inMemoryFormSubmissions: any[] = [];

vi.mock("@prisma/client", () => {
  class PrismaClientMock {
    project = {
      findMany: async (args: any = {}) => {
        const where = args.where ?? {};

        let items = [...inMemoryProjects];

        if (where.userId) {
          items = items.filter((p) => p.userId === where.userId);
        }

        return items;
      },
    };

    formSubmission = {
      findMany: async (args: any = {}) => {
        const where = args.where ?? {};

        let items = [...inMemoryFormSubmissions];

        if (where.userId) {
          items = items.filter((s) => s.userId === where.userId);
        }

        return items;
      },
    };
  }

  return { PrismaClient: PrismaClientMock };
});

const TEST_USER = { id: "user-1", email: "dashboard@example.com", tokenVersion: 1 };
const OTHER_USER = { id: "user-2", email: "dashboard2@example.com", tokenVersion: 1 };

async function buildAuthHeadersFor(user: { id: string; email: string; tokenVersion: number }) {
  const token = await signAccessToken(user);
  return { cookie: `pb_access=${token}` };
}

async function buildAuthHeaders() {
  return buildAuthHeadersFor(TEST_USER);
}

beforeEach(() => {
  process.env.AUTH_JWT_SECRET = "test-jwt-secret-dashboard";
  inMemoryProjects.length = 0;
  inMemoryFormSubmissions.length = 0;
});

describe("/api/dashboard/overview", () => {
  it("로그인한 사용자가 자신의 프로젝트/폼 제출 통계를 조회할 수 있어야 한다", async () => {
    const project1 = {
      id: "proj-1",
      userId: TEST_USER.id,
      title: "테스트 프로젝트 A",
      slug: "test-project-a",
      status: "DRAFT",
    };

    const project2 = {
      id: "proj-2",
      userId: TEST_USER.id,
      title: "테스트 프로젝트 B",
      slug: "test-project-b",
      status: "PUBLISHED",
    };

    const projectOther = {
      id: "proj-3",
      userId: OTHER_USER.id,
      title: "다른 유저 프로젝트",
      slug: "other-project",
      status: "DRAFT",
    };

    inMemoryProjects.push(project1, project2, projectOther);

    const now = new Date("2025-01-03T12:00:00.000Z");
    const yesterday = new Date("2025-01-02T12:00:00.000Z");
    const eightDaysAgo = new Date("2024-12-26T12:00:00.000Z");

    inMemoryFormSubmissions.push(
      {
        id: "sub-1",
        projectId: project1.id,
        projectSlug: project1.slug,
        userId: TEST_USER.id,
        createdAt: now,
        payloadJson: { message: "A-1" },
        sensitiveEnc: undefined,
        metaJson: null,
      },
      {
        id: "sub-2",
        projectId: project1.id,
        projectSlug: project1.slug,
        userId: TEST_USER.id,
        createdAt: yesterday,
        payloadJson: { message: "A-2" },
        sensitiveEnc: undefined,
        metaJson: null,
      },
      {
        id: "sub-3",
        projectId: project2.id,
        projectSlug: project2.slug,
        userId: TEST_USER.id,
        createdAt: eightDaysAgo,
        payloadJson: { message: "B-1" },
        sensitiveEnc: undefined,
        metaJson: null,
      },
      {
        id: "sub-4",
        projectId: projectOther.id,
        projectSlug: projectOther.slug,
        userId: OTHER_USER.id,
        createdAt: now,
        payloadJson: { message: "OTHER" },
        sensitiveEnc: undefined,
        metaJson: null,
      },
    );

    const { GET: getDashboardOverview } = await import("@/app/api/dashboard/overview/route");

    const headers = await buildAuthHeaders();

    const res = await getDashboardOverview(
      new Request(`${BASE_URL}/api/dashboard/overview`, {
        headers,
      }),
    );

    expect(res.status).toBe(200);

    const json = (await res.json()) as any;

    expect(json.summaryStats.totalProjects).toBe(2);
    expect(json.summaryStats.totalSubmissions).toBe(3);

    expect(Array.isArray(json.projectStats)).toBe(true);
    expect(json.projectStats.length).toBe(2);

    const proj1Stats = json.projectStats.find((p: any) => p.slug === project1.slug);
    const proj2Stats = json.projectStats.find((p: any) => p.slug === project2.slug);

    expect(proj1Stats).toBeTruthy();
    expect(proj1Stats.totalSubmissions).toBe(2);
    expect(proj1Stats.lastSubmissionAt).toBe(now.toISOString());

    expect(proj2Stats).toBeTruthy();
    expect(proj2Stats.totalSubmissions).toBe(1);
    expect(proj2Stats.lastSubmissionAt).toBe(eightDaysAgo.toISOString());

    const daily = (json as any).dailySubmissions;

    expect(Array.isArray(daily)).toBe(true);
    expect(daily.length).toBe(3);

    const dates = daily.map((item: any) => item.date).sort();
    expect(dates).toEqual(["2024-12-26", "2025-01-02", "2025-01-03"]);

    const countsByDate = (daily as any[]).reduce((acc: Record<string, number>, item: any) => {
      acc[item.date] = item.count;
      return acc;
    }, {} as Record<string, number>);

    expect(countsByDate["2025-01-03"]).toBe(1);
    expect(countsByDate["2025-01-02"]).toBe(1);
    expect(countsByDate["2024-12-26"]).toBe(1);
  });

  it("로그인하지 않은 경우 401 을 반환해야 한다", async () => {
    const { GET: getDashboardOverview } = await import("@/app/api/dashboard/overview/route");

    const res = await getDashboardOverview(
      new Request(`${BASE_URL}/api/dashboard/overview`),
    );

    expect(res.status).toBe(401);
  });

  it("프로젝트와 제출 내역이 없는 경우 0 값과 빈 배열을 반환해야 한다", async () => {
    const { GET: getDashboardOverview } = await import("@/app/api/dashboard/overview/route");

    const headers = await buildAuthHeaders();

    const res = await getDashboardOverview(
      new Request(`${BASE_URL}/api/dashboard/overview`, {
        headers,
      }),
    );

    expect(res.status).toBe(200);

    const json = (await res.json()) as any;
    expect(json.summaryStats.totalProjects).toBe(0);
    expect(json.summaryStats.totalSubmissions).toBe(0);
    expect(Array.isArray(json.projectStats)).toBe(true);
    expect(json.projectStats.length).toBe(0);
  });
});
