import { beforeEach, describe, expect, it, vi } from "vitest";
import { render, screen, waitFor } from "@testing-library/react";
import userEvent from "@testing-library/user-event";
import LoginForm from "@/components/auth/LoginForm";
import { FCM_DEVICE_ID_UNAVAILABLE_MSG } from "@/lib/firebase/messaging";

const TEST_FCM_TOKEN = "TEST_FCM_TOKEN_123";

const replace = vi.fn();
const push = vi.fn();
const setAuth = vi.fn();
const setPendingLogin = vi.fn();
const { resolveAuthDeviceId, markFcmTokenSynced } = vi.hoisted(() => ({
  resolveAuthDeviceId: vi.fn(),
  markFcmTokenSynced: vi.fn(),
}));

vi.mock("@/lib/firebase/authDeviceId", () => ({
  resolveAuthDeviceId,
}));

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

vi.mock("@/lib/auth/store", () => ({
  useAuthStore: (
    selector: (state: {
      setAuth: typeof setAuth;
      setPendingLogin: typeof setPendingLogin;
    }) => unknown,
  ) =>
    selector({
      setAuth,
      setPendingLogin,
    }),
}));

vi.mock("@/lib/firebase/messaging", async (importOriginal) => {
  const actual =
    await importOriginal<typeof import("@/lib/firebase/messaging")>();
  return {
    ...actual,
    markFcmTokenSynced,
  };
});

vi.mock("@/lib/api", async (importOriginal) => {
  const actual = await importOriginal<typeof import("@/lib/api")>();
  return {
    ...actual,
    apiFetch: vi.fn(),
  };
});

const { refreshUnreadNotificationCount } = vi.hoisted(() => ({
  refreshUnreadNotificationCount: vi.fn(),
}));

vi.mock("@/lib/notifications/count", () => ({
  refreshUnreadNotificationCount,
}));

import { apiFetch } from "@/lib/api";

describe("LoginForm device_id", () => {
  beforeEach(() => {
    vi.clearAllMocks();
    resolveAuthDeviceId.mockResolvedValue({
      token: TEST_FCM_TOKEN,
      reason: null,
    });
  });

  it("sends the exact FCM token as device_id on login", async () => {
    const user = userEvent.setup();
    vi.mocked(apiFetch).mockImplementation(async (_url, options) => {
      const body = JSON.parse(String(options?.body));
      expect(body.device_id).toBe(TEST_FCM_TOKEN);
      expect(body.device_id).not.toMatch(
        /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i,
      );
      options?.successCase?.({
        key: "success",
        data: { token: "auth-token" },
      });
      return { key: "success", data: { token: "auth-token" } };
    });

    render(<LoginForm />);

    await user.type(screen.getByPlaceholderText("ahmed@beepbeep.sa"), "a@b.com");
    await user.type(screen.getByPlaceholderText("••••••••"), "Abcd1234!");
    await user.click(screen.getByRole("button", { name: "تسجيل الدخول" }));

    await waitFor(() => {
      expect(apiFetch).toHaveBeenCalledWith(
        "/provider/login",
        expect.objectContaining({ method: "POST" }),
      );
    });
    expect(resolveAuthDeviceId).toHaveBeenCalledWith({ requestPermission: true });
    expect(markFcmTokenSynced).toHaveBeenCalledWith(TEST_FCM_TOKEN);
    expect(refreshUnreadNotificationCount).toHaveBeenCalledTimes(1);
  });

  it("does not call login when FCM token retrieval fails", async () => {
    const user = userEvent.setup();
    resolveAuthDeviceId.mockResolvedValueOnce({
      token: null,
      reason: "permission_dismissed",
    });

    render(<LoginForm />);

    await user.type(screen.getByPlaceholderText("ahmed@beepbeep.sa"), "a@b.com");
    await user.type(screen.getByPlaceholderText("••••••••"), "Abcd1234!");
    await user.click(screen.getByRole("button", { name: "تسجيل الدخول" }));

    await waitFor(() => {
      expect(screen.getByText(FCM_DEVICE_ID_UNAVAILABLE_MSG)).toBeInTheDocument();
    });
    expect(apiFetch).not.toHaveBeenCalled();
  });

  it("shows the loading label while the login request is pending", async () => {
    const user = userEvent.setup();
    vi.mocked(apiFetch).mockImplementation(() => new Promise(() => {}));

    render(<LoginForm />);
    await user.type(screen.getByPlaceholderText("ahmed@beepbeep.sa"), "a@b.com");
    await user.type(screen.getByPlaceholderText("••••••••"), "Abcd1234!");
    await user.click(screen.getByRole("button", { name: "تسجيل الدخول" }));

    expect(await screen.findByText("جاري الدخول...")).toBeInTheDocument();
  });

  it.each([
    ["fail", "تعذّر تسجيل الدخول، حاول مرة أخرى"],
    ["needApprove", "حسابك في انتظار الموافقة"],
    ["needActive", "حسابك غير مفعّل"],
  ] as const)(
    "shows the default message for a %s login response",
    async (key, message) => {
      const user = userEvent.setup();
      vi.mocked(apiFetch).mockImplementation(async (_url, options) => {
        const envelope = { key, msg: "" };
        if (key === "needApprove") options?.needApproveCase?.(envelope);
        else if (key === "needActive") options?.needActiveCase?.(envelope);
        else options?.errorCase?.(envelope);
        return envelope;
      });

      render(<LoginForm />);
      await user.type(screen.getByPlaceholderText("ahmed@beepbeep.sa"), "a@b.com");
      await user.type(screen.getByPlaceholderText("••••••••"), "Abcd1234!");
      await user.click(screen.getByRole("button", { name: "تسجيل الدخول" }));

      expect(await screen.findByText(message)).toBeInTheDocument();
    },
  );

  it("shows the API error message and sends verification-required users onward", async () => {
    const user = userEvent.setup();
    vi.mocked(apiFetch).mockImplementation(async (_url, options) => {
      const envelope = {
        key: "fail",
        msg: "بيانات الدخول غير صحيحة",
      } as const;
      options?.errorCase?.(envelope);
      return envelope;
    });

    render(<LoginForm />);
    await user.type(screen.getByPlaceholderText("ahmed@beepbeep.sa"), "a@b.com");
    await user.type(screen.getByPlaceholderText("••••••••"), "Abcd1234!");
    await user.click(screen.getByRole("button", { name: "تسجيل الدخول" }));
    expect(await screen.findByText("بيانات الدخول غير صحيحة")).toBeInTheDocument();

    vi.mocked(apiFetch).mockImplementation(async (_url, options) => {
      const envelope = { key: "needVerify", msg: "" } as const;
      options?.needVerifyCase?.(envelope);
      return envelope;
    });
    await user.click(screen.getByRole("button", { name: "تسجيل الدخول" }));
    await waitFor(() => {
      expect(setPendingLogin).toHaveBeenCalledWith({
        email: "a@b.com",
        password: "Abcd1234!",
        device_id: TEST_FCM_TOKEN,
      });
      expect(push).toHaveBeenCalledWith("/verify-code");
    });
  });

  it("toggles the password visibility and remember-me selection", async () => {
    const user = userEvent.setup();
    render(<LoginForm />);

    const password = screen.getByPlaceholderText("••••••••");
    const togglePassword = password.parentElement?.querySelector("button")!;
    const rememberMe = document.querySelector("#remember-me")!;

    expect(password).toHaveAttribute("type", "password");
    expect(rememberMe.querySelector(".ri-check-line")).toBeInTheDocument();
    await user.click(togglePassword);
    await user.click(rememberMe);

    expect(password).toHaveAttribute("type", "text");
    expect(rememberMe.querySelector(".ri-check-line")).not.toBeInTheDocument();
  });

  it("shows validation errors and handles a successful response without data", async () => {
    const user = userEvent.setup();
    render(<LoginForm />);
    await user.click(screen.getByRole("button", { name: "تسجيل الدخول" }));
    expect(await screen.findByText("البريد الإلكتروني مطلوب")).toBeInTheDocument();
    expect(screen.getByText("كلمة المرور مطلوبة")).toBeInTheDocument();

    vi.mocked(apiFetch).mockImplementation(async (_url, options) => {
      const envelope = { key: "success" } as const;
      options?.successCase?.(envelope);
      return envelope;
    });
    await user.type(screen.getByPlaceholderText("ahmed@beepbeep.sa"), "a@b.com");
    await user.type(screen.getByPlaceholderText("••••••••"), "Abcd1234!");
    await user.click(screen.getByRole("button", { name: "تسجيل الدخول" }));
    await waitFor(() => expect(apiFetch).toHaveBeenCalled());
    expect(setAuth).not.toHaveBeenCalled();
    expect(replace).not.toHaveBeenCalled();
  });
});
