package com.ffii.fpsms.modules.master.service

import com.ffii.fpsms.modules.master.entity.Bom
import com.ffii.fpsms.modules.master.entity.BomRepository
import com.ffii.fpsms.modules.settings.entity.BomWeightingScoreRepository
import org.springframework.stereotype.Service
import org.springframework.transaction.annotation.Transactional
import java.math.BigDecimal
import java.math.RoundingMode

@Service
open class BomScoreRecalculateService(
    private val bomRepository: BomRepository,
    private val bomWeightingScoreRepository: BomWeightingScoreRepository,
) {

    /**
     * Recalculate baseScore for all non-deleted BOMs and persist the result.
     */
    @Transactional
    open fun recalculateAllBaseScores(): Int {
        val boms: List<Bom> = bomRepository.findAllByDeletedIsFalse()
        if (boms.isEmpty()) return 0

        boms.forEach { bom ->
            val newScore = calculateBaseScoreForBom(bom)
            bom.baseScore = newScore
        }

        bomRepository.saveAll(boms)
        return boms.size
    }

    /**
     * Same logic as BomService.calculateBaseScore (984–1023),
     * duplicated to avoid modifying BomService.kt.
     */
    private fun calculateBaseScoreForBom(bom: Bom): BigDecimal {
        val scale = 2
        val roundingMode = RoundingMode.HALF_UP
        var sum = BigDecimal.ZERO.setScale(scale, roundingMode)

        // Score columns: contribution = (extractedScore / range) * weighting * 100
        val scoreColumns: List<Pair<String, BigDecimal>> = listOf(
            "isDark" to (bom.isDark?.toBigDecimal() ?: BigDecimal.ZERO),
            "isFloat" to (bom.isFloat?.toBigDecimal() ?: BigDecimal.ZERO),
            "isDense" to (bom.isDense?.toBigDecimal() ?: BigDecimal.ZERO),
            "allergicSubstances" to (bom.allergicSubstances?.toBigDecimal() ?: BigDecimal.ZERO),
            "timeSequence" to (bom.timeSequence?.toBigDecimal() ?: BigDecimal.ZERO),
            "complexity" to (bom.complexity?.toBigDecimal() ?: BigDecimal.ZERO),
        )

        for ((code, extractedScore) in scoreColumns) {
            val row = bomWeightingScoreRepository.findByCodeAndDeletedFalse(code) ?: continue
            val range = (row.range ?: 1).toBigDecimal()
            val weighting = row.weighting ?: continue
            if (range.compareTo(BigDecimal.ZERO) == 0) continue

            val contribution = extractedScore
                .divide(range, scale, roundingMode)
                .multiply(weighting)
                .multiply(BigDecimal(100))
                .setScale(scale, roundingMode)
            sum = sum.add(contribution)
        }

        // equipmentConflict: contribution = weighting * 100 only
        val equipmentConflictRow =
            bomWeightingScoreRepository.findByCodeAndDeletedFalse("equipmentConflict")
        if (equipmentConflictRow?.weighting != null) {
            val contribution = equipmentConflictRow.weighting!!
                .multiply(BigDecimal(100))
                .setScale(scale, roundingMode)
            sum = sum.add(contribution)
        }

        return sum
    }
}

package com.ffii.fpsms.modules.master.service

import com.ffii.fpsms.modules.master.entity.Bom
import com.ffii.fpsms.modules.master.entity.BomRepository
import com.ffii.fpsms.modules.settings.entity.BomWeightingScoreRepository
import org.springframework.stereotype.Service
import org.springframework.transaction.annotation.Transactional
import java.math.BigDecimal
import java.math.RoundingMode

@Service
open class BomScoreRecalculateService(
    private val bomRepository: BomRepository,
    private val bomWeightingScoreRepository: BomWeightingScoreRepository,
) {

    /**
     * Recalculate baseScore for all non-deleted BOMs and persist the result.
     */
    @Transactional
    open fun recalculateAllBaseScores(): Int {
        val boms: List<Bom> = bomRepository.findAllByDeletedIsFalse()
        if (boms.isEmpty()) return 0

        boms.forEach { bom ->
            val newScore = calculateBaseScoreForBom(bom)
            bom.baseScore = newScore
        }

        bomRepository.saveAll(boms)
        return boms.size
    }

    /**
     * Same logic as BomService.calculateBaseScore, but duplicated here
     * to avoid modifying the existing BomService file.
     */
    private fun calculateBaseScoreForBom(bom: Bom): BigDecimal {
        val scale = 2
        val roundingMode = RoundingMode.HALF_UP
        var sum = BigDecimal.ZERO.setScale(scale, roundingMode)

        // Score columns: contribution = (extractedScore / range) * weighting * 100
        val scoreColumns: List<Pair<String, BigDecimal>> = listOf(
            "isDark" to (bom.isDark?.toBigDecimal() ?: BigDecimal.ZERO),
            "isFloat" to (bom.isFloat?.toBigDecimal() ?: BigDecimal.ZERO),
            "isDense" to (bom.isDense?.toBigDecimal() ?: BigDecimal.ZERO),
            "allergicSubstances" to (bom.allergicSubstances?.toBigDecimal() ?: BigDecimal.ZERO),
            "timeSequence" to (bom.timeSequence?.toBigDecimal() ?: BigDecimal.ZERO),
            "complexity" to (bom.complexity?.toBigDecimal() ?: BigDecimal.ZERO),
        )

        for ((code, extractedScore) in scoreColumns) {
            val row = bomWeightingScoreRepository.findByCodeAndDeletedFalse(code) ?: continue
            val range = (row.range ?: 1).toBigDecimal()
            val weighting = row.weighting ?: continue
            if (range.compareTo(BigDecimal.ZERO) == 0.toBigDecimal()) continue

            val contribution = extractedScore
                .divide(range, scale, roundingMode)
                .multiply(weighting)
                .multiply(BigDecimal(100))
                .setScale(scale, roundingMode)
            sum = sum.add(contribution)
        }

        // equipmentConflict: contribution = weighting * 100 only
        val equipmentConflictRow =
            bomWeightingScoreRepository.findByCodeAndDeletedFalse("equipmentConflict")
        if (equipmentConflictRow?.weighting != null) {
            val contribution = equipmentConflictRow.weighting!!
                .multiply(BigDecimal(100))
                .setScale(scale, roundingMode)
            sum = sum.add(contribution)
        }

        return sum
    }
}

