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

vi.mock("next/cache", async () => ({ revalidatePath: (await import("@/tests/helpers/next-mocks")).revalidatePath }));
vi.mock("next/navigation", async () => ({ redirect: (await import("@/tests/helpers/next-mocks")).redirect }));
vi.mock("next/dist/client/components/redirect-error", async () => ({
  isRedirectError: (await import("@/tests/helpers/next-mocks")).isRedirectError,
}));
vi.mock("@/lib/admin-auth", async () => {
  const m = await import("@/tests/helpers/next-mocks");
  return { isAdminAuthenticated: m.isAdminAuthenticated, clearAdminSessionCookie: m.clearAdminSessionCookie };
});

import {
  deleteCategoryAction,
  deleteProjectAction,
  saveProjectAction,
  upsertCategoryAction,
} from "@/app/_admin/portfolio/actions";
import { and, eq } from "drizzle-orm";

import { db } from "@/lib/db";
import {
  category as categoryTable,
  mediaUsage as mediaUsageTable,
  portfolioAsset as portfolioAssetTable,
  portfolioProject as portfolioProjectTable,
} from "@/lib/db/schema";
import { createCategory, createProject } from "@/tests/helpers/factories";
import { adminAuth, captureRedirect, formDataFrom, resetNextMocks } from "@/tests/helpers/next-mocks";

beforeEach(() => {
  resetNextMocks();
});

function categoryForm(overrides: Record<string, string> = {}) {
  return formDataFrom({
    slug: "branding",
    nameAr: "الهوية",
    nameEn: "Branding",
    nameDe: "Branding",
    descriptionAr: "وصف",
    descriptionEn: "Description",
    descriptionDe: "Beschreibung",
    sortOrder: "1",
    isActive: "on",
    ...overrides,
  });
}

function projectForm(categoryId: string, overrides: Record<string, string> = {}) {
  const assets = JSON.stringify([
    {
      kind: "IMAGE",
      altAr: "ع",
      altEn: "Alt",
      altDe: "Alt",
      sortOrder: 0,
      media: { mode: "external", url: "https://cdn/asset.png", kind: "IMAGE", label: "Asset" },
    },
  ]);
  const coverMedia = JSON.stringify({ mode: "external", url: "https://cdn/cover.png", kind: "IMAGE", label: "Cover" });
  return formDataFrom({
    categoryId,
    slug: "case-study",
    viewMode: "GRID",
    titleAr: "عنوان",
    titleEn: "Title",
    titleDe: "Titel",
    summaryAr: "ملخص",
    summaryEn: "Summary",
    summaryDe: "Zusammenfassung",
    clientName: "Client",
    projectYear: "2025",
    serviceLabelAr: "خدمة",
    serviceLabelEn: "Service",
    serviceLabelDe: "Service",
    previewUrl: "https://example.com",
    sortOrder: "0",
    isFeatured: "",
    isPublished: "on",
    sections: "[]",
    assets,
    coverMedia,
    ...overrides,
  });
}

describe("upsertCategoryAction", () => {
  it("creates a category", async () => {
    const url = await captureRedirect(() => upsertCategoryAction(categoryForm()));
    expect(url).toContain("success=");
    const category = await db.query.category.findFirst({ where: eq(categoryTable.slug, "branding") });
    expect(category?.nameEn).toBe("Branding");
    expect(category?.isActive).toBe(true);
  });

  it("updates an existing category", async () => {
    const existing = await createCategory({ slug: "old", nameEn: "Old" });
    const url = await captureRedirect(() =>
      upsertCategoryAction(categoryForm({ id: existing.id, slug: "old", nameEn: "Renamed" })),
    );
    expect(url).toContain("success=");
    const category = await db.query.category.findFirst({ where: eq(categoryTable.id, existing.id) });
    expect(category?.nameEn).toBe("Renamed");
  });

  it("reports a unique-constraint violation on duplicate slugs", async () => {
    await createCategory({ slug: "branding" });
    const url = await captureRedirect(() => upsertCategoryAction(categoryForm({ slug: "branding" })));
    expect(url).toContain("error=");
    expect(decodeURIComponent(url)).toContain("eindeutig");
  });

  it("reports validation errors for an invalid slug", async () => {
    const url = await captureRedirect(() => upsertCategoryAction(categoryForm({ slug: "Not Valid" })));
    expect(url).toContain("error=");
  });

  it("redirects unauthenticated callers to the admin root", async () => {
    adminAuth.authenticated = false;
    const url = await captureRedirect(() => upsertCategoryAction(categoryForm()));
    expect(url).toBe("/");
  });
});

describe("deleteCategoryAction", () => {
  it("refuses to delete a category that has projects", async () => {
    const category = await createCategory();
    await createProject({ categoryId: category.id });
    const url = await captureRedirect(() => deleteCategoryAction(formDataFrom({ id: category.id })));
    expect(url).toContain("error=");
    expect((await db.query.category.findFirst({ where: eq(categoryTable.id, category.id) })) ?? null).not.toBeNull();
  });

  it("deletes an empty category", async () => {
    const category = await createCategory();
    const url = await captureRedirect(() => deleteCategoryAction(formDataFrom({ id: category.id })));
    expect(url).toContain("success=");
    expect((await db.query.category.findFirst({ where: eq(categoryTable.id, category.id) })) ?? null).toBeNull();
  });
});

describe("saveProjectAction", () => {
  it("creates a published project with cover and asset media usages", async () => {
    const category = await createCategory();
    const url = await captureRedirect(() => saveProjectAction(projectForm(category.id)));
    expect(url).toContain("success=");

    const project = await db.query.portfolioProject.findFirst({ where: eq(portfolioProjectTable.slug, "case-study") });
    expect(project).not.toBeNull();
    expect(project?.isPublished).toBe(true);
    expect(project?.publishedAt).not.toBeNull();
    expect(project?.coverImagePath).toBe("https://cdn/cover.png");

    expect(await db.$count(portfolioAssetTable, eq(portfolioAssetTable.projectId, project!.id))).toBe(1);
    const usages = await db.select().from(mediaUsageTable).where(and(eq(mediaUsageTable.entityType, "portfolio-project"), eq(mediaUsageTable.entityId, project!.id)));
    const usageTypes = usages.map((u) => u.usageType).sort();
    expect(usageTypes).toEqual(["PORTFOLIO_ASSET", "PORTFOLIO_COVER"]);
  });

  it("updates an existing project and replaces its assets", async () => {
    const category = await createCategory();
    const created = await captureRedirect(() => saveProjectAction(projectForm(category.id)));
    void created;
    const project = await db.query.portfolioProject.findFirst({ where: eq(portfolioProjectTable.slug, "case-study") });

    const url = await captureRedirect(() =>
      saveProjectAction(projectForm(category.id, { id: project!.id, titleEn: "Updated Title" })),
    );
    expect(url).toContain("success=");
    const updated = await db.query.portfolioProject.findFirst({ where: eq(portfolioProjectTable.id, project!.id) });
    expect(updated?.titleEn).toBe("Updated Title");
    // assets are replaced, not duplicated
    expect(await db.$count(portfolioAssetTable, eq(portfolioAssetTable.projectId, project!.id))).toBe(1);
  });

  it("keeps the original publishedAt when re-saving an already published project", async () => {
    const category = await createCategory();
    await captureRedirect(() => saveProjectAction(projectForm(category.id)));
    const first = await db.query.portfolioProject.findFirst({ where: eq(portfolioProjectTable.slug, "case-study") });
    const originalPublishedAt = first!.publishedAt;

    await captureRedirect(() => saveProjectAction(projectForm(category.id, { id: first!.id })));
    const second = await db.query.portfolioProject.findFirst({ where: eq(portfolioProjectTable.id, first!.id) });
    expect(second?.publishedAt?.toISOString()).toBe(originalPublishedAt?.toISOString());
  });

  it("reports validation errors and creates nothing", async () => {
    const category = await createCategory();
    const url = await captureRedirect(() => saveProjectAction(projectForm(category.id, { titleEn: "" })));
    expect(url).toContain("error=");
    expect(await db.$count(portfolioProjectTable)).toBe(0);
  });

  it("reports a unique-constraint violation on duplicate slugs", async () => {
    const category = await createCategory();
    await createProject({ categoryId: category.id, slug: "case-study" });
    const url = await captureRedirect(() => saveProjectAction(projectForm(category.id)));
    expect(url).toContain("error=");
    expect(decodeURIComponent(url)).toContain("eindeutig");
  });

  it("redirects unauthenticated callers to the admin root", async () => {
    adminAuth.authenticated = false;
    const url = await captureRedirect(() => saveProjectAction(projectForm("cat")));
    expect(url).toBe("/");
  });
});

describe("deleteProjectAction", () => {
  it("deletes a project and its media usages", async () => {
    const project = await createProject();
    const url = await captureRedirect(() => deleteProjectAction(formDataFrom({ id: project.id })));
    expect(url).toContain("success=");
    expect((await db.query.portfolioProject.findFirst({ where: eq(portfolioProjectTable.id, project.id) })) ?? null).toBeNull();
  });

  it("errors when the project does not exist", async () => {
    const url = await captureRedirect(() => deleteProjectAction(formDataFrom({ id: "missing" })));
    expect(url).toContain("error=");
  });

  it("redirects unauthenticated callers to the admin root", async () => {
    adminAuth.authenticated = false;
    const url = await captureRedirect(() => deleteProjectAction(formDataFrom({ id: "x" })));
    expect(url).toBe("/");
  });
});
