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

const mocked = vi.hoisted(() => ({
  authMiddleware: vi.fn((req: any, _res: any, next: any) => { req.user = { userId: 'user-1' }; next(); }),
  optionalAuth: vi.fn((_req: any, _res: any, next: any) => next()),
  poolQuery: vi.fn(),
  getPickBalance: vi.fn(),
  recordLedgerEntry: vi.fn(),
}));

const VALID_SURVEY_ID = '550e8400-e29b-41d4-a716-446655440000';

vi.mock('../../middleware/auth', () => ({
  authMiddleware: mocked.authMiddleware,
  optionalAuth: mocked.optionalAuth,
}));
vi.mock('../../db', () => ({ default: { query: mocked.poolQuery } }));
vi.mock('../../models/user', () => ({
  SURVEY_BONUS_FORECASTS: 30,
  getPickBalance: mocked.getPickBalance,
}));
vi.mock('../../models/ledger', () => ({ recordLedgerEntry: mocked.recordLedgerEntry }));

async function loadRouter() {
  vi.resetModules();
  return (await import('../survey')).default;
}

describe('/survey/:id/submit contract', () => {
  beforeEach(() => {
    vi.clearAllMocks();
    mocked.recordLedgerEntry.mockResolvedValue(undefined);
    mocked.getPickBalance.mockResolvedValue({
      daily_free_forecasts: 8,
      signup_bonus_forecasts: 30,
      survey_bonus_forecasts: 30,
      daily_pass_valid: false,
      daily_pass_picks: 0,
      single_picks: 0,
    });
  });

  it('returns authoritative forecast balance fields after awarding survey credits', async () => {
    const router = await loadRouter();

    mocked.poolQuery
      .mockResolvedValueOnce({
        rows: [{ id: VALID_SURVEY_ID, credit_reward: 3, is_active: true }],
      })
      .mockResolvedValueOnce({
        rows: [{ email_verified: true, grace_expires_at: null }],
      })
      .mockResolvedValueOnce({
        rows: [],
      })
      .mockResolvedValueOnce({
        rows: [{ id: 'resp-1' }],
      })
      .mockResolvedValueOnce({
        rows: [],
      });

    const app = express();
    app.use(express.json());
    app.use('/', router);

    const res = await request(app)
      .post(`/${VALID_SURVEY_ID}/submit`)
      .send({ answers: { experience: 'daily' } });

    expect(res.status).toBe(200);
    expect(res.body).toMatchObject({
      ok: true,
      creditsAwarded: 30,
      newBalance: 68,
      forecastBalance: 68,
      forecast_balance: 68,
    });
    expect(mocked.recordLedgerEntry).toHaveBeenCalledWith(
      'user-1',
      30,
      'SURVEY_REWARD',
      68,
      expect.objectContaining({
        surveyId: VALID_SURVEY_ID,
        creditReward: 30,
      }),
    );
  });

  it('rejects invalid survey ids before hitting the database', async () => {
    const router = await loadRouter();

    const app = express();
    app.use(express.json());
    app.use('/', router);

    const res = await request(app)
      .post('/999/submit')
      .send({ answers: { experience: 'daily' } });

    expect(res.status).toBe(400);
    expect(res.body).toEqual({ error: 'Invalid survey id' });
    expect(mocked.poolQuery).not.toHaveBeenCalled();
  });

  it('returns the most recent active survey instead of an inactive newer one', async () => {
    const router = await loadRouter();

    mocked.poolQuery
      .mockResolvedValueOnce({
        rows: [{
          id: 'survey-active',
          title: 'Still Live',
          questions: JSON.stringify([{ id: 'q1', type: 'radio', question: 'Question?' }]),
          credit_reward: 3,
          is_active: true,
        }],
      })
      .mockResolvedValueOnce({ rows: [] })
      .mockResolvedValueOnce({ rows: [] })
      .mockResolvedValueOnce({ rows: [] })
      .mockResolvedValueOnce({
        rows: [{ is_weatherman: false, preferences: {} }],
      });

    const app = express();
    app.use(express.json());
    app.use('/', router);

    const res = await request(app)
      .get('/current');

    expect(res.status).toBe(200);
    expect(res.body).toMatchObject({
      survey: {
        id: 'survey-active',
        title: 'Still Live',
        questions: [{ id: 'q1', type: 'radio', question: 'Question?' }],
        creditReward: 30,
      },
      hasCompletedAnySurvey: false,
    });
    expect(mocked.poolQuery).toHaveBeenNthCalledWith(
      1,
      expect.stringContaining('WHERE is_active = TRUE'),
    );
  });

});
