/**
 * WorkReportService 테스트
 *
 * 작업 보고서 서비스 레이어 단위 테스트
 */

const workReportService = require('../../../services/workReportService');
const { ValidationError, NotFoundError, DatabaseError } = require('../../../utils/errors');
const { mockWorkReports } = require('../../helpers/mockData');

// 모델 모킹
jest.mock('../../../models/workReportModel');
const workReportModel = require('../../../models/workReportModel');

describe('WorkReportService', () => {
  beforeEach(() => {
    jest.clearAllMocks();
  });

  describe('createWorkReportService', () => {
    it('단일 보고서를 성공적으로 생성해야 함', async () => {
      // Arrange
      const reportData = {
        report_date: '2025-12-11',
        worker_id: 1,
        project_id: 1,
        work_type_id: 1,
        work_hours: 8.0,
        work_content: '기능 개발'
      };

      // workReportModel.create가 콜백 형태이므로 모킹 설정
      workReportModel.create = jest.fn((data, callback) => {
        callback(null, 123); // insertId = 123
      });

      // Act
      const result = await workReportService.createWorkReportService(reportData);

      // Assert
      expect(result).toEqual({ workReport_ids: [123] });
      expect(workReportModel.create).toHaveBeenCalledTimes(1);
      expect(workReportModel.create).toHaveBeenCalledWith(
        reportData,
        expect.any(Function)
      );
    });

    it('다중 보고서를 성공적으로 생성해야 함', async () => {
      // Arrange
      const reportsData = [
        { report_date: '2025-12-11', worker_id: 1, work_hours: 8 },
        { report_date: '2025-12-11', worker_id: 2, work_hours: 7 }
      ];

      let callCount = 0;
      workReportModel.create = jest.fn((data, callback) => {
        callCount++;
        callback(null, 100 + callCount);
      });

      // Act
      const result = await workReportService.createWorkReportService(reportsData);

      // Assert
      expect(result).toEqual({ workReport_ids: [101, 102] });
      expect(workReportModel.create).toHaveBeenCalledTimes(2);
    });

    it('빈 배열이면 ValidationError를 던져야 함', async () => {
      // Act & Assert
      await expect(workReportService.createWorkReportService([]))
        .rejects.toThrow(ValidationError);

      await expect(workReportService.createWorkReportService([]))
        .rejects.toThrow('보고서 데이터가 필요합니다');
    });

    it('DB 오류 시 DatabaseError를 던져야 함', async () => {
      // Arrange
      const reportData = { report_date: '2025-12-11', worker_id: 1 };

      workReportModel.create = jest.fn((data, callback) => {
        callback(new Error('DB connection failed'), null);
      });

      // Act & Assert
      await expect(workReportService.createWorkReportService(reportData))
        .rejects.toThrow(DatabaseError);
    });
  });

  describe('getWorkReportsByDateService', () => {
    it('날짜로 보고서를 조회해야 함', async () => {
      // Arrange
      const date = '2025-12-11';
      const mockReports = mockWorkReports.filter(r => r.report_date === date);

      workReportModel.getAllByDate = jest.fn((date, callback) => {
        callback(null, mockReports);
      });

      // Act
      const result = await workReportService.getWorkReportsByDateService(date);

      // Assert
      expect(result).toEqual(mockReports);
      expect(workReportModel.getAllByDate).toHaveBeenCalledWith(date, expect.any(Function));
    });

    it('날짜가 없으면 ValidationError를 던져야 함', async () => {
      // Act & Assert
      await expect(workReportService.getWorkReportsByDateService(null))
        .rejects.toThrow(ValidationError);

      await expect(workReportService.getWorkReportsByDateService(null))
        .rejects.toThrow('날짜가 필요합니다');
    });

    it('DB 오류 시 DatabaseError를 던져야 함', async () => {
      // Arrange
      workReportModel.getAllByDate = jest.fn((date, callback) => {
        callback(new Error('DB error'), null);
      });

      // Act & Assert
      await expect(workReportService.getWorkReportsByDateService('2025-12-11'))
        .rejects.toThrow(DatabaseError);
    });
  });

  describe('getWorkReportByIdService', () => {
    it('ID로 보고서를 조회해야 함', async () => {
      // Arrange
      const mockReport = mockWorkReports[0];

      workReportModel.getById = jest.fn((id, callback) => {
        callback(null, mockReport);
      });

      // Act
      const result = await workReportService.getWorkReportByIdService(1);

      // Assert
      expect(result).toEqual(mockReport);
      expect(workReportModel.getById).toHaveBeenCalledWith(1, expect.any(Function));
    });

    it('보고서가 없으면 NotFoundError를 던져야 함', async () => {
      // Arrange
      workReportModel.getById = jest.fn((id, callback) => {
        callback(null, null);
      });

      // Act & Assert
      await expect(workReportService.getWorkReportByIdService(999))
        .rejects.toThrow(NotFoundError);

      await expect(workReportService.getWorkReportByIdService(999))
        .rejects.toThrow('작업 보고서를 찾을 수 없습니다');
    });

    it('ID가 없으면 ValidationError를 던져야 함', async () => {
      // Act & Assert
      await expect(workReportService.getWorkReportByIdService(null))
        .rejects.toThrow(ValidationError);
    });
  });

  describe('updateWorkReportService', () => {
    it('보고서를 성공적으로 수정해야 함', async () => {
      // Arrange
      const updateData = { work_hours: 9 };

      workReportModel.update = jest.fn((id, data, callback) => {
        callback(null, 1); // affectedRows = 1
      });

      // Act
      const result = await workReportService.updateWorkReportService(123, updateData);

      // Assert
      expect(result).toEqual({ changes: 1 });
      expect(workReportModel.update).toHaveBeenCalledWith(
        123,
        updateData,
        expect.any(Function)
      );
    });

    it('수정할 보고서가 없으면 NotFoundError를 던져야 함', async () => {
      // Arrange
      workReportModel.update = jest.fn((id, data, callback) => {
        callback(null, 0); // affectedRows = 0
      });

      // Act & Assert
      await expect(workReportService.updateWorkReportService(999, {}))
        .rejects.toThrow(NotFoundError);
    });

    it('ID가 없으면 ValidationError를 던져야 함', async () => {
      // Act & Assert
      await expect(workReportService.updateWorkReportService(null, {}))
        .rejects.toThrow(ValidationError);
    });
  });

  describe('removeWorkReportService', () => {
    it('보고서를 성공적으로 삭제해야 함', async () => {
      // Arrange
      workReportModel.remove = jest.fn((id, callback) => {
        callback(null, 1);
      });

      // Act
      const result = await workReportService.removeWorkReportService(123);

      // Assert
      expect(result).toEqual({ changes: 1 });
      expect(workReportModel.remove).toHaveBeenCalledWith(123, expect.any(Function));
    });

    it('삭제할 보고서가 없으면 NotFoundError를 던져야 함', async () => {
      // Arrange
      workReportModel.remove = jest.fn((id, callback) => {
        callback(null, 0);
      });

      // Act & Assert
      await expect(workReportService.removeWorkReportService(999))
        .rejects.toThrow(NotFoundError);
    });

    it('ID가 없으면 ValidationError를 던져야 함', async () => {
      // Act & Assert
      await expect(workReportService.removeWorkReportService(null))
        .rejects.toThrow(ValidationError);
    });
  });

  describe('getWorkReportsInRangeService', () => {
    it('기간으로 보고서를 조회해야 함', async () => {
      // Arrange
      const start = '2025-12-01';
      const end = '2025-12-31';

      workReportModel.getByRange = jest.fn((start, end, callback) => {
        callback(null, mockWorkReports);
      });

      // Act
      const result = await workReportService.getWorkReportsInRangeService(start, end);

      // Assert
      expect(result).toEqual(mockWorkReports);
      expect(workReportModel.getByRange).toHaveBeenCalledWith(
        start,
        end,
        expect.any(Function)
      );
    });

    it('시작일이 없으면 ValidationError를 던져야 함', async () => {
      // Act & Assert
      await expect(workReportService.getWorkReportsInRangeService(null, '2025-12-31'))
        .rejects.toThrow(ValidationError);
    });

    it('종료일이 없으면 ValidationError를 던져야 함', async () => {
      // Act & Assert
      await expect(workReportService.getWorkReportsInRangeService('2025-12-01', null))
        .rejects.toThrow(ValidationError);
    });
  });

  describe('getSummaryService', () => {
    it('월간 요약을 조회해야 함', async () => {
      // Arrange
      const year = '2025';
      const month = '12';

      workReportModel.getByRange = jest.fn((start, end, callback) => {
        callback(null, mockWorkReports);
      });

      // Act
      const result = await workReportService.getSummaryService(year, month);

      // Assert
      expect(result).toEqual(mockWorkReports);
      expect(workReportModel.getByRange).toHaveBeenCalled();
    });

    it('연도가 없으면 ValidationError를 던져야 함', async () => {
      // Act & Assert
      await expect(workReportService.getSummaryService(null, '12'))
        .rejects.toThrow(ValidationError);
    });

    it('월이 없으면 ValidationError를 던져야 함', async () => {
      // Act & Assert
      await expect(workReportService.getSummaryService('2025', null))
        .rejects.toThrow(ValidationError);
    });

    it('데이터가 없으면 NotFoundError를 던져야 함', async () => {
      // Arrange
      workReportModel.getByRange = jest.fn((start, end, callback) => {
        callback(null, []);
      });

      // Act & Assert
      await expect(workReportService.getSummaryService('2025', '12'))
        .rejects.toThrow(NotFoundError);

      await expect(workReportService.getSummaryService('2025', '12'))
        .rejects.toThrow('해당 기간의 작업 보고서가 없습니다');
    });
  });
});
