import { beforeEach, describe, expect, it, vi } from 'vitest';
import type { AuthUser } from '@alqove/types';
import type { Membership } from '@alqove/api-client';
import { useAuthStore } from '../auth';

const { myMemberships, me, login, register, logout } = vi.hoisted(() => ({
  myMemberships: vi.fn(), me: vi.fn(), login: vi.fn(), register: vi.fn(), logout: vi.fn(),
}));
vi.mock('@/lib/api', () => ({ api: { team: { myMemberships }, auth: { me, login, register, logout } } }));
const user = { id: 'user-a', name: 'Ada', store_id: 'legacy' } as AuthUser;
const member = (storeId: string): Membership => ({
  id: `m-${storeId}`, store_id: storeId, role: 'owner', capabilities: ['store.admin'], is_exempt: false,
});
function deferred<T>() {
  let resolve!: (value: T) => void;
  const promise = new Promise<T>((r) => { resolve = r; });
  return { promise, resolve };
}
beforeEach(() => {
  vi.resetAllMocks();
  useAuthStore.getState().clearAuth();
  localStorage.clear();
});
describe('authoritative store selection', () => {
  it('forwards invitation registration phone and discovers store selection', async () => {
    register.mockResolvedValue({ data: { user, token: 'token-a' } });
    myMemberships.mockResolvedValue({ data: [member('store-a')] });
    await useAuthStore.getState().register('Ada', 'a@b.c', 'password', '+15555550123');
    expect(register).toHaveBeenCalledWith({ name: 'Ada', email: 'a@b.c', password: 'password', password_confirmation: 'password', phone: '+15555550123' });
    expect(useAuthStore.getState().selectedStoreId).toBe('store-a');
  });
  it('hides previously loaded access while revalidating storage, including a removed token', async () => {
    useAuthStore.getState().setAuth(user, 'token-a');
    myMemberships.mockResolvedValue({ data: [member('store-a')] });
    await useAuthStore.getState().refreshMemberships();
    me.mockReturnValue(new Promise(() => {}));
    void useAuthStore.getState().loadFromStorage();
    expect(useAuthStore.getState().memberships).toBeNull();
    expect(useAuthStore.getState().selectedStoreId).toBeNull();
    localStorage.removeItem('auth_token');
    await useAuthStore.getState().loadFromStorage();
    expect(useAuthStore.getState().user).toBeNull();
  });
  it('does not let an earlier refresh resurrect revoked memberships', async () => {
    useAuthStore.getState().setAuth(user, 'token-a');
    const stale = deferred<{ data: Membership[] }>();
    myMemberships.mockReturnValueOnce(stale.promise).mockResolvedValueOnce({ data: [] });
    const first = useAuthStore.getState().refreshMemberships();
    await useAuthStore.getState().refreshMemberships();
    stale.resolve({ data: [member('store-a')] });
    await first;
    expect(useAuthStore.getState().memberships).toEqual([]);
    expect(useAuthStore.getState().selectedStoreId).toBeNull();
  });
  it.each(['load', 'login', 'register'] as const)('cannot restore a logged-out session from a late %s response', async (source) => {
    const pending = deferred<{ data: AuthUser & { user: AuthUser; token: string } }>();
    myMemberships.mockResolvedValue({ data: [member('store-a')] });
    localStorage.setItem('auth_token', 'token-a');
    me.mockReturnValue(pending.promise);
    login.mockReturnValue(pending.promise);
    register.mockReturnValue(pending.promise);
    const work = source === 'load' ? useAuthStore.getState().loadFromStorage()
      : source === 'login' ? useAuthStore.getState().login('a@b.c', 'password')
      : useAuthStore.getState().register('Ada', 'a@b.c', 'password');
    useAuthStore.getState().clearAuth();
    pending.resolve({ data: { ...user, user, token: 'token-a' } });
    await work;
    expect(useAuthStore.getState().user).toBeNull();
    expect(useAuthStore.getState().memberships).toBeNull();
    expect(localStorage.getItem('auth_token')).toBeNull();
  });
  it('logout clears access and preference before the network response, without clearing a later login', async () => {
    useAuthStore.getState().setAuth(user, 'token-a');
    myMemberships.mockResolvedValue({ data: [member('store-a'), member('store-b')] });
    await useAuthStore.getState().refreshMemberships();
    useAuthStore.getState().selectStore('store-b');
    const pending = deferred<void>();
    logout.mockReturnValue(pending.promise);
    const leaving = useAuthStore.getState().logout();
    expect(useAuthStore.getState().selectedStoreId).toBeNull();
    expect(useAuthStore.getState().user).toBeNull();
    useAuthStore.getState().setAuth(user, 'token-new');
    await useAuthStore.getState().refreshMemberships();
    expect(useAuthStore.getState().selectedStoreId).toBe('store-a');
    pending.resolve();
    await leaving;
    expect(useAuthStore.getState().token).toBe('token-new');
  });
  it('drops access immediately on auth change and ignores an old membership response', async () => {
    useAuthStore.getState().setAuth(user, 'token-a');
    myMemberships.mockResolvedValueOnce({ data: [member('store-a')] });
    await useAuthStore.getState().refreshMemberships();
    const pending = deferred<{ data: Membership[] }>();
    myMemberships.mockReturnValue(pending.promise);
    const refresh = useAuthStore.getState().refreshMemberships();
    useAuthStore.getState().setAuth({ ...user, id: 'user-b' }, 'token-b');
    expect(useAuthStore.getState().memberships).toBeNull();
    expect(useAuthStore.getState().selectedStoreId).toBeNull();
    pending.resolve({ data: [member('store-a')] });
    await refresh;
    expect(useAuthStore.getState().memberships).toBeNull();
  });
  it('persists a user-scoped preference and reconciles it on every authoritative load', async () => {
    useAuthStore.getState().setAuth(user, 'token-a');
    myMemberships.mockResolvedValue({ data: [member('store-a'), member('store-b')] });
    await useAuthStore.getState().refreshMemberships();
    useAuthStore.getState().selectStore('store-b');
    useAuthStore.setState({ user: null, memberships: null, selectedStoreId: null });
    me.mockResolvedValue({ data: user });
    await useAuthStore.getState().loadFromStorage();
    expect(useAuthStore.getState().selectedStoreId).toBe('store-b');
    myMemberships.mockResolvedValue({ data: [member('store-a')] });
    await useAuthStore.getState().refreshMemberships();
    expect(useAuthStore.getState().selectedStoreId).toBe('store-a');
    myMemberships.mockResolvedValue({ data: [] });
    await useAuthStore.getState().refreshMemberships();
    expect(useAuthStore.getState().selectedStoreId).toBeNull();
  });
  it('selects a discovered membership rather than the legacy owned store', async () => {
    useAuthStore.getState().setAuth(user, 'token-a');
    myMemberships.mockResolvedValue({ data: [member('store-a'), member('store-b')] });
    await useAuthStore.getState().refreshMemberships();
    expect(useAuthStore.getState().selectedStoreId).toBe('store-a');
    useAuthStore.getState().selectStore('store-b');
    expect(useAuthStore.getState().selectedStoreId).toBe('store-b');
    useAuthStore.getState().selectStore('legacy');
    expect(useAuthStore.getState().selectedStoreId).toBe('store-b');
  });
});
