/**
 * Unit / integration tests: staff-only admin review page (auth gate + application + draft load).
 */
import { describe, it, expect, beforeEach, vi, afterEach } from "vitest";
import { render, screen, waitFor } from "@testing-library/react";
import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
import { MemoryRouter, Route, Routes } from "react-router-dom";
import type { ReactNode } from "react";
import { apiClient } from "@/shared/api/client";
import { getPermissionsForRoles, type Permission } from "@/lib/permissions";
import type { User } from "@/contexts/AuthContext";
import AdminApplicationReviewPage from "@/pages/AdminApplicationReviewPage";

const adminUser: User = {
  id: "admin-unit-1",
  email: "admin@unit.test",
  name: "Admin Unit",
  roles: ["admin"],
  availableRoles: ["admin"],
  permissions: getPermissionsForRoles(["admin"]),
};

const viewerUser: User = {
  id: "viewer-unit-1",
  email: "viewer@unit.test",
  name: "Viewer Unit",
  roles: ["viewer"],
  availableRoles: ["viewer"],
  permissions: getPermissionsForRoles(["viewer"]),
};

function buildAuthForUser(user: User, roleCheck: "admin" | "editor" | "viewer") {
  return {
    user,
    loading: false,
    isAuthenticated: true,
    isDemoMode: false,
    hasPermission: (permission: Permission) => user.permissions.includes(permission),
    hasAnyPermission: (ps: Permission[]) => ps.some((p) => user.permissions.includes(p)),
    hasRole: (r: "admin" | "editor" | "viewer") => {
      if (roleCheck === "admin") return r === "admin";
      if (roleCheck === "editor") return r === "editor";
      return r === "viewer";
    },
    login: vi.fn().mockResolvedValue({ success: true }),
    register: vi.fn().mockResolvedValue({ success: true }),
    logout: vi.fn().mockResolvedValue(undefined),
    setDemoUser: vi.fn(),
    clearDemoUser: vi.fn(),
    updateUserFields: vi.fn(),
    applyAuthUser: vi.fn().mockReturnValue({ success: true }),
  };
}

const mockUseAuth = vi.fn(() => buildAuthForUser(adminUser, "admin"));

vi.mock("@/contexts/AuthContext", () => ({
  useAuth: () => mockUseAuth(),
  AuthProvider: ({ children }: { children: ReactNode }) => <>{children}</>,
}));

vi.mock("@/components/ChatAssistant", () => ({
  default: function MockChat() {
    return <div data-testid="chat-assistant-mock" />;
  },
}));

vi.mock("@/components/application-review/ReviewFormsPanel", () => ({
  ReviewFormsPanel: function MockReviewFormsPanel(props: { caseKey: string }) {
    return <div data-testid="review-forms-panel" data-case-key={props.caseKey} />;
  },
}));

const SAMPLE_APP = {
  id: "sub-admin-unit-abc123",
  name: "Hồ sơ unit test",
  submittedDate: "2026-04-27T10:00:00.000Z",
  author: { id: "SUB-CASE-UNIT-001", name: "Tác giả", email: "a@unit.test" },
  status: "pending" as const,
  reviewStatus: "not_reviewed" as const,
  draft_case_id: "SUB-CASE-UNIT-001",
};

const SAMPLE_DRAFT = {
  caseId: "SUB-CASE-UNIT-001",
  updatedAt: "2026-04-27T10:00:00.000Z",
  tabs: {
    report: { initiativeName: SAMPLE_APP.name },
    application: {},
    contribution: {},
  },
};

function makeQueryClient() {
  return new QueryClient({
    defaultOptions: {
      queries: { retry: false },
      mutations: { retry: false },
    },
  });
}

function renderAdminReview(search: string) {
  const client = makeQueryClient();
  return render(
    <QueryClientProvider client={client}>
      <MemoryRouter initialEntries={[`/dashboard/admin/applications/review${search}`]}>
        <Routes>
          <Route path="/dashboard/admin/applications/review" element={<AdminApplicationReviewPage />} />
          <Route path="/unauthorized" element={<div>Unauthorized</div>} />
          <Route path="/dashboard" element={<div>Dashboard home</div>} />
        </Routes>
      </MemoryRouter>
    </QueryClientProvider>,
  );
}

describe("AdminApplicationReviewPage", () => {
  const getSpy = vi.spyOn(apiClient, "get");

  beforeEach(() => {
    mockUseAuth.mockImplementation(() => buildAuthForUser(adminUser, "admin"));
    getSpy.mockImplementation((url: unknown) => {
      const u = String(url);
      if (u.includes(`/api/applications/${encodeURIComponent(SAMPLE_APP.id)}`)) {
        return Promise.resolve(SAMPLE_APP);
      }
      if (u.includes("/api/v1/application-drafts/")) {
        return Promise.resolve(SAMPLE_DRAFT);
      }
      return Promise.reject(new Error(`Unmocked GET: ${u}`));
    });
  });

  afterEach(() => {
    getSpy.mockReset();
    vi.clearAllMocks();
  });

  it("sends viewers to /unauthorized (non-staff cannot review)", async () => {
    mockUseAuth.mockImplementation(() => buildAuthForUser(viewerUser, "viewer"));
    renderAdminReview(`?applicationId=${encodeURIComponent(SAMPLE_APP.id)}`);

    await waitFor(() => {
      expect(screen.getByText("Unauthorized")).toBeInTheDocument();
    });
    expect(getSpy).not.toHaveBeenCalled();
  });

  it("redirects to dashboard when applicationId is missing", async () => {
    renderAdminReview("");

    await waitFor(() => {
      expect(screen.getByText("Dashboard home")).toBeInTheDocument();
    });
  });

  it("loads application detail and draft bundle with staff syncLocalSubmission off (admin can read full hồ sơ)", async () => {
    renderAdminReview(
      `?applicationId=${encodeURIComponent(SAMPLE_APP.id)}&returnTo=${encodeURIComponent("/dashboard")}`,
    );

    await waitFor(() => {
      expect(screen.getByRole("heading", { name: /Xem hồ sơ đã nộp \(quản trị\)/i })).toBeInTheDocument();
    });

    await waitFor(() => {
      expect(getSpy.mock.calls.some((c) => String(c[0]).includes("/api/applications/"))).toBe(true);
    });
    await waitFor(() => {
      expect(getSpy.mock.calls.some((c) => String(c[0]).includes("/api/v1/application-drafts/"))).toBe(true);
    });

    const panel = await screen.findByTestId("review-forms-panel");
    expect(panel.getAttribute("data-case-key")).toBe(SAMPLE_APP.draft_case_id);
  });

  it("uses encodeURIComponent for application id in GET /api/applications", async () => {
    const id = "sub-weird/id";
    mockUseAuth.mockImplementation(() => buildAuthForUser(adminUser, "admin"));
    getSpy.mockImplementation((url: unknown) => {
      const u = String(url);
      if (u === `/api/applications/${encodeURIComponent(id)}`) {
        return Promise.resolve({
          ...SAMPLE_APP,
          id,
          draft_case_id: "CASE-X",
          author: { ...SAMPLE_APP.author, id: "CASE-X" },
        });
      }
      if (u.includes("/api/v1/application-drafts/CASE-X")) {
        return Promise.resolve({ ...SAMPLE_DRAFT, caseId: "CASE-X" });
      }
      return Promise.reject(new Error(`Unmocked GET: ${u}`));
    });

    renderAdminReview(`?applicationId=${encodeURIComponent(id)}`);

    await waitFor(() => {
      expect(
        getSpy.mock.calls.some((c) => c[0] === `/api/applications/${encodeURIComponent(id)}`),
      ).toBe(true);
    });
  });
});
