package com.ffii.fpsms.modules.pickOrder.service

import com.ffii.core.support.AbstractBaseEntityService
import com.ffii.core.support.JdbcDao
import com.ffii.core.utils.ExcelUtils
import org.apache.poi.ss.usermodel.Sheet
import org.apache.poi.ss.usermodel.Workbook
import org.springframework.stereotype.Service
import com.ffii.fpsms.modules.pickOrder.entity.Truck
import com.ffii.fpsms.modules.pickOrder.entity.TruckRepository
import com.ffii.fpsms.modules.pickOrder.web.models.SaveTruckRequest
import com.ffii.fpsms.modules.pickOrder.web.models.CreateTruckWithoutShopRequest
import com.ffii.fpsms.modules.pickOrder.web.models.UpdateTruckShopDetailsRequest
import java.time.LocalTime
import java.time.format.DateTimeFormatter
import com.ffii.fpsms.modules.master.entity.ShopRepository
import com.ffii.fpsms.modules.master.entity.projections.ShopAndTruck
import com.ffii.fpsms.modules.pickOrder.web.models.SaveTruckLane
import jakarta.transaction.Transactional


@Service
open class TruckService(
    private val jdbcDao: JdbcDao,
    private val truckRepository: TruckRepository,
    private val shopRepository: ShopRepository,
) : AbstractBaseEntityService<Truck, Long, TruckRepository>(jdbcDao, truckRepository) {
    open fun saveTruck(request: SaveTruckRequest): Truck {
        val truck = request.id?.let {
            truckRepository.findById(it).orElse(null)
        } ?: Truck()
        val shop = shopRepository.findById(request.shopId).orElse(null)
        if (shop == null) {
            throw IllegalArgumentException("Shop not found with id: ${request.shopId}")
        }

        truck.apply {
            this.storeId = request.store_id
            this.truckLanceCode = request.truckLanceCode
            this.departureTime = request.departureTime
            this.shop = shop
            this.shopName = request.shopName
            this.shopCode = request.shopCode
            this.loadingSequence = request.loadingSequence
            this.remark = request.remark
        }

        return truckRepository.save(truck);
    }

    private fun parseDepartureTime(timeStr: String?): LocalTime? {
        if (timeStr.isNullOrBlank()) return null

        return try {
            val cleaned = timeStr.trim().uppercase().replace(" ", "")

            // 处理 3:00AM / 5:30PM 这类 12 小时制
            if (cleaned.contains("AM") || cleaned.contains("PM")) {
                val isPM = cleaned.contains("PM")
                val timePart = cleaned.replace("AM", "").replace("PM", "")
                val parts = timePart.split(":")
                if (parts.size == 2) {
                    var hour = parts[0].toInt()
                    val minute = parts[1].toIntOrNull() ?: 0

                    if (isPM && hour != 12) hour += 12
                    if (!isPM && hour == 12) hour = 0

                    LocalTime.of(hour, minute)
                } else null
            } else {
                // 处理 17:30 / 3:00 这类 24 小时制
                try {
                    LocalTime.parse(timeStr.trim(), DateTimeFormatter.ofPattern("H:mm"))
                } catch (_: Exception) {
                    LocalTime.parse(timeStr.trim(), DateTimeFormatter.ofPattern("HH:mm"))
                }
            }
        } catch (e: Exception) {
            logger.warn("Failed to parse departure time: $timeStr", e)
            null
        }
    }

    private fun normalizeShopCode(shopCode: String): String {
        val firstDigitIndex = shopCode.indexOfFirst { it.isDigit() }
        if (firstDigitIndex == -1) {
            return shopCode
        }

        val letterPart = shopCode.substring(0, firstDigitIndex)
        val numberPart = shopCode.substring(firstDigitIndex)

        val normalizedNumber = if (numberPart.startsWith("0") && numberPart.length > 1) {
            numberPart.substring(1)
        } else {
            numberPart
        }
        return letterPart + normalizedNumber
    }

    open fun importExcel(workbook: Workbook?): String {
        logger.info("--------- Start - Import Warehouse Excel -------");

        if (workbook == null) {
            logger.error("No Excel Import");
            return "Import Excel failure";
        }
        val sheet: Sheet = workbook.getSheetAt(0);

        // Columns
        val COLUMN_STORE_ID_INDEX = 0;
        val COLUMN_REQUIRED_DELIVERY_DATE_INDEX = 1;
        val COLUMN_DEPARTURE_TIME_INDEX = 2;
        val COLUMN_TRUCK_LANCE_CODE_INDEX = 3;
        val COLUMN_SHOP_CODE_INDEX = 4;
        val COLUMN_SHOP_NAME_INDEX = 5;
        val COLUMN_LOADING_SEQUENCE_INDEX = 6;
        val COLUMN_REMARK_INDEX = 7;

        val START_ROW_INDEX = 3;
        logger.info("Total rows in sheet: ${sheet.lastRowNum + 1}, Processing from row ${START_ROW_INDEX + 1} to ${sheet.lastRowNum + 1}");
        // Start Import
        for (i in START_ROW_INDEX..<sheet.lastRowNum) {
            val row = sheet.getRow(i)

            try {
                val truckLanceCode = ExcelUtils.getStringValue(row.getCell(COLUMN_TRUCK_LANCE_CODE_INDEX)).trim()
                val departureTimeStr = ExcelUtils.getStringValue(row.getCell(COLUMN_DEPARTURE_TIME_INDEX)).trim()
                val shopName = ExcelUtils.getStringValue(row.getCell(COLUMN_SHOP_NAME_INDEX)).trim()
                val shopCode = ExcelUtils.getStringValue(row.getCell(COLUMN_SHOP_CODE_INDEX)).trim()
                val loadingSequence = ExcelUtils.getIntValue(row.getCell(COLUMN_LOADING_SEQUENCE_INDEX))
                val store_id = ExcelUtils.getStringValue(row.getCell(COLUMN_STORE_ID_INDEX)).trim()
                val remark = ExcelUtils.getStringValue(row.getCell(COLUMN_REMARK_INDEX)).trim()

                val storeIdInt = when (store_id.uppercase()) {
                    "2F" -> 2
                    "4F" -> 4
                    "3F" -> 3
                    else -> {
                        logger.warn("Invalid store_id '${store_id}', defaulting to 2")
                        2
                    }
                }
                val departureTime = parseDepartureTime(departureTimeStr)
                if (departureTime == null) {
                    logger.warn("Row ${i + 1}: Invalid departure time '$departureTimeStr', skipping")
                    continue
                }
                val normalizedShopCode = normalizeShopCode(shopCode)
                val shop = shopRepository.findAllByDeletedIsFalse().firstOrNull { it.code == normalizedShopCode }
                //println("shop: ${shop}")
                val existingTruck = truckRepository.findByShopCodeAndStoreId(shopCode, store_id)
                if (existingTruck != null) {

                    val truckRequest = SaveTruckRequest(
                        id = existingTruck.id,
                        store_id = store_id,
                        truckLanceCode = truckLanceCode ?: existingTruck.truckLanceCode ?: "",
                        departureTime = departureTime,
                        shopId = shop?.id!!,
                        shopName = shopName ?: "",
                        shopCode = normalizedShopCode,
                        loadingSequence = loadingSequence,
                        remark = remark
                    )
                    saveTruck(truckRequest)
                } else {
                   
                    val truckRequest = SaveTruckRequest(
                        id = null,
                        store_id = store_id,
                        truckLanceCode = truckLanceCode ?: "",
                        departureTime = departureTime,
                        shopId = shop?.id!!,
                        shopName = shopName ?: "",
                        shopCode = normalizedShopCode,
                        loadingSequence = loadingSequence,
                        remark = remark
                    )
                    saveTruck(truckRequest)
                }
            } catch (e: Exception) {
                logger.error("Import Error (Warehouse Error): ${e.message}")
            }
        }
        logger.info("--------- End - Import Warehouse Excel -------")

        return "Import Excel success";
    }

    open fun findAllByShopId(shopId: Long): List<Truck> {
        return truckRepository.findByShopIdAndDeletedFalse(shopId)
    }

    @Transactional
    open fun updateTruckLaneByTruckLanceCode(request: SaveTruckLane): List<Truck> {

        val updateTruckLance = truckRepository.findById(request.id).orElseThrow()
            ?: throw IllegalArgumentException("Truck not found with truckLanceCode: $request.truckLanceCode")
        
        // Use remark from request (user input) - no auto-fill
        updateTruckLance.truckLanceCode = request.truckLanceCode
        updateTruckLance.loadingSequence = request.loadingSequence.toInt()
        updateTruckLance.districtReference = request.districtReference.toInt()
        updateTruckLance.departureTime = request.departureTime
        updateTruckLance.storeId = request.storeId
        // Only set remark if storeId is "4F", otherwise set to null
        updateTruckLance.remark = if (request.storeId == "4F") request.remark else null
        
        val savedTruck = truckRepository.save(updateTruckLance)
        return listOf(savedTruck)
    }


    @Transactional
    open fun deleteById(id: Long): String {
        val deleteTruck = truckRepository.findById(id).orElseThrow().apply {
            deleted = true
        }
        truckRepository.save(deleteTruck)
        return "Truck deleted successfully with id: $id"
    }

    @Transactional
    open fun createTruckInShop(request: SaveTruckRequest): Truck {
        // Always create a new truck, ignore id if provided
        val truck = Truck()
        val shop = shopRepository.findById(request.shopId).orElse(null)
        if (shop == null) {
            throw IllegalArgumentException("Shop not found with id: ${request.shopId}")
        }

        truck.apply {
            this.storeId = request.store_id
            this.truckLanceCode = request.truckLanceCode
            this.departureTime = request.departureTime
            this.shop = shop
            this.shopName = request.shopName
            this.shopCode = request.shopCode
            this.loadingSequence = request.loadingSequence
            this.districtReference = request.districtReference
            // Only set remark if store_id is "4F", otherwise set to null
            this.remark = if (request.store_id == "4F") request.remark else null
        }

        return truckRepository.save(truck)
    }

    open fun findAllUniqueTruckLanceCodeAndRemarkCombinations(): List<Truck> {
        return truckRepository.findAllUniqueTruckLanceCodeAndRemarkCombinations()
    }

    open fun findAllFromShopAndTruckByTruckLanceCodeAndDeletedFalse(truckLanceCode: String): List<ShopAndTruck> {
        return truckRepository.findAllFromShopAndTruckByTruckLanceCodeAndDeletedFalse(truckLanceCode)
    }

    open fun findAllByTruckLanceCodeAndDeletedFalse(truckLanceCode: String): List<Truck> {
        return truckRepository.findAllByTruckLanceCodeAndDeletedFalse(truckLanceCode)
    }

    open fun findAllUniqueShopNamesAndCodesFromTrucks(): List<Map<String, String>> {
        return truckRepository.findAllUniqueShopNamesAndCodesFromTrucks()
    }

    open fun findAllUniqueRemarksFromTrucks(): List<String> {
        return truckRepository.findAllUniqueRemarksFromTrucks()
    }

    open fun findAllUniqueShopCodesFromTrucks(): List<String> {
        return truckRepository.findAllUniqueShopCodesFromTrucks()
    }

    open fun findAllUniqueShopNamesFromTrucks(): List<String> {
        return truckRepository.findAllUniqueShopNamesFromTrucks()
    }

    @Transactional
    open fun updateTruckShopDetails(request: UpdateTruckShopDetailsRequest): Truck {
        val truck = truckRepository.findById(request.id).orElseThrow {
            IllegalArgumentException("Truck not found with id: ${request.id}")
        }
        
        // Update shop relationship if shopId is provided
        if (request.shopId != null) {
            val shop = shopRepository.findById(request.shopId).orElse(null)
            if (shop != null) {
                truck.shop = shop
            } else {
                throw IllegalArgumentException("Shop not found with id: ${request.shopId}")
            }
        }
        
        // Always use shopName and shopCode from request (from truck table), not from shop entity
        // This allows truck table to have different shop names/codes than shop table
        if (request.shopName != null) {
            truck.shopName = request.shopName
        }
        
        if (request.shopCode != null) {
            truck.shopCode = request.shopCode
        }
        
        // Update loadingSequence
        truck.loadingSequence = request.loadingSequence
        
        // Update remark - only set if storeId is "4F", otherwise set to null
        val storeIdStr = truck.storeId
        if (storeIdStr == "4F") {
            truck.remark = request.remark
        } else {
            truck.remark = null
        }
        
        return truckRepository.save(truck)
    }

    @Transactional
    open fun createTruckWithoutShop(request: CreateTruckWithoutShopRequest): Truck {
        // Create a new truck without a shop
        val truck = Truck()
        
        truck.apply {
            this.storeId = request.store_id
            this.truckLanceCode = request.truckLanceCode
            this.departureTime = request.departureTime
            this.shop = null
            this.shopName = "Unassign"
            this.shopCode = "Unassign"
            this.loadingSequence = request.loadingSequence
            this.districtReference = request.districtReference
            // Only set remark if store_id is "4F", otherwise set to null
            this.remark = if (request.store_id == "4F") request.remark else null
        }
        
        return truckRepository.save(truck)
    }

}