/**
 * End-to-end style workflow tests for a Người nộp đơn (viewer) account:
 * API → application detail + draft bundle → review UI.
 * Uses Vitest + RTL; real network is not required (apiClient + auth are mocked).
 */
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 ApplicationReviewPage from "./ApplicationReviewPage";

const applicantUser: User = {
  id: "applicant-user-1",
  email: "applicant@e2e.test",
  name: "Applicant E2E",
  roles: ["viewer"],
  availableRoles: ["viewer"],
  permissions: getPermissionsForRoles(["viewer"]),
};

function createMockUseAuth(overrides: Partial<ReturnType<typeof buildMockAuth>> = {}) {
  return buildMockAuth(overrides);
}

function buildMockAuth(overrides: Partial<ReturnType<typeof buildMockAuth>> = {}) {
  return {
    user: applicantUser,
    loading: false,
    isAuthenticated: true,
    isDemoMode: false,
    hasPermission: (permission: Permission) => applicantUser.permissions.includes(permission),
    hasAnyPermission: (ps: Permission[]) => ps.some((p) => applicantUser.permissions.includes(p)),
    hasRole: (r: "admin" | "editor" | "viewer") => 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(),
    ...overrides,
  };
}

const mockUseAuth = vi.fn(() => createMockUseAuth());

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" />;
  },
}));

const SAMPLE_APP = {
  id: "app-e2e-1",
  name: "Sáng kiến thử nghiệm E2E",
  submittedDate: "2026-04-20T10:00:00.000Z",
  author: { id: "applicant-user-1", name: "Applicant E2E", email: "applicant@e2e.test" },
  status: "pending" as const,
  reviewStatus: "not_reviewed" as const,
  draftCaseId: "app-e2e-1",
};

const SAMPLE_DRAFT_BUNDLE = {
  caseId: "app-e2e-1",
  updatedAt: "2026-04-20T10:00:00.000Z",
  tabs: {
    report: {
      initiativeName: "Sáng kiến thử nghiệm E2E",
      introduction: "Đoạn mở đầu từ bản nháp Postgres.",
      authorName: "Applicant E2E",
    },
    application: {
      initiativeName: "Sáng kiến thử nghiệm E2E",
    },
    contribution: {
      initiativeName: "Sáng kiến thử nghiệm E2E",
      mainAuthor: "Applicant E2E",
      participants: [],
    },
  },
};

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

function renderReviewPage(search: string) {
  const client = makeQueryClient();
  return render(
    <QueryClientProvider client={client}>
      <MemoryRouter initialEntries={[`/dashboard/documents/review${search ? `?${search}` : ""}`]}>
        <Routes>
          <Route path="/dashboard/documents/review" element={<ApplicationReviewPage />} />
        </Routes>
      </MemoryRouter>
    </QueryClientProvider>,
  );
}

describe("Applicant account workflow (integration)", () => {
  const getSpy = vi.spyOn(apiClient, "get");

  beforeEach(() => {
    mockUseAuth.mockImplementation(() => createMockUseAuth());
    getSpy.mockImplementation((url: string) => {
      if (url.includes("/api/applications/mine")) {
        return Promise.resolve([SAMPLE_APP]);
      }
      if (url === `/api/applications/${SAMPLE_APP.id}` || url.endsWith(`applications/${SAMPLE_APP.id}`)) {
        return Promise.resolve(SAMPLE_APP);
      }
      if (url.includes("/api/v1/application-drafts/")) {
        return Promise.resolve(SAMPLE_DRAFT_BUNDLE);
      }
      return Promise.reject(new Error(`Unmocked GET: ${url}`));
    });
  });

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

  it("loads list + detail + draft for viewer and shows review header with case id on /documents/review", async () => {
    const params = new URLSearchParams();
    params.set("applicationId", SAMPLE_APP.id);
    params.set("returnTo", "/dashboard");
    renderReviewPage(params.toString());

    await waitFor(
      () => {
        expect(screen.getByText(new RegExp(SAMPLE_APP.id))).toBeInTheDocument();
      },
      { timeout: 8000 },
    );

    expect(screen.getByText("Hồ sơ người nộp đơn")).toBeInTheDocument();
    expect(screen.getByRole("tab", { name: /Báo cáo Mô tả Sáng kiến/i })).toBeInTheDocument();
    expect(screen.getByRole("tab", { name: /Đơn Đề nghị Công nhận/i })).toBeInTheDocument();
    expect(screen.getByRole("tab", { name: /Xác nhận Tỷ lệ Đóng góp/i })).toBeInTheDocument();
  });

  it("fetches application detail then Postgres draft bundle when opening /documents/review", async () => {
    const params = new URLSearchParams();
    params.set("applicationId", SAMPLE_APP.id);
    params.set("returnTo", "/dashboard");
    renderReviewPage(params.toString());

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

  it("documents list URL without applicationId loads mine only (no application detail)", async () => {
    const client = makeQueryClient();
    render(
      <QueryClientProvider client={client}>
        <MemoryRouter initialEntries={["/dashboard/documents?returnTo=%2Fdashboard"]}>
          <Routes>
            <Route path="/dashboard/documents" element={<ApplicationReviewPage />} />
          </Routes>
        </MemoryRouter>
      </QueryClientProvider>,
    );

    await waitFor(() => {
      expect(getSpy.mock.calls.some((c) => c[0] === "/api/applications/mine")).toBe(true);
    });

    const detailFetches = getSpy.mock.calls.filter(
      (c) => String(c[0]).includes(`/api/applications/${SAMPLE_APP.id}`) && !String(c[0]).includes("mine"),
    );
    expect(detailFetches.length).toBe(0);
  });
});
