import { beforeEach, describe, expect, it, vi } from 'vitest';

import { getSupabaseClient } from '@/lib/supabase';

vi.mock('@/lib/supabase', () => ({
  getSupabaseClient: vi.fn(),
}));

import {
  getCurrentAccessToken,
  getCurrentDataAccessScope,
  getVisibleBatchIds,
} from '@/services/dataAccessScope';

function createBatchQuery(response: { data: unknown[] | null; error: { message: string } | null }) {
  const query = {
    eq: vi.fn(() => query),
    then: (onFulfilled: (value: typeof response) => unknown, onRejected?: (reason: unknown) => unknown) =>
      Promise.resolve(response).then(onFulfilled, onRejected),
    catch: (onRejected: (reason: unknown) => unknown) => Promise.resolve(response).catch(onRejected),
  };

  return query;
}

function createSupabaseMock(params: {
  sessionUserId?: string | null;
  accessToken?: string | null;
  role?: 'admin' | 'user';
  batches?: Array<{ batch_id: string }>;
}) {
  const batchQuery = createBatchQuery({ data: params.batches ?? [], error: null });

  return {
    batchQuery,
    auth: {
      getSession: vi.fn(async () => ({
        data: {
          session: params.sessionUserId
            ? {
              access_token: params.accessToken ?? 'token-001',
              user: { id: params.sessionUserId },
            }
            : null,
        },
        error: null,
      })),
    },
    from: vi.fn((table: string) => {
      if (table === 'profiles') {
        return {
          select: vi.fn(() => ({
            eq: vi.fn(() => ({
              maybeSingle: vi.fn(async () => ({
                data: params.sessionUserId ? { role: params.role ?? 'user' } : null,
                error: null,
              })),
            })),
          })),
        };
      }

      if (table === 'procurement_batches') {
        return {
          select: vi.fn(() => batchQuery),
        };
      }

      throw new Error(`Unexpected table: ${table}`);
    }),
  };
}

describe('dataAccessScope', () => {
  beforeEach(() => {
    vi.resetAllMocks();
  });

  it('returns current user scope with admin role', async () => {
    vi.mocked(getSupabaseClient).mockReturnValue(createSupabaseMock({
      sessionUserId: 'user-001',
      accessToken: 'access-token-001',
      role: 'admin',
    }) as unknown as ReturnType<typeof getSupabaseClient>);

    await expect(getCurrentDataAccessScope()).resolves.toEqual({
      userId: 'user-001',
      role: 'admin',
      isAdmin: true,
      accessToken: 'access-token-001',
    });
  });

  it('returns null scope when there is no session', async () => {
    vi.mocked(getSupabaseClient).mockReturnValue(createSupabaseMock({
      sessionUserId: null,
    }) as unknown as ReturnType<typeof getSupabaseClient>);

    await expect(getCurrentDataAccessScope()).resolves.toEqual({
      userId: null,
      role: null,
      isAdmin: false,
      accessToken: null,
    });
  });

  it('reads the current access token without loading profile role', async () => {
    vi.mocked(getSupabaseClient).mockReturnValue(createSupabaseMock({
      sessionUserId: 'user-002',
      accessToken: 'token-xyz',
    }) as unknown as ReturnType<typeof getSupabaseClient>);

    await expect(getCurrentAccessToken()).resolves.toBe('token-xyz');
  });

  it('filters visible batches by current owner when user is not admin', async () => {
    const supabase = createSupabaseMock({
      sessionUserId: 'user-003',
      role: 'user',
      batches: [{ batch_id: 'CG-20260420-001' }, { batch_id: 'CG-20260420-002' }],
    });
    vi.mocked(getSupabaseClient).mockReturnValue(supabase as unknown as ReturnType<typeof getSupabaseClient>);

    await expect(getVisibleBatchIds({ includeArchived: false })).resolves.toEqual([
      'CG-20260420-001',
      'CG-20260420-002',
    ]);

    expect(supabase.batchQuery.eq).toHaveBeenCalledWith('created_by', 'user-003');
    expect(supabase.batchQuery.eq).toHaveBeenCalledWith('archived', false);
  });
});