import { describe, it, expect, vi, beforeEach } from "vitest";
import { renderHook, waitFor, act } from "@testing-library/react";

const mockGetCurrentPosition = vi.fn();
const mockFetch = vi.fn();

vi.stubGlobal("navigator", {
  geolocation: {
    getCurrentPosition: mockGetCurrentPosition,
  },
});
vi.stubGlobal("fetch", mockFetch);

import { useGeolocation } from "./useGeolocation";

beforeEach(() => {
  vi.clearAllMocks();
  mockFetch.mockResolvedValue({
    json: () => Promise.resolve({ name: "黄浦区", formatted: "上海市黄浦区" }),
  });
});

describe("useGeolocation", () => {
  it("returns success with coords on successful geolocation", async () => {
    mockGetCurrentPosition.mockImplementation((success) => {
      success({ coords: { latitude: 31.23, longitude: 121.47 } });
    });

    const { result } = renderHook(() => useGeolocation());

    await waitFor(() => {
      expect(result.current.status).toBe("success");
    });

    expect(result.current.coords).toEqual({ lat: 31.23, lng: 121.47 });
  });

  it("reverse geocodes after getting coords", async () => {
    mockGetCurrentPosition.mockImplementation((success) => {
      success({ coords: { latitude: 31.23, longitude: 121.47 } });
    });

    const { result } = renderHook(() => useGeolocation());

    await waitFor(() => {
      expect(result.current.locationName).toBe("黄浦区");
    });

    expect(mockFetch).toHaveBeenCalledWith(
      expect.stringContaining("/api/location/regeo"),
    );
  });

  it("sets denied status on permission denied", async () => {
    mockGetCurrentPosition.mockImplementation((_success, error) => {
      error({ code: 1, PERMISSION_DENIED: 1 });
    });

    const { result } = renderHook(() => useGeolocation());

    await waitFor(() => {
      expect(result.current.status).toBe("denied");
    });

    expect(result.current.coords).toBeNull();
  });

  it("sets failed status on timeout", async () => {
    mockGetCurrentPosition.mockImplementation((_success, error) => {
      error({ code: 3, PERMISSION_DENIED: 1, TIMEOUT: 3 });
    });

    const { result } = renderHook(() => useGeolocation());

    await waitFor(() => {
      expect(result.current.status).toBe("failed");
    });
  });

  it("provides retry function", async () => {
    mockGetCurrentPosition
      .mockImplementationOnce((_success, error) => {
        error({ code: 3, PERMISSION_DENIED: 1, TIMEOUT: 3 });
      })
      .mockImplementationOnce((success) => {
        success({ coords: { latitude: 31.23, longitude: 121.47 } });
      });

    const { result } = renderHook(() => useGeolocation());

    await waitFor(() => {
      expect(result.current.status).toBe("failed");
    });

    await act(async () => {
      await result.current.retry();
    });

    expect(result.current.status).toBe("success");
    expect(result.current.coords).toEqual({ lat: 31.23, lng: 121.47 });
  });
});
