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

const mocked = vi.hoisted(() => ({
  poolQuery: vi.fn(),
  recordLedgerEntry: vi.fn(),
}));

vi.mock('../../db', () => ({ default: { query: mocked.poolQuery } }));
vi.mock('../ledger', () => ({ recordLedgerEntry: mocked.recordLedgerEntry }));

describe('monthly pass expiry enforcement', () => {
  beforeEach(() => {
    vi.clearAllMocks();
  });

  it('revokes expired monthly pass access when loading a user by id', async () => {
    mocked.poolQuery
      .mockResolvedValueOnce({
        rows: [{
          id: 'user-1',
          email: 'expired@example.com',
          password_hash: 'hash',
          is_weatherman: true,
          monthly_pass_start: '2026-02-01T00:00:00.000Z',
          monthly_pass_expires: '2026-03-10T06:00:00.000Z',
        }],
      })
      .mockResolvedValueOnce({
        rows: [{ id: 'user-1' }],
      });

    const { findUserById } = await import('../user');
    const user = await findUserById('user-1');

    expect(user).toMatchObject({
      id: 'user-1',
      is_weatherman: false,
      monthly_pass_start: null,
      monthly_pass_expires: null,
    });
    expect(String(mocked.poolQuery.mock.calls[1]?.[0])).toContain('monthly_pass_expires <= NOW()');
  });

  it('revokes expired monthly pass access when loading a user by email', async () => {
    mocked.poolQuery
      .mockResolvedValueOnce({
        rows: [{
          id: 'user-2',
          email: 'expired@example.com',
          password_hash: 'hash',
          is_weatherman: true,
          monthly_pass_start: '2026-02-01T00:00:00.000Z',
          monthly_pass_expires: '2026-03-10T06:00:00.000Z',
        }],
      })
      .mockResolvedValueOnce({
        rows: [{ id: 'user-2' }],
      });

    const { findUserByEmail } = await import('../user');
    const user = await findUserByEmail('expired@example.com');

    expect(user).toMatchObject({
      id: 'user-2',
      is_weatherman: false,
      monthly_pass_expires: null,
    });
    expect(String(mocked.poolQuery.mock.calls[0]?.[0])).toContain('lower(email) = lower($1)');
    expect(String(mocked.poolQuery.mock.calls[1]?.[0])).toContain('SET is_weatherman = FALSE');
  });

  it('clears expired monthly pass state before calculating pick balance', async () => {
    const todayET = new Date().toLocaleDateString('en-CA', { timeZone: 'America/New_York' });

    mocked.poolQuery
      .mockResolvedValueOnce({
        rows: [{ id: 'user-3' }],
      })
      .mockResolvedValueOnce({
        rows: [{
          email_verified: true,
          last_reset_date_pacific: todayET,
          daily_free_forecasts: 0,
          grace_expires_at: null,
        }],
      })
      .mockResolvedValueOnce({
        rows: [{ 1: 1 }],
      })
      .mockResolvedValueOnce({
        rows: [{
          single_picks: 0,
          daily_pass_picks: 0,
          daily_pass_date: null,
          daily_free_forecasts: 0,
          email_verified: true,
          tourist_pass_expires_at: null,
          tourist_pass_timezone: null,
        }],
      });

    const { getPickBalance } = await import('../user');
    const balance = await getPickBalance('user-3');

    expect(balance).toMatchObject({
      single_picks: 0,
      daily_pass_valid: false,
      daily_free_forecasts: 0,
    });
    expect(String(mocked.poolQuery.mock.calls[0]?.[0])).toContain('monthly_pass_expires <= NOW()');
  });

  it('extends an active monthly pass instead of truncating remaining paid time', async () => {
    mocked.poolQuery.mockResolvedValueOnce({ rows: [] });

    const { creditMonthlyPass } = await import('../user');
    await creditMonthlyPass('user-4');

    expect(String(mocked.poolQuery.mock.calls[0]?.[0])).toContain('GREATEST(COALESCE(monthly_pass_expires, NOW()), NOW()) + INTERVAL \'30 days\'');
    expect(String(mocked.poolQuery.mock.calls[0]?.[0])).toContain('monthly_pass_renewal_reminder_sent_at = NULL');
    expect(mocked.poolQuery.mock.calls[0]?.[1]).toEqual(['user-4']);
  });
});
