// Grace-period middleware tests. We mock the supabase client at the
// module boundary so the middleware's calls land on a controllable
// chainable fake. No real DB / network.

const mockSupabaseUpdates = [];
const supabaseFake = {
  from(table) {
    const ctx = { table, filters: [], action: null };
    const proxy = {
      update(patch) {
        ctx.patch = patch;
        ctx.action = 'update';
        mockSupabaseUpdates.push(ctx);
        return proxy;
      },
      eq(col, val) {
        ctx.filters.push([col, val]);
        // The middleware awaits the result of .eq() after .update() —
        // return a resolved promise (no error).
        return Promise.resolve({ error: null });
      },
    };
    return proxy;
  },
};

jest.mock('../../src/utils/supabase', () => ({
  getSupabaseServiceClient: () => supabaseFake,
}));

const { checkGracePeriod } = require('../../src/middleware/gracePeriod');

beforeEach(() => {
  mockSupabaseUpdates.length = 0;
});

function runMiddleware(req) {
  return new Promise((resolve, reject) => {
    const res = {
      status: (code) => ({ json: (b) => reject(new Error(`unexpected ${code}: ${JSON.stringify(b)}`)) }),
    };
    checkGracePeriod(req, res, resolve);
  });
}

describe('checkGracePeriod', () => {
  test('passes through when no user (unauth route slip)', async () => {
    await runMiddleware({});
    expect(mockSupabaseUpdates).toHaveLength(0);
  });

  test('passes through when user has no grace_period_until', async () => {
    await runMiddleware({ user: { id: 'u1', tier: 'analyst', grace_period_until: null } });
    expect(mockSupabaseUpdates).toHaveLength(0);
  });

  test('passes through when grace is still in the future', async () => {
    const future = new Date(Date.now() + 12 * 3600 * 1000).toISOString();
    const req = { user: { id: 'u1', tier: 'analyst', grace_period_until: future } };
    await runMiddleware(req);
    expect(mockSupabaseUpdates).toHaveLength(0);
    expect(req.user.tier).toBe('analyst'); // unchanged
  });

  test('expired grace → downgrades both tables + rewrites req.user', async () => {
    const past = new Date(Date.now() - 60_000).toISOString();
    const req = { user: { id: 'u1', tier: 'desk', grace_period_until: past } };
    await runMiddleware(req);

    expect(mockSupabaseUpdates).toHaveLength(2);

    const usersUpdate = mockSupabaseUpdates.find((u) => u.table === 'users');
    expect(usersUpdate.patch).toEqual({ tier: 'free', grace_period_until: null });
    expect(usersUpdate.filters).toEqual([['id', 'u1']]);

    const profileUpdate = mockSupabaseUpdates.find((u) => u.table === 'user_profiles');
    expect(profileUpdate.patch.tier).toBe('free');
    expect(profileUpdate.patch.subscription_status).toBe('expired');
    expect(profileUpdate.patch.grace_period_until).toBeNull();

    // req.user reflects the downgrade so the downstream route sees it.
    expect(req.user.tier).toBe('free');
    expect(req.user.grace_period_until).toBeNull();
  });

  test('expired but already free → still scrubs the grace timestamp', async () => {
    // Edge case — Stripe set grace, then user got downgraded by some
    // other path. We should still null the timestamp so the row is clean.
    const past = new Date(Date.now() - 60_000).toISOString();
    const req = { user: { id: 'u2', tier: 'free', grace_period_until: past } };
    await runMiddleware(req);
    const usersUpdate = mockSupabaseUpdates.find((u) => u.table === 'users');
    expect(usersUpdate.patch.grace_period_until).toBeNull();
    expect(req.user.grace_period_until).toBeNull();
  });

  test('malformed grace timestamp → treated as no grace (no downgrade)', async () => {
    const req = { user: { id: 'u3', tier: 'analyst', grace_period_until: 'not-a-date' } };
    await runMiddleware(req);
    expect(mockSupabaseUpdates).toHaveLength(0);
    expect(req.user.tier).toBe('analyst');
  });

  test('exactly-at-boundary timestamp is treated as expired', async () => {
    // The middleware does `> Date.now()`; an exact match counts as
    // expired. We test that policy stays explicit.
    const req = { user: { id: 'u4', tier: 'analyst', grace_period_until: new Date(Date.now() - 1).toISOString() } };
    await runMiddleware(req);
    expect(mockSupabaseUpdates.length).toBeGreaterThan(0);
    expect(req.user.tier).toBe('free');
  });
});
