import { afterAll, beforeAll, describe, expect, it } from 'vitest';
import request from 'supertest';
import { createApp } from '../src/app';
import { prisma } from '../src/db';

const app = createApp();

async function login(email: string) {
  const res = await request(app)
    .post('/api/auth/login')
    .send({ email, password: 'Password123!' });
  expect(res.status).toBe(200);
  return res.body.token as string;
}

describe('API integration (requires seeded Postgres)', () => {
  let devToken = '';
  let saToken = '';
  let taskId = '';

  beforeAll(async () => {
    const task = await prisma.task.findFirst({ where: { code: 'ZPT-101' } });
    if (!task) {
      throw new Error('Seed data missing — run npm run seed before tests');
    }
    taskId = task.id;
    saToken = await login('gowtham@zettai.demo');
    devToken = await login('karthik@zettai.demo');
  }, 30000);

  afterAll(async () => {
    await prisma.$disconnect();
  });

  describe('leave', () => {
    it('rejects half-day across multiple days', async () => {
      const res = await request(app)
        .post('/api/leave/requests')
        .set('Authorization', `Bearer ${devToken}`)
        .send({
          typeId: 'cl',
          fromDate: '2026-12-01',
          toDate: '2026-12-02',
          dayPeriod: 'morning',
        });
      expect(res.status).toBe(400);
      expect(res.body.error.message).toMatch(/Half-day/i);
    });

    it('allows unpaid without balance', async () => {
      const res = await request(app)
        .post('/api/leave/requests')
        .set('Authorization', `Bearer ${devToken}`)
        .send({
          typeId: 'unpaid',
          fromDate: '2026-12-10',
          toDate: '2026-12-10',
          dayPeriod: 'full',
          description: 'Unpaid day',
        });
      expect(res.status).toBe(201);
      expect(res.body.request.typeId).toBe('unpaid');
    });

    it('rejects over-balance CL', async () => {
      const res = await request(app)
        .post('/api/leave/requests')
        .set('Authorization', `Bearer ${devToken}`)
        .send({
          typeId: 'cl',
          fromDate: '2026-12-01',
          toDate: '2026-12-20',
          dayPeriod: 'full',
        });
      expect(res.status).toBe(400);
      expect(res.body.error.message).toMatch(/Insufficient balance/i);
    });
  });

  describe('timer', () => {
    it('starts, stops into fromTimer timesheet, double-stop 404', async () => {
      await request(app)
        .post('/api/timer/discard')
        .set('Authorization', `Bearer ${devToken}`);

      const start = await request(app)
        .post('/api/timer/start')
        .set('Authorization', `Bearer ${devToken}`)
        .send({ taskId });
      expect(start.status).toBe(201);
      expect(start.body.timer.status).toBe('running');

      const stop = await request(app)
        .post('/api/timer/stop')
        .set('Authorization', `Bearer ${devToken}`)
        .send({ force: true, description: 'test stop' });
      expect(stop.status).toBe(200);
      expect(stop.body.timesheet.fromTimer).toBe(true);

      const again = await request(app)
        .post('/api/timer/stop')
        .set('Authorization', `Bearer ${devToken}`)
        .send({ force: true });
      expect(again.status).toBe(404);
    });

    it('switch=true replaces active timer', async () => {
      await request(app)
        .post('/api/timer/discard')
        .set('Authorization', `Bearer ${devToken}`);

      const other = await prisma.task.findFirst({ where: { code: 'ZPT-102' } });
      expect(other).toBeTruthy();

      await request(app)
        .post('/api/timer/start')
        .set('Authorization', `Bearer ${devToken}`)
        .send({ taskId });

      const switched = await request(app)
        .post('/api/timer/start')
        .set('Authorization', `Bearer ${devToken}`)
        .send({ taskId: other!.id, switch: true });
      expect(switched.status).toBe(201);
      expect(switched.body.timer.taskId).toBe(other!.id);

      await request(app)
        .post('/api/timer/discard')
        .set('Authorization', `Bearer ${devToken}`);
    });
  });

  describe('reports + rbac smoke', () => {
    it('SA dashboard and project report', async () => {
      const dash = await request(app)
        .get('/api/reports/dashboard')
        .set('Authorization', `Bearer ${saToken}`);
      expect(dash.status).toBe(200);
      expect(dash.body.kind).toBe('super_admin');

      const rep = await request(app)
        .get('/api/reports/project')
        .set('Authorization', `Bearer ${saToken}`);
      expect(rep.status).toBe(200);
      expect(Array.isArray(rep.body.rows)).toBe(true);
    });

    it('Developer cannot manage leave allocations', async () => {
      const res = await request(app)
        .get('/api/leave/allocations')
        .set('Authorization', `Bearer ${devToken}`);
      expect(res.status).toBe(403);
    });

    it('list endpoints return hasMore', async () => {
      const res = await request(app)
        .get('/api/tasks?limit=2')
        .set('Authorization', `Bearer ${saToken}`);
      expect(res.status).toBe(200);
      expect(res.body.limit).toBe(2);
      expect(typeof res.body.hasMore).toBe('boolean');
      expect(res.body.tasks.length).toBeLessThanOrEqual(2);
    });
  });
});
