const request = require('supertest');
const express = require('express');
const statsRoutes = require('../../src/routes/stats');
const propsRoutes = require('../../src/routes/props');

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

// Mock auth middleware
jest.mock('../../src/middleware/auth', () => ({
  requireAuth: (req, res, next) => {
    req.user = req._mockUser || { id: '123', tier: 'analyst' };
    next();
  },
}));

let mockSupabase;

beforeEach(() => {
  mockSupabase = {
    from: jest.fn().mockReturnThis(),
    select: jest.fn().mockReturnThis(),
    order: jest.fn().mockReturnThis(),
    limit: jest.fn().mockReturnThis(),
    not: jest.fn().mockReturnThis(),
    eq: jest.fn().mockReturnThis(),
    single: jest.fn(),
  };
});

function buildStatsApp() {
  const app = express();
  app.use(express.json());
  app.use('/stats', statsRoutes);
  return app;
}

function buildPropsApp(mockUser) {
  const app = express();
  app.use(express.json());
  // Inject mock user before routes
  app.use((req, res, next) => {
    if (mockUser) req._mockUser = mockUser;
    next();
  });
  app.use('/props', propsRoutes);
  return app;
}

describe('GET /stats/parlays-graded', () => {
  it('returns count from scan_sessions', async () => {
    // Mock the select to return count via head:true pattern
    // supabase .from().select('*', { count: 'exact', head: true }) returns { count, error }
    mockSupabase.select.mockReturnValue({ count: 42, error: null });

    const app = buildStatsApp();
    const res = await request(app).get('/stats/parlays-graded');

    expect(res.status).toBe(200);
    expect(res.body).toEqual({ count: 42 });
    expect(res.headers['x-vyndr-mission']).toBeDefined();
  });

  it('returns 503 on supabase error', async () => {
    mockSupabase.select.mockReturnValue({ count: null, error: new Error('db down') });

    const app = buildStatsApp();
    const res = await request(app).get('/stats/parlays-graded');

    expect(res.status).toBe(503);
    expect(res.headers['x-vyndr-mission']).toBeDefined();
  });
});

describe('GET /stats/public', () => {
  it('returns all 4 fields', async () => {
    // Chain: first call is for count, second for grades, third for kill_conditions
    let callCount = 0;
    mockSupabase.from.mockImplementation(() => {
      callCount++;
      return mockSupabase;
    });

    mockSupabase.select.mockImplementation((fields, opts) => {
      if (opts && opts.count === 'exact') {
        // parlays count
        return { count: 100, error: null };
      }
      if (fields === 'final_grade') {
        // grades
        return {
          data: [
            { final_grade: 'A' },
            { final_grade: 'B' },
            { final_grade: 'A' },
            { final_grade: 'A' },
          ],
          error: null,
        };
      }
      if (fields === 'kill_conditions') {
        // return this for the .not() chain
        return mockSupabase;
      }
      return mockSupabase;
    });

    mockSupabase.not.mockReturnValue({
      data: [
        { kill_conditions: ['back-to-back'] },
        { kill_conditions: ['injury'] },
        { kill_conditions: [] },
      ],
      error: null,
    });

    const app = buildStatsApp();
    const res = await request(app).get('/stats/public');

    expect(res.status).toBe(200);
    expect(res.body).toHaveProperty('parlays_graded');
    expect(res.body).toHaveProperty('avg_grade');
    expect(res.body).toHaveProperty('kill_conditions_caught');
    expect(res.body).toHaveProperty('sports_covered');
    expect(res.body.parlays_graded).toBe(100);
    expect(res.body.avg_grade).toBe('A');
    expect(res.body.kill_conditions_caught).toBe(2);
    expect(res.body.sports_covered).toEqual(['NBA', 'MLB']);
    expect(res.headers['x-vyndr-mission']).toBeDefined();
  });
});

describe('GET /stats/live', () => {
  it('returns array of max 3 items', async () => {
    const mockPicks = [
      { player: 'LeBron', stat_type: 'points', line: 25.5, direction: 'over', grade: 'A', confidence: 0.85, created_at: '2026-03-28T12:00:00Z' },
      { player: 'Curry', stat_type: 'threes', line: 3.5, direction: 'over', grade: 'B', confidence: 0.72, created_at: '2026-03-28T11:00:00Z' },
      { player: 'Jokic', stat_type: 'rebounds', line: 10.5, direction: 'under', grade: 'A', confidence: 0.9, created_at: '2026-03-28T10:00:00Z' },
    ];

    mockSupabase.limit.mockReturnValue({ data: mockPicks, error: null });

    const app = buildStatsApp();
    const res = await request(app).get('/stats/live');

    expect(res.status).toBe(200);
    expect(Array.isArray(res.body)).toBe(true);
    expect(res.body.length).toBeLessThanOrEqual(3);
    expect(res.body[0]).toHaveProperty('player');
    expect(res.body[0]).toHaveProperty('stat');
    expect(res.body[0]).toHaveProperty('sport', 'NBA');
    expect(res.body[0]).toHaveProperty('graded_at');
    expect(res.headers['x-vyndr-mission']).toBeDefined();
  });

  it('returns 503 on supabase error', async () => {
    mockSupabase.limit.mockReturnValue({ data: null, error: new Error('db down') });

    const app = buildStatsApp();
    const res = await request(app).get('/stats/live');

    expect(res.status).toBe(503);
    expect(res.headers['x-vyndr-mission']).toBeDefined();
  });
});

describe('GET /props/joint-history', () => {
  it('blocks free tier with 403', async () => {
    const app = buildPropsApp({ id: '123', tier: 'free' });
    const res = await request(app)
      .get('/props/joint-history')
      .query({ player_a: 'LeBron', stat_a: 'points', player_b: 'AD', stat_b: 'rebounds' });

    expect(res.status).toBe(403);
    expect(res.body.error).toMatch(/Analyst or Desk/);
  });

  it('returns sample_size 0 when no data', async () => {
    let eqCalls = 0;
    mockSupabase.eq.mockImplementation(() => {
      eqCalls++;
      if (eqCalls >= 4) return { data: [], error: null };
      return mockSupabase;
    });

    const app = buildPropsApp({ id: '123', tier: 'analyst' });
    const res = await request(app)
      .get('/props/joint-history')
      .query({ player_a: 'LeBron', stat_a: 'points', player_b: 'AD', stat_b: 'rebounds' });

    expect(res.status).toBe(200);
    expect(res.body.sample_size).toBe(0);
  });

  it('returns 400 when missing query params', async () => {
    const app = buildPropsApp({ id: '123', tier: 'analyst' });
    const res = await request(app).get('/props/joint-history');

    expect(res.status).toBe(400);
  });
});
