package com.ffii.fpsms.m18.service

import com.ffii.fpsms.m18.entity.M18GoodsReceiptNoteLogRepository
import org.slf4j.LoggerFactory
import org.springframework.stereotype.Service
import org.springframework.transaction.annotation.Transactional
import java.time.LocalDate
import java.time.LocalDateTime
import java.time.ZoneId
import java.time.ZonedDateTime

/**
 * Fills [com.ffii.fpsms.m18.entity.M18GoodsReceiptNoteLog.grnCode] by calling M18 GET /root/api/read/an?id=...
 */
@Service
open class M18GrnCodeSyncService(
    private val m18GoodsReceiptNoteLogRepository: M18GoodsReceiptNoteLogRepository,
    private val m18GoodsReceiptNoteService: M18GoodsReceiptNoteService,
) {
    private val logger = LoggerFactory.getLogger(M18GrnCodeSyncService::class.java)

    /**
     * Backfill `grn_code` for rows created in **[start of calendar day (today − syncOffsetDays), now]** (inclusive),
     * with `m18_record_id` set and `grn_code` empty.
     *
     * Example: `syncOffsetDays = 4` → `created` from 4 days ago 00:00:00 up to **now**.
     * Example: `syncOffsetDays = 0` → `created` from **today** 00:00:00 up to **now**.
     *
     * One M18 read per distinct [m18_record_id]; all matching log rows get the same code.
     */
    @Transactional
    open fun syncGrnCodesForLookback(syncOffsetDays: Int, zoneId: ZoneId = ZoneId.systemDefault()): Int {
        val days = syncOffsetDays.coerceAtLeast(0)
        val nowZ = ZonedDateTime.now(zoneId)
        val startLdt = nowZ.toLocalDate().minusDays(days.toLong()).atStartOfDay(zoneId).toLocalDateTime()
        val endLdt = nowZ.toLocalDateTime()
        return syncGrnCodesInWindow(startLdt, endLdt, "lookback syncOffsetDays=$days")
    }

    /**
     * Single calendar day window: [day 00:00, next day 00:00) — useful for manual/testing.
     */
    @Transactional
    open fun syncGrnCodesForDate(day: LocalDate, zoneId: ZoneId = ZoneId.systemDefault()): Int {
        val startLdt = day.atStartOfDay(zoneId).toLocalDateTime()
        val endLdt = day.plusDays(1).atStartOfDay(zoneId).toLocalDateTime().minusNanos(1)
        return syncGrnCodesInWindow(startLdt, endLdt, "single day=$day")
    }

    private fun syncGrnCodesInWindow(startLdt: LocalDateTime, endLdt: LocalDateTime, label: String): Int {
        val rows = m18GoodsReceiptNoteLogRepository.findNeedingGrnCode(startLdt, endLdt)
        if (rows.isEmpty()) {
            logger.info("[M18GrnCodeSync] No GRN log rows need grn_code ($label, window=[$startLdt, $endLdt])")
            return 0
        }
        val byRecord = rows.groupBy { it.m18RecordId }
        var updated = 0
        for ((recordId, list) in byRecord) {
            val rid = recordId ?: continue
            val code = m18GoodsReceiptNoteService.readAnCodeByRecordId(rid)
            if (code.isNullOrBlank()) {
                logger.warn("[M18GrnCodeSync] No code from M18 for m18_record_id=$rid ($label)")
                continue
            }
            for (log in list) {
                log.grnCode = code
                m18GoodsReceiptNoteLogRepository.save(log)
                updated++
            }
            logger.info("[M18GrnCodeSync] Set grn_code=$code for m18_record_id=$rid (${list.size} row(s))")
        }
        return updated
    }

    /** Convenience: lookback 0 days = from start of today to now. */
    open fun syncGrnCodesForToday(): Int = syncGrnCodesForLookback(0)
}
