import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
import { render, renderHook, screen, waitFor } from "@testing-library/react";
import type React from "react";
import { describe, expect, it, vi } from "vitest";
import {
  fetchMe,
  fetchTenant,
  type TenantSummary,
  type UserProfileResponse,
} from "../../../lib/adminApi";
import { TenantPermissionGuard } from "../components/TenantPermissionGuard";
import { useTenantPermission } from "./useTenantPermission";

vi.mock("../../../lib/adminApi", () => ({
  fetchMe: vi.fn(),
  fetchTenant: vi.fn(),
}));

function createWrapper() {
  const queryClient = new QueryClient({
    defaultOptions: {
      queries: { retry: false },
    },
  });
  return ({ children }: { children: React.ReactNode }) => (
    <QueryClientProvider client={queryClient}>{children}</QueryClientProvider>
  );
}

function mockProfile(
  overrides: Partial<UserProfileResponse>,
): UserProfileResponse {
  return {
    id: "user-id",
    email: "user@example.com",
    name: "Test User",
    phone: "",
    role: "user",
    department: "",
    affiliationType: "general",
    ...overrides,
  };
}

function mockTenant(overrides: Partial<TenantSummary>): TenantSummary {
  return {
    id: "tenant-id",
    type: "COMPANY",
    name: "Test Tenant",
    slug: "test-tenant",
    description: "",
    status: "active",
    memberCount: 0,
    createdAt: "2026-01-01T00:00:00Z",
    updatedAt: "2026-01-01T00:00:00Z",
    ...overrides,
  };
}

describe("useTenantPermission", () => {
  it("returns true for all permissions if user is super_admin", async () => {
    vi.mocked(fetchMe).mockResolvedValue(
      mockProfile({
        id: "user-super",
        role: "super_admin",
      }),
    );

    vi.mocked(fetchTenant).mockResolvedValue(
      mockTenant({
        id: "tenant-1",
        name: "Super Tenant",
        userPermissions: { view: false, manage: false, manage_admins: false },
      }),
    );

    const { result } = renderHook(() => useTenantPermission("tenant-1"), {
      wrapper: createWrapper(),
    });

    await waitFor(() => {
      expect(result.current.isLoading).toBe(false);
    });

    expect(result.current.hasPermission("view")).toBe(true);
    expect(result.current.hasPermission("manage")).toBe(true);
    expect(result.current.hasPermission("manage_admins")).toBe(true);
  });

  it("returns permissions mapped from userPermissions for normal admins/users", async () => {
    vi.mocked(fetchMe).mockResolvedValue(
      mockProfile({
        id: "user-admin",
        role: "tenant_admin",
      }),
    );

    vi.mocked(fetchTenant).mockResolvedValue(
      mockTenant({
        id: "tenant-2",
        name: "Tenant Admin Corp",
        userPermissions: { view: true, manage: true, manage_admins: false },
      }),
    );

    const { result } = renderHook(() => useTenantPermission("tenant-2"), {
      wrapper: createWrapper(),
    });

    await waitFor(() => {
      expect(result.current.isLoading).toBe(false);
    });

    expect(result.current.hasPermission("view")).toBe(true);
    expect(result.current.hasPermission("manage")).toBe(true);
    expect(result.current.hasPermission("manage_admins")).toBe(false);
  });
});

describe("TenantPermissionGuard", () => {
  it("renders children when user has permission", async () => {
    vi.mocked(fetchMe).mockResolvedValue(
      mockProfile({
        id: "user-admin",
        role: "tenant_admin",
      }),
    );

    vi.mocked(fetchTenant).mockResolvedValue(
      mockTenant({
        id: "tenant-3",
        userPermissions: { view: true, manage: true, manage_admins: false },
      }),
    );

    render(
      <TenantPermissionGuard
        tenantId="tenant-3"
        relation="manage"
        fallback={<div>Access Denied</div>}
      >
        <div>Access Granted</div>
      </TenantPermissionGuard>,
      { wrapper: createWrapper() },
    );

    await waitFor(() => {
      expect(screen.getByText("Access Granted")).toBeInTheDocument();
    });
    expect(screen.queryByText("Access Denied")).not.toBeInTheDocument();
  });

  it("renders fallback when user lacks permission", async () => {
    vi.mocked(fetchMe).mockResolvedValue(
      mockProfile({
        id: "user-admin",
        role: "tenant_admin",
      }),
    );

    vi.mocked(fetchTenant).mockResolvedValue(
      mockTenant({
        id: "tenant-4",
        userPermissions: { view: true, manage: false, manage_admins: false },
      }),
    );

    render(
      <TenantPermissionGuard
        tenantId="tenant-4"
        relation="manage"
        fallback={<div>Access Denied</div>}
      >
        <div>Access Granted</div>
      </TenantPermissionGuard>,
      { wrapper: createWrapper() },
    );

    await waitFor(() => {
      expect(screen.getByText("Access Denied")).toBeInTheDocument();
    });
    expect(screen.queryByText("Access Granted")).not.toBeInTheDocument();
  });
});
