const request = require('supertest');

// Mock Redis
const mockRedis = { get: jest.fn(), set: jest.fn(), hset: jest.fn(), hgetall: jest.fn(), expire: jest.fn() };
jest.mock('../../src/utils/redis', () => ({ getRedisClient: () => mockRedis }));

// Mock Supabase
const mockSupabaseFrom = jest.fn();
const mockSupabaseAuth = { getUser: jest.fn() };
jest.mock('../../src/utils/supabase', () => ({
  getSupabaseClient: () => ({ auth: mockSupabaseAuth, from: mockSupabaseFrom }),
  getSupabaseServiceClient: () => ({ auth: mockSupabaseAuth, from: mockSupabaseFrom }),
}));

jest.mock('axios');

process.env.ODDS_API_KEY = 'test-key';
const app = require('../../src/app');

const MOCK_USER = {
  id: 'user-1',
  email: 'test@test.com',
  tier: 'analyst',
  scan_count: 0,
  scan_reset_date: '2026-04-01',
};

const VALID_QUICKSLIP = {
  legs: [
    { player: 'Nikola Jokic', stat_type: 'points', line: 26.5, direction: 'over', odds: -110 },
  ],
  amount: 20,
  book: 'draftkings',
  bet_type: 'straight',
};

const VALID_PARLAY_SLIP = {
  legs: [
    { player: 'Nikola Jokic', stat_type: 'points', line: 26.5, direction: 'over', odds: -110 },
    { player: 'LeBron James', stat_type: 'rebounds', line: 8.5, direction: 'over', odds: -120 },
  ],
  amount: 10,
  book: 'fanduel',
  bet_type: 'parlay',
};

function setupAuthMock() {
  mockSupabaseAuth.getUser.mockResolvedValue({
    data: { user: { id: MOCK_USER.id, email: MOCK_USER.email } },
    error: null,
  });
}

function setupFullMocks() {
  setupAuthMock();

  mockSupabaseFrom.mockImplementation((table) => {
    if (table === 'users') {
      return {
        select: () => ({
          eq: () => ({
            single: () => Promise.resolve({ data: MOCK_USER, error: null }),
          }),
        }),
      };
    }
    if (table === 'bets') {
      return {
        insert: () => ({
          select: () => ({
            single: () => Promise.resolve({
              data: { id: 'bet-1', status: 'pending', amount: 20, potential_payout: 38.18, bet_type: 'straight', book: 'draftkings', created_at: '2026-03-22T00:00:00Z' },
              error: null,
            }),
          }),
        }),
        select: (sel, opts) => ({
          eq: (col, val) => ({
            eq: () => ({
              single: () => Promise.resolve({
                data: { id: 'bet-1', user_id: 'user-1', amount: 20, potential_payout: 38.18, status: 'pending', book: 'draftkings' },
                error: null,
              }),
            }),
            order: () => ({
              range: () => Promise.resolve({ data: [], count: 0, error: null }),
            }),
            in: () => Promise.resolve({ data: [], error: null }),
          }),
        }),
        update: () => ({
          eq: () => Promise.resolve({ error: null }),
        }),
      };
    }
    if (table === 'scan_sessions') {
      return {
        select: () => ({
          eq: () => ({
            eq: () => ({
              single: () => Promise.resolve({ data: { id: 'session-1' }, error: null }),
            }),
          }),
        }),
      };
    }
    if (table === 'performance') {
      return {
        upsert: () => Promise.resolve({ error: null }),
      };
    }
    return {
      select: () => ({ eq: () => ({ in: () => Promise.resolve({ data: [], error: null }) }) }),
    };
  });
}

beforeEach(() => {
  jest.clearAllMocks();
  mockRedis.get.mockResolvedValue(null);
  mockRedis.set.mockResolvedValue('OK');
  mockRedis.hset.mockResolvedValue(1);
  mockRedis.hgetall.mockResolvedValue({});
  mockRedis.expire.mockResolvedValue(1);
});

describe('POST /api/bets/quickslip', () => {
  test('creates straight bet with correct payout', async () => {
    setupFullMocks();
    const res = await request(app)
      .post('/api/bets/quickslip')
      .set('Authorization', 'Bearer valid-token')
      .send(VALID_QUICKSLIP)
      .expect(201);

    expect(res.body.bet_id).toBeDefined();
    expect(res.body.status).toBe('pending');
    expect(res.body.bet_type).toBe('straight');
  });

  test('creates parlay bet', async () => {
    setupFullMocks();
    // Override bets insert to return parlay data
    const res = await request(app)
      .post('/api/bets/quickslip')
      .set('Authorization', 'Bearer valid-token')
      .send(VALID_PARLAY_SLIP)
      .expect(201);

    expect(res.body.bet_id).toBeDefined();
  });

  test('returns 400 for missing legs', async () => {
    setupFullMocks();
    const res = await request(app)
      .post('/api/bets/quickslip')
      .set('Authorization', 'Bearer valid-token')
      .send({ amount: 20, book: 'draftkings', bet_type: 'straight' })
      .expect(400);

    expect(res.body.error).toContain('legs');
  });

  test('returns 400 for invalid bet_type', async () => {
    setupFullMocks();
    const res = await request(app)
      .post('/api/bets/quickslip')
      .set('Authorization', 'Bearer valid-token')
      .send({ ...VALID_QUICKSLIP, bet_type: 'invalid' })
      .expect(400);

    expect(res.body.error).toContain('bet_type');
  });

  test('returns 401 without auth', async () => {
    const res = await request(app)
      .post('/api/bets/quickslip')
      .send(VALID_QUICKSLIP)
      .expect(401);

    expect(res.body.error).toContain('Authentication');
  });
});

describe('POST /api/bets/screenshot', () => {
  test('returns needs_confirmation with stub extraction', async () => {
    setupFullMocks();
    const res = await request(app)
      .post('/api/bets/screenshot')
      .set('Authorization', 'Bearer valid-token')
      .send({ book: 'draftkings' })
      .expect(201);

    expect(res.body.needs_confirmation).toBe(true);
    expect(res.body.extracted).toBeDefined();
    expect(res.body.extracted.book).toBe('draftkings');
  });
});

describe('POST /api/bets/screenshot/confirm', () => {
  test('saves confirmed bet', async () => {
    setupFullMocks();
    const res = await request(app)
      .post('/api/bets/screenshot/confirm')
      .set('Authorization', 'Bearer valid-token')
      .send(VALID_QUICKSLIP)
      .expect(201);

    expect(res.body.bet_id).toBeDefined();
    expect(res.body.status).toBe('pending');
  });
});

describe('POST /api/bets/sync', () => {
  test('returns coming_soon stub', async () => {
    setupFullMocks();
    const res = await request(app)
      .post('/api/bets/sync')
      .set('Authorization', 'Bearer valid-token')
      .expect(200);

    expect(res.body.status).toBe('coming_soon');
    expect(res.body.supported_books).toContain('draftkings');
  });
});

describe('PATCH /api/bets/:id/settle', () => {
  test('settles pending bet as won', async () => {
    setupFullMocks();
    const res = await request(app)
      .patch('/api/bets/bet-1/settle')
      .set('Authorization', 'Bearer valid-token')
      .send({ status: 'won' })
      .expect(200);

    expect(res.body.status).toBe('won');
    expect(res.body.settled_at).toBeDefined();
    expect(typeof res.body.profit).toBe('number');
  });

  test('rejects settling already-settled bet', async () => {
    setupFullMocks();
    // Override bet fetch to return already-settled
    mockSupabaseFrom.mockImplementation((table) => {
      if (table === 'users') {
        return { select: () => ({ eq: () => ({ single: () => Promise.resolve({ data: MOCK_USER, error: null }) }) }) };
      }
      if (table === 'bets') {
        return {
          select: () => ({
            eq: () => ({
              eq: () => ({
                single: () => Promise.resolve({
                  data: { id: 'bet-1', user_id: 'user-1', amount: 20, potential_payout: 38, status: 'won' },
                  error: null,
                }),
              }),
            }),
          }),
        };
      }
      return { select: () => ({ eq: () => Promise.resolve({ data: [] }) }) };
    });

    const res = await request(app)
      .patch('/api/bets/bet-1/settle')
      .set('Authorization', 'Bearer valid-token')
      .send({ status: 'lost' })
      .expect(422);

    expect(res.body.error).toContain('already settled');
  });

  test('returns 400 for invalid status', async () => {
    setupFullMocks();
    const res = await request(app)
      .patch('/api/bets/bet-1/settle')
      .set('Authorization', 'Bearer valid-token')
      .send({ status: 'invalid' })
      .expect(400);

    expect(res.body.error).toContain('Invalid status');
  });
});

describe('GET /api/bets', () => {
  test('returns user bets with pagination', async () => {
    setupFullMocks();
    const res = await request(app)
      .get('/api/bets')
      .set('Authorization', 'Bearer valid-token')
      .expect(200);

    expect(Array.isArray(res.body.bets)).toBe(true);
    expect(typeof res.body.total).toBe('number');
    expect(res.body.limit).toBeDefined();
    expect(res.body.offset).toBeDefined();
  });
});

describe('GET /api/bets/performance', () => {
  test('returns performance stats', async () => {
    setupFullMocks();
    const res = await request(app)
      .get('/api/bets/performance')
      .set('Authorization', 'Bearer valid-token')
      .expect(200);

    // Performance service returns results for weekly/monthly/all_time
    expect(res.body).toBeDefined();
  });
});
