/**
 * Component tests for admin page: render, access denied, duty list, reassign sheet.
 */

import { describe, it, expect, beforeEach, vi } from "vitest";
import { render, screen, waitFor, fireEvent } from "@testing-library/react";
import AdminPage from "./page";
import { resetAppStore } from "@/test/test-utils";
import { useAppStore } from "@/store/app-store";

vi.mock("@/hooks/use-telegram-auth", () => ({
  useTelegramAuth: vi.fn(),
}));

vi.mock("next/navigation", () => ({
  useRouter: vi.fn(() => ({ push: vi.fn() })),
}));

const mockUseTelegramAuth = vi.mocked(
  await import("@/hooks/use-telegram-auth").then((m) => m.useTelegramAuth)
);

const sampleUsers = [
  { id: 1, full_name: "Alice", username: "alice", role_id: 1 },
  { id: 2, full_name: "Bob", username: null, role_id: 2 },
];

const sampleDuties: Array<{
  id: number;
  user_id: number;
  start_at: string;
  end_at: string;
  full_name: string;
  event_type: string;
  phone: string | null;
  username: string | null;
}> = [
  {
    id: 10,
    user_id: 1,
    start_at: "2030-01-15T09:00:00Z",
    end_at: "2030-01-15T18:00:00Z",
    full_name: "Alice",
    event_type: "duty",
    phone: null,
    username: "alice",
  },
];

function mockFetchForAdmin(
  users: Array<{ id: number; full_name: string; username: string | null; role_id?: number }> = sampleUsers,
  duties = sampleDuties,
  options?: { adminMe?: { is_admin: boolean } }
) {
  const adminMe = options?.adminMe ?? { is_admin: true };
  vi.stubGlobal(
    "fetch",
    vi.fn((url: string, _init?: RequestInit) => {
      if (url.includes("/api/admin/me")) {
        return Promise.resolve({
          ok: true,
          status: 200,
          json: () => Promise.resolve(adminMe),
        } as Response);
      }
      if (url.includes("/api/admin/users")) {
        return Promise.resolve({
          ok: true,
          status: 200,
          json: () => Promise.resolve(users),
        } as Response);
      }
      if (url.includes("/api/duties")) {
        return Promise.resolve({
          ok: true,
          status: 200,
          json: () => Promise.resolve(duties),
        } as Response);
      }
      return Promise.reject(new Error(`Unexpected URL: ${url}`));
    })
  );
}

describe("AdminPage", () => {
  beforeEach(() => {
    resetAppStore();
    useAppStore.getState().setCurrentMonth(new Date(2025, 0, 1));
    mockUseTelegramAuth.mockReturnValue({
      initDataRaw: "test-init",
      startParam: undefined,
      isLocalhost: true,
    });
  });

  it("shows admin title (month/year) when allowed and data loaded", async () => {
    mockFetchForAdmin();
    render(<AdminPage />);
    await waitFor(() => {
      expect(screen.getByRole("heading", { name: /admin|админка/i })).toBeInTheDocument();
    });
  });

  it("shows access denied when fetchAdminMe returns is_admin false", async () => {
    mockFetchForAdmin(sampleUsers, [], { adminMe: { is_admin: false } });
    render(<AdminPage />);
    await waitFor(
      () => {
        expect(
          screen.getByText(/Access only for administrators|Доступ только для администраторов/i)
        ).toBeInTheDocument();
      },
      { timeout: 2000 }
    );
  });

  it("shows access denied message when GET /api/admin/users returns 403", async () => {
    vi.stubGlobal(
      "fetch",
      vi.fn((url: string) => {
        if (url.includes("/api/admin/me")) {
          return Promise.resolve({
            ok: true,
            status: 200,
            json: () => Promise.resolve({ is_admin: true }),
          } as Response);
        }
        if (url.includes("/api/admin/users")) {
          return Promise.resolve({
            ok: false,
            status: 403,
            json: () => Promise.resolve({ detail: "Admin only" }),
          } as Response);
        }
        if (url.includes("/api/duties")) {
          return Promise.resolve({
            ok: true,
            status: 200,
            json: () => Promise.resolve([]),
          } as Response);
        }
        return Promise.reject(new Error(`Unexpected: ${url}`));
      })
    );
    render(<AdminPage />);
    await waitFor(
      () => {
        expect(
          screen.getByText(/Admin only|Access only for administrators|Доступ только для администраторов/i)
        ).toBeInTheDocument();
      },
      { timeout: 2000 }
    );
  });

  it("shows duty row and opens reassign sheet on click", async () => {
    mockFetchForAdmin();
    render(<AdminPage />);
    await waitFor(() => {
      expect(screen.getByText("Alice")).toBeInTheDocument();
    });
    const dutyButton = screen.getByRole("button", { name: /Alice/ });
    fireEvent.click(dutyButton);
    await waitFor(() => {
      expect(
        screen.getByRole("radiogroup", { name: /select user|выберите пользователя/i })
      ).toBeInTheDocument();
    });
    expect(screen.getByRole("button", { name: /save|сохранить/i })).toBeInTheDocument();
  });

  it("shows no users message in sheet when usersForSelect is empty", async () => {
    mockFetchForAdmin([], sampleDuties);
    render(<AdminPage />);
    await waitFor(() => {
      expect(screen.getByText("Alice")).toBeInTheDocument();
    });
    fireEvent.click(screen.getByRole("button", { name: /Alice/ }));
    await waitFor(() => {
      expect(
        screen.getByText(/No users available for assignment|Нет пользователей для назначения/i)
      ).toBeInTheDocument();
    });
  });

  it("shows success message after successful reassign", async () => {
    mockFetchForAdmin();
    vi.stubGlobal(
      "fetch",
      vi.fn((url: string, init?: RequestInit) => {
        if (url.includes("/api/admin/me")) {
          return Promise.resolve({
            ok: true,
            status: 200,
            json: () => Promise.resolve({ is_admin: true }),
          } as Response);
        }
        if (url.includes("/api/admin/users")) {
          return Promise.resolve({
            ok: true,
            status: 200,
            json: () => Promise.resolve(sampleUsers),
          } as Response);
        }
        if (url.includes("/api/duties")) {
          return Promise.resolve({
            ok: true,
            status: 200,
            json: () => Promise.resolve(sampleDuties),
          } as Response);
        }
        if (url.includes("/api/admin/duties/") && init?.method === "PATCH") {
          return Promise.resolve({
            ok: true,
            status: 200,
            json: () =>
              Promise.resolve({
                id: 10,
                user_id: 2,
                start_at: "2030-01-15T09:00:00Z",
                end_at: "2030-01-15T18:00:00Z",
              }),
          } as Response);
        }
        return Promise.reject(new Error(`Unexpected URL: ${url}`));
      })
    );
    render(<AdminPage />);
    await waitFor(() => {
      expect(screen.getByText("Alice")).toBeInTheDocument();
    });
    fireEvent.click(screen.getByRole("button", { name: /Alice/ }));
    await waitFor(() => {
      expect(screen.getByRole("radiogroup", { name: /select user|выберите пользователя/i })).toBeInTheDocument();
    });
    fireEvent.click(screen.getByRole("radio", { name: /Bob/ }));
    fireEvent.click(screen.getByRole("button", { name: /save|сохранить/i }));
    await waitFor(() => {
      expect(
        screen.getByText(/Duty reassigned|Дежурство переназначено/i)
      ).toBeInTheDocument();
    });
  });
});
