import { TestBed } from '@angular/core/testing';
import { ApolloTestingController, ApolloTestingModule } from 'apollo-angular/testing';
import { StudentService } from './student.service';
import { GET_STUDENTS, GET_STUDENT } from '@core/graphql/queries/students.queries';
import { CREATE_STUDENT, DELETE_STUDENT } from '@core/graphql/mutations/students.mutations';

describe('StudentService', () => {
  let service: StudentService;
  let controller: ApolloTestingController;

  beforeEach(() => {
    TestBed.configureTestingModule({
      imports: [ApolloTestingModule],
      providers: [StudentService],
    });

    service = TestBed.inject(StudentService);
    controller = TestBed.inject(ApolloTestingController);
  });

  afterEach(() => {
    controller.verify();
  });

  describe('getStudents', () => {
    it('should return students list', (done) => {
      const mockStudents = [
        { id: 1, name: 'Juan Pérez', email: 'juan@test.com', totalCredits: 6, enrollments: [] },
        { id: 2, name: 'María García', email: 'maria@test.com', totalCredits: 3, enrollments: [] },
      ];

      service.getStudents().subscribe((result) => {
        expect(result.data).toEqual(mockStudents);
        done();
      });

      const op = controller.expectOne(GET_STUDENTS);
      expect(op.operation.operationName).toBe('GetStudents');
      op.flush({ data: { students: mockStudents } });
    });

    it('should return empty array when no students', (done) => {
      service.getStudents().subscribe((result) => {
        expect(result.data).toEqual([]);
        done();
      });

      const op = controller.expectOne(GET_STUDENTS);
      op.flush({ data: { students: [] } });
    });
  });

  describe('getStudent', () => {
    it('should return student by id', (done) => {
      const mockStudent = {
        id: 1, name: 'Juan Pérez', email: 'juan@test.com',
        totalCredits: 6, enrollments: []
      };

      service.getStudent(1).subscribe((result) => {
        expect(result.data).toEqual(mockStudent);
        done();
      });

      const op = controller.expectOne(GET_STUDENT);
      expect(op.operation.variables['id']).toBe(1);
      op.flush({ data: { student: mockStudent } });
    });

    it('should return null when student not found', (done) => {
      service.getStudent(999).subscribe((result) => {
        expect(result.data).toBeNull();
        done();
      });

      const op = controller.expectOne(GET_STUDENT);
      op.flush({ data: { student: null } });
    });
  });

  describe('createStudent', () => {
    it('should create student and return payload', (done) => {
      const input = { name: 'Nuevo Estudiante', email: 'nuevo@test.com' };
      const mockResponse = {
        student: { id: 3, name: 'Nuevo Estudiante', email: 'nuevo@test.com', totalCredits: 0, enrollments: [] },
        errors: null,
      };

      service.createStudent(input).subscribe((result) => {
        expect(result.student).toBeDefined();
        expect(result.student?.name).toBe('Nuevo Estudiante');
        done();
      });

      const op = controller.expectOne(CREATE_STUDENT);
      expect(op.operation.variables['input']).toEqual(input);
      op.flush({ data: { createStudent: mockResponse } });

      // Flush refetch query
      const refetch = controller.expectOne(GET_STUDENTS);
      refetch.flush({ data: { students: [] } });
    });

    it('should return errors when validation fails', (done) => {
      const input = { name: '', email: 'invalid' };
      const mockResponse = {
        student: null,
        errors: ['Name is required', 'Invalid email format'],
      };

      service.createStudent(input).subscribe((result) => {
        expect(result.student).toBeNull();
        expect(result.errors).toContain('Name is required');
        done();
      });

      const op = controller.expectOne(CREATE_STUDENT);
      op.flush({ data: { createStudent: mockResponse } });

      // Flush refetch query
      const refetch = controller.expectOne(GET_STUDENTS);
      refetch.flush({ data: { students: [] } });
    });
  });

  describe('deleteStudent', () => {
    it('should delete student and return success', (done) => {
      const mockResponse = { success: true, errors: null };

      service.deleteStudent(1).subscribe((result) => {
        expect(result.success).toBeTrue();
        done();
      });

      const op = controller.expectOne(DELETE_STUDENT);
      expect(op.operation.variables['id']).toBe(1);
      op.flush({ data: { deleteStudent: mockResponse } });

      // Flush refetch query
      const refetch = controller.expectOne(GET_STUDENTS);
      refetch.flush({ data: { students: [] } });
    });
  });
});
