import { Test, TestingModule } from '@nestjs/testing'
import { AuthorizationValidatorService, IReferralRepository } from '@/domain/services/authorization-validator.service'
import { AssessmentCalculatorService, TeamStatistics, ITeamStatisticsRepository } from '@/domain/services/assessment-calculator.service'
import { IAuthorizationRoleRepository } from '@/domain/repositories/authorization-role.repository'
import { IMonthlyAssessmentRepository } from '@/domain/repositories/monthly-assessment.repository'
import { AUTHORIZATION_ROLE_REPOSITORY } from '@/infrastructure/persistence/repositories/authorization-role.repository.impl'
import { MONTHLY_ASSESSMENT_REPOSITORY } from '@/infrastructure/persistence/repositories/monthly-assessment.repository.impl'
import { UserId, RegionCode, Month, AuthorizationId } from '@/domain/value-objects'
import { RoleType, AuthorizationStatus } from '@/domain/enums'
import { AuthorizationRole } from '@/domain/aggregates'
import { LadderTargetRule } from '@/domain/entities'

describe('Domain Services Integration Tests', () => {
  let module: TestingModule
  let validatorService: AuthorizationValidatorService
  let calculatorService: AssessmentCalculatorService

  // Mock repositories
  const mockAuthorizationRoleRepository: jest.Mocked<IAuthorizationRoleRepository> = {
    save: jest.fn(),
    findById: jest.fn(),
    findByUserIdAndRoleType: jest.fn(),
    findByUserIdRoleTypeAndRegion: jest.fn(),
    findByUserId: jest.fn(),
    findActiveByRoleTypeAndRegion: jest.fn(),
    findAllActive: jest.fn(),
    findPendingByUserId: jest.fn(),
    findByStatus: jest.fn(),
    delete: jest.fn(),
    findByAccountSequenceAndRoleType: jest.fn(),
    findByAccountSequence: jest.fn(),
    findActiveCommunityByAccountSequences: jest.fn(),
    findActiveProvinceByAccountSequencesAndRegion: jest.fn(),
    findActiveCityByAccountSequencesAndRegion: jest.fn(),
    findCommunityByAccountSequences: jest.fn(),
    findAuthProvinceByAccountSequencesAndRegion: jest.fn(),
    findAuthCityByAccountSequencesAndRegion: jest.fn(),
    findAuthProvinceByAccountSequences: jest.fn(),
    findAuthCityByAccountSequences: jest.fn(),
    findProvinceCompanyByRegion: jest.fn(),
    findCityCompanyByRegion: jest.fn(),
    findExpiredActiveByRoleType: jest.fn(),
    findCommunityByName: jest.fn(),
    findAllByUserIdIncludeDeleted: jest.fn(),
    findAllByAccountSequenceIncludeDeleted: jest.fn(),
    findByIdIncludeDeleted: jest.fn(),
    findAllActiveAuthProvinceCompanies: jest.fn(),
    findAllActiveAuthCityCompanies: jest.fn(),
  }

  const mockMonthlyAssessmentRepository: jest.Mocked<IMonthlyAssessmentRepository> = {
    save: jest.fn(),
    saveAll: jest.fn(),
    findById: jest.fn(),
    findByAuthorizationAndMonth: jest.fn(),
    findByUserAndMonth: jest.fn(),
    findFirstByAuthorization: jest.fn(),
    findByMonthAndRegion: jest.fn(),
    findRankingsByMonthAndRegion: jest.fn(),
    findRankingsByMonthAndRoleType: jest.fn(),
    findByAuthorization: jest.fn(),
    delete: jest.fn(),
  }

  // Mock referral repository
  const mockReferralRepository: jest.Mocked<IReferralRepository> = {
    findByUserId: jest.fn(),
    getAllAncestors: jest.fn(),
    getAllDescendants: jest.fn(),
  }

  // Mock team statistics repository
  const mockTeamStatisticsRepository: jest.Mocked<ITeamStatisticsRepository> = {
    findByUserId: jest.fn(),
    findByAccountSequence: jest.fn(),
    getReferralChain: jest.fn(),
  }

  beforeAll(async () => {
    module = await Test.createTestingModule({
      providers: [
        AuthorizationValidatorService,
        AssessmentCalculatorService,
        {
          provide: AUTHORIZATION_ROLE_REPOSITORY,
          useValue: mockAuthorizationRoleRepository,
        },
        {
          provide: MONTHLY_ASSESSMENT_REPOSITORY,
          useValue: mockMonthlyAssessmentRepository,
        },
        {
          provide: 'REFERRAL_REPOSITORY',
          useValue: mockReferralRepository,
        },
        {
          provide: 'TEAM_STATISTICS_REPOSITORY',
          useValue: mockTeamStatisticsRepository,
        },
      ],
    }).compile()

    validatorService = module.get<AuthorizationValidatorService>(AuthorizationValidatorService)
    calculatorService = module.get<AssessmentCalculatorService>(AssessmentCalculatorService)
  })

  afterAll(async () => {
    await module.close()
  })

  beforeEach(() => {
    jest.clearAllMocks()
  })

  describe('AuthorizationValidatorService', () => {
    describe('validateAuthorizationRequest', () => {
      it('should return success when no conflicts in referral chain', async () => {
        const userId = UserId.create('user-123', 'D2412190123')
        const roleType = RoleType.AUTH_PROVINCE_COMPANY
        const regionCode = RegionCode.create('430000')

        mockAuthorizationRoleRepository.findByUserIdAndRoleType.mockResolvedValue(null)
        mockReferralRepository.findByUserId.mockResolvedValue(null)

        const result = await validatorService.validateAuthorizationRequest(
          userId,
          roleType,
          regionCode,
          mockReferralRepository,
          mockAuthorizationRoleRepository,
        )

        expect(result.isValid).toBe(true)
      })

      it('should return failure when user already has province authorization', async () => {
        const userId = UserId.create('user-123', 'D2412190123')
        const roleType = RoleType.AUTH_PROVINCE_COMPANY
        const regionCode = RegionCode.create('430000')

        const existingAuth = AuthorizationRole.createAuthProvinceCompany({
          userId,
          provinceCode: '440000',
          provinceName: '广东省',
        })
        mockAuthorizationRoleRepository.findByUserIdAndRoleType.mockResolvedValue(existingAuth)

        const result = await validatorService.validateAuthorizationRequest(
          userId,
          roleType,
          regionCode,
          mockReferralRepository,
          mockAuthorizationRoleRepository,
        )

        expect(result.isValid).toBe(false)
        expect(result.errorMessage).toContain('只能申请一个省代或市代授权')
      })

      it('should return failure when ancestor has same region authorization', async () => {
        const userId = UserId.create('user-123', 'D2412190123')
        const roleType = RoleType.AUTH_PROVINCE_COMPANY
        const regionCode = RegionCode.create('430000')

        const ancestorUserId = UserId.create('ancestor-user', 'D2412190999')

        mockAuthorizationRoleRepository.findByUserIdAndRoleType.mockResolvedValue(null)
        mockReferralRepository.findByUserId.mockResolvedValue({ parentId: 'ancestor-user' })
        mockReferralRepository.getAllAncestors.mockResolvedValue([ancestorUserId])
        mockReferralRepository.getAllDescendants.mockResolvedValue([])

        const existingAuth = AuthorizationRole.createAuthProvinceCompany({
          userId: ancestorUserId,
          provinceCode: '430000',
          provinceName: '湖南省',
        })
        mockAuthorizationRoleRepository.findByUserIdRoleTypeAndRegion.mockResolvedValue(existingAuth)

        const result = await validatorService.validateAuthorizationRequest(
          userId,
          roleType,
          regionCode,
          mockReferralRepository,
          mockAuthorizationRoleRepository,
        )

        expect(result.isValid).toBe(false)
        expect(result.errorMessage).toContain('本团队已有人申请')
      })
    })
  })

  describe('AssessmentCalculatorService', () => {
    describe('assessAndRankRegion', () => {
      it('should calculate assessments and rank by exceed ratio', async () => {
        const roleType = RoleType.AUTH_PROVINCE_COMPANY
        const regionCode = RegionCode.create('430000')
        const assessmentMonth = Month.current()

        // Mock two authorizations
        const auth1 = AuthorizationRole.createAuthProvinceCompany({
          userId: UserId.create('user-1', 'D2412190001'),
          provinceCode: '430000',
          provinceName: '湖南省',
        })
        auth1.authorize(UserId.create('admin', 'D2412190100'))

        const auth2 = AuthorizationRole.createAuthProvinceCompany({
          userId: UserId.create('user-2', 'D2412190002'),
          provinceCode: '430000',
          provinceName: '湖南省',
        })
        auth2.authorize(UserId.create('admin', 'D2412190100'))

        mockAuthorizationRoleRepository.findActiveByRoleTypeAndRegion.mockResolvedValue([auth1, auth2])
        mockMonthlyAssessmentRepository.findByAuthorizationAndMonth.mockResolvedValue(null)

        // User 1 has better stats
        mockTeamStatisticsRepository.findByUserId
          .mockResolvedValueOnce({
            userId: 'user-1',
            accountSequence: 'D2412190001',
            totalTeamPlantingCount: 200,
            selfPlantingCount: 10,
            get subordinateTeamPlantingCount() { return this.totalTeamPlantingCount - this.selfPlantingCount },
            getProvinceTeamCount: () => 70,
            getCityTeamCount: () => 0,
          })
          .mockResolvedValueOnce({
            userId: 'user-2',
            accountSequence: 'D2412190002',
            totalTeamPlantingCount: 100,
            selfPlantingCount: 5,
            get subordinateTeamPlantingCount() { return this.totalTeamPlantingCount - this.selfPlantingCount },
            getProvinceTeamCount: () => 35,
            getCityTeamCount: () => 0,
          })

        const assessments = await calculatorService.assessAndRankRegion(
          roleType,
          regionCode,
          assessmentMonth,
          mockAuthorizationRoleRepository,
          mockTeamStatisticsRepository,
          mockMonthlyAssessmentRepository,
        )

        expect(assessments.length).toBe(2)
        // User 1 should rank first (higher exceed ratio)
        expect(assessments[0].userId.value).toBe('user-1')
        expect(assessments[0].rankingInRegion).toBe(1)
        expect(assessments[0].isFirstPlace).toBe(true)
      })
    })
  })
})

describe('LadderTargetRule Entity Integration Tests', () => {
  describe('getTarget', () => {
    it('should return correct target for province month 1', () => {
      const target = LadderTargetRule.getTarget(RoleType.AUTH_PROVINCE_COMPANY, 1)

      expect(target.monthlyTarget).toBe(150)
      expect(target.cumulativeTarget).toBe(150)
    })

    it('should return correct target for province month 5', () => {
      const target = LadderTargetRule.getTarget(RoleType.AUTH_PROVINCE_COMPANY, 5)

      expect(target.monthlyTarget).toBe(2400)
      expect(target.cumulativeTarget).toBe(4650)
    })

    it('should return month 9 target for months beyond 9', () => {
      const target = LadderTargetRule.getTarget(RoleType.AUTH_PROVINCE_COMPANY, 12)

      expect(target.monthlyTarget).toBe(11750)
      expect(target.cumulativeTarget).toBe(50000)
    })

    it('should return correct target for city month 1', () => {
      const target = LadderTargetRule.getTarget(RoleType.AUTH_CITY_COMPANY, 1)

      expect(target.monthlyTarget).toBe(30)
      expect(target.cumulativeTarget).toBe(30)
    })

    it('should return correct target for city month 9', () => {
      const target = LadderTargetRule.getTarget(RoleType.AUTH_CITY_COMPANY, 9)

      expect(target.monthlyTarget).toBe(2350)
      expect(target.cumulativeTarget).toBe(10000)
    })

    it('should return fixed target for community', () => {
      const target = LadderTargetRule.getTarget(RoleType.COMMUNITY, 1)

      expect(target.monthlyTarget).toBe(10)
      expect(target.cumulativeTarget).toBe(10)
    })
  })

  describe('getFinalTarget', () => {
    it('should return 50000 for province', () => {
      expect(LadderTargetRule.getFinalTarget(RoleType.AUTH_PROVINCE_COMPANY)).toBe(50000)
    })

    it('should return 10000 for city', () => {
      expect(LadderTargetRule.getFinalTarget(RoleType.AUTH_CITY_COMPANY)).toBe(10000)
    })

    it('should return 10 for community', () => {
      expect(LadderTargetRule.getFinalTarget(RoleType.COMMUNITY)).toBe(10)
    })
  })

  describe('getAllTargets', () => {
    it('should return 9 targets for province', () => {
      const targets = LadderTargetRule.getAllTargets(RoleType.AUTH_PROVINCE_COMPANY)
      expect(targets.length).toBe(9)
    })

    it('should return 9 targets for city', () => {
      const targets = LadderTargetRule.getAllTargets(RoleType.AUTH_CITY_COMPANY)
      expect(targets.length).toBe(9)
    })

    it('should return 1 target for community', () => {
      const targets = LadderTargetRule.getAllTargets(RoleType.COMMUNITY)
      expect(targets.length).toBe(1)
    })
  })
})

describe('Month Value Object Integration Tests', () => {
  describe('create and format', () => {
    it('should create month from string', () => {
      const month = Month.create('2024-03')
      expect(month.value).toBe('2024-03')
    })

    it('should create month from date', () => {
      const date = new Date('2024-06-15')
      const month = Month.fromDate(date)
      expect(month.value).toBe('2024-06')
    })

    it('should create current month', () => {
      const month = Month.current()
      const now = new Date()
      const expectedMonth = String(now.getMonth() + 1).padStart(2, '0')
      const expectedYear = now.getFullYear()
      expect(month.value).toBe(`${expectedYear}-${expectedMonth}`)
    })
  })

  describe('navigation', () => {
    it('should get next month correctly', () => {
      const month = Month.create('2024-03')
      const next = month.next()
      expect(next.value).toBe('2024-04')
    })

    it('should handle year boundary for next', () => {
      const month = Month.create('2024-12')
      const next = month.next()
      expect(next.value).toBe('2025-01')
    })

    it('should get previous month correctly', () => {
      const month = Month.create('2024-03')
      const prev = month.previous()
      expect(prev.value).toBe('2024-02')
    })

    it('should handle year boundary for previous', () => {
      const month = Month.create('2024-01')
      const prev = month.previous()
      expect(prev.value).toBe('2023-12')
    })
  })

  describe('comparison', () => {
    it('should compare months correctly', () => {
      const month1 = Month.create('2024-03')
      const month2 = Month.create('2024-06')

      expect(month1.isBefore(month2)).toBe(true)
      expect(month2.isAfter(month1)).toBe(true)
      expect(month1.equals(month1)).toBe(true)
    })

    it('should compare across years', () => {
      const month1 = Month.create('2023-12')
      const month2 = Month.create('2024-01')

      expect(month1.isBefore(month2)).toBe(true)
      expect(month2.isAfter(month1)).toBe(true)
    })
  })

  describe('extraction', () => {
    it('should extract year and month', () => {
      const month = Month.create('2024-03')

      expect(month.getYear()).toBe(2024)
      expect(month.getMonth()).toBe(3)
    })
  })
})

describe('RegionCode Value Object Integration Tests', () => {
  describe('create', () => {
    it('should create valid region code', () => {
      const regionCode = RegionCode.create('430000')
      expect(regionCode.value).toBe('430000')
    })

    it('should create city code', () => {
      const regionCode = RegionCode.create('430100')
      expect(regionCode.value).toBe('430100')
    })
  })

  describe('equality', () => {
    it('should compare equal region codes', () => {
      const code1 = RegionCode.create('430000')
      const code2 = RegionCode.create('430000')

      expect(code1.equals(code2)).toBe(true)
    })

    it('should compare different region codes', () => {
      const code1 = RegionCode.create('430000')
      const code2 = RegionCode.create('440000')

      expect(code1.equals(code2)).toBe(false)
    })
  })
})
