import { Test, TestingModule } from '@nestjs/testing';
import { INestApplication, ValidationPipe } from '@nestjs/common';
import * as request from 'supertest';
import { AppModule } from '../src/app.module';
import { DataSource } from 'typeorm';

describe('Children (e2e)', () => {
  let app: INestApplication;
  let dataSource: DataSource;

  // Test user and auth
  const testUser = {
    email: `test-children-${Date.now()}@example.com`,
    password: 'SecurePass123!',
    name: 'Test Parent',
    deviceInfo: {
      deviceId: 'test-device-children',
      platform: 'ios',
      model: 'iPhone 14',
      osVersion: '17.0',
    },
  };

  let accessToken: string;
  let userId: string;
  let familyId: string;
  let childId: string;

  beforeAll(async () => {
    const moduleFixture: TestingModule = await Test.createTestingModule({
      imports: [AppModule],
    }).compile();

    app = moduleFixture.createNestApplication();

    app.useGlobalPipes(
      new ValidationPipe({
        whitelist: true,
        forbidNonWhitelisted: true,
        transform: true,
      }),
    );

    await app.init();

    dataSource = app.get(DataSource);

    // Register user and get auth token
    const registerRes = await request(app.getHttpServer())
      .post('/api/v1/auth/register')
      .send(testUser);

    accessToken = registerRes.body.data.tokens.accessToken;
    userId = registerRes.body.data.user.id;
    familyId = registerRes.body.data.family.id;
  });

  afterAll(async () => {
    // Cleanup
    if (childId) {
      await dataSource.query('DELETE FROM children WHERE id = $1', [childId]);
    }
    if (userId) {
      await dataSource.query('DELETE FROM refresh_tokens WHERE user_id = $1', [
        userId,
      ]);
      await dataSource.query('DELETE FROM device_registry WHERE user_id = $1', [
        userId,
      ]);
      await dataSource.query('DELETE FROM family_members WHERE user_id = $1', [
        userId,
      ]);
    }
    if (familyId) {
      await dataSource.query('DELETE FROM families WHERE id = $1', [familyId]);
    }
    if (userId) {
      await dataSource.query('DELETE FROM users WHERE id = $1', [userId]);
    }

    await app.close();
  });

  describe('POST /api/v1/children', () => {
    it('should create a child', () => {
      return request(app.getHttpServer())
        .post(`/api/v1/children?familyId=${familyId}`)
        .set('Authorization', `Bearer ${accessToken}`)
        .send({
          name: 'Emma',
          birthDate: '2023-06-15',
          gender: 'female',
        })
        .expect(201)
        .expect((res) => {
          expect(res.body.success).toBe(true);
          expect(res.body.data).toHaveProperty('child');
          expect(res.body.data.child.name).toBe('Emma');
          expect(res.body.data.child.gender).toBe('female');
          expect(res.body.data.child.id).toMatch(/^chd_/);
          expect(res.body.data.child.familyId).toBe(familyId);

          // Store for subsequent tests
          childId = res.body.data.child.id;
        });
    });

    it('should require authentication', () => {
      return request(app.getHttpServer())
        .post(`/api/v1/children?familyId=${familyId}`)
        .send({
          name: 'Test Child',
          birthDate: '2023-01-01',
        })
        .expect(401);
    });

    it('should require familyId query parameter', () => {
      return request(app.getHttpServer())
        .post('/api/v1/children')
        .set('Authorization', `Bearer ${accessToken}`)
        .send({
          name: 'Test Child',
          birthDate: '2023-01-01',
        })
        .expect(201)
        .expect((res) => {
          expect(res.body.success).toBe(false);
          expect(res.body.error.code).toBe('VALIDATION_ERROR');
        });
    });

    it('should validate required fields', () => {
      return request(app.getHttpServer())
        .post(`/api/v1/children?familyId=${familyId}`)
        .set('Authorization', `Bearer ${accessToken}`)
        .send({
          name: 'Test Child',
          // Missing birthDate
        })
        .expect(400);
    });

    it('should validate birthDate format', () => {
      return request(app.getHttpServer())
        .post(`/api/v1/children?familyId=${familyId}`)
        .set('Authorization', `Bearer ${accessToken}`)
        .send({
          name: 'Test Child',
          birthDate: 'invalid-date',
        })
        .expect(400);
    });
  });

  describe('GET /api/v1/children', () => {
    it('should get all children for a family', () => {
      return request(app.getHttpServer())
        .get(`/api/v1/children?familyId=${familyId}`)
        .set('Authorization', `Bearer ${accessToken}`)
        .expect(200)
        .expect((res) => {
          expect(res.body.success).toBe(true);
          expect(res.body.data).toHaveProperty('children');
          expect(Array.isArray(res.body.data.children)).toBe(true);
          expect(res.body.data.children.length).toBeGreaterThan(0);
          expect(res.body.data.children[0].name).toBe('Emma');
        });
    });

    it('should get all children across all families when no familyId provided', () => {
      return request(app.getHttpServer())
        .get('/api/v1/children')
        .set('Authorization', `Bearer ${accessToken}`)
        .expect(200)
        .expect((res) => {
          expect(res.body.success).toBe(true);
          expect(res.body.data).toHaveProperty('children');
          expect(Array.isArray(res.body.data.children)).toBe(true);
        });
    });

    it('should require authentication', () => {
      return request(app.getHttpServer()).get('/api/v1/children').expect(401);
    });
  });

  describe('GET /api/v1/children/:id', () => {
    it('should get a specific child', () => {
      return request(app.getHttpServer())
        .get(`/api/v1/children/${childId}`)
        .set('Authorization', `Bearer ${accessToken}`)
        .expect(200)
        .expect((res) => {
          expect(res.body.success).toBe(true);
          expect(res.body.data).toHaveProperty('child');
          expect(res.body.data.child.id).toBe(childId);
          expect(res.body.data.child.name).toBe('Emma');
        });
    });

    it('should return 404 for non-existent child', () => {
      return request(app.getHttpServer())
        .get('/api/v1/children/chd_nonexistent')
        .set('Authorization', `Bearer ${accessToken}`)
        .expect(404);
    });

    it('should require authentication', () => {
      return request(app.getHttpServer())
        .get(`/api/v1/children/${childId}`)
        .expect(401);
    });
  });

  describe('GET /api/v1/children/:id/age', () => {
    it('should get child age', () => {
      return request(app.getHttpServer())
        .get(`/api/v1/children/${childId}/age`)
        .set('Authorization', `Bearer ${accessToken}`)
        .expect(200)
        .expect((res) => {
          expect(res.body.success).toBe(true);
          expect(res.body.data).toHaveProperty('ageInMonths');
          expect(res.body.data).toHaveProperty('ageInYears');
          expect(res.body.data).toHaveProperty('remainingMonths');
          expect(typeof res.body.data.ageInMonths).toBe('number');
        });
    });

    it('should return 404 for non-existent child', () => {
      return request(app.getHttpServer())
        .get('/api/v1/children/chd_nonexistent/age')
        .set('Authorization', `Bearer ${accessToken}`)
        .expect(404);
    });
  });

  describe('PATCH /api/v1/children/:id', () => {
    it('should update a child', () => {
      return request(app.getHttpServer())
        .patch(`/api/v1/children/${childId}`)
        .set('Authorization', `Bearer ${accessToken}`)
        .send({
          name: 'Emma Updated',
          medicalInfo: {
            allergies: ['peanuts'],
          },
        })
        .expect(200)
        .expect((res) => {
          expect(res.body.success).toBe(true);
          expect(res.body.data.child.name).toBe('Emma Updated');
          expect(res.body.data.child.medicalInfo.allergies).toEqual([
            'peanuts',
          ]);
        });
    });

    it('should return 404 for non-existent child', () => {
      return request(app.getHttpServer())
        .patch('/api/v1/children/chd_nonexistent')
        .set('Authorization', `Bearer ${accessToken}`)
        .send({
          name: 'Test',
        })
        .expect(404);
    });

    it('should require authentication', () => {
      return request(app.getHttpServer())
        .patch(`/api/v1/children/${childId}`)
        .send({
          name: 'Test',
        })
        .expect(401);
    });
  });

  describe('DELETE /api/v1/children/:id', () => {
    it('should soft delete a child', async () => {
      await request(app.getHttpServer())
        .delete(`/api/v1/children/${childId}`)
        .set('Authorization', `Bearer ${accessToken}`)
        .expect(200)
        .expect((res) => {
          expect(res.body.success).toBe(true);
          expect(res.body.message).toBe('Child deleted successfully');
        });

      // Verify child is soft deleted (not visible in list)
      await request(app.getHttpServer())
        .get(`/api/v1/children?familyId=${familyId}`)
        .set('Authorization', `Bearer ${accessToken}`)
        .expect(200)
        .expect((res) => {
          const deletedChild = res.body.data.children.find(
            (c: any) => c.id === childId,
          );
          expect(deletedChild).toBeUndefined();
        });
    });

    it('should return 404 for non-existent child', () => {
      return request(app.getHttpServer())
        .delete('/api/v1/children/chd_nonexistent')
        .set('Authorization', `Bearer ${accessToken}`)
        .expect(404);
    });

    it('should require authentication', () => {
      return request(app.getHttpServer())
        .delete(`/api/v1/children/${childId}`)
        .expect(401);
    });
  });
});
