-- Owner-side, read-only presentation logic shared by the Agronomy frame and public read facade.
RealisticCropRotationPresentationService = RealisticCropRotationPresentationService or {}

local ADVICE_KEY = {
    CEREAL="rcr_advice_afterCereal", LEGUME="rcr_advice_afterLegume",
    OILSEED="rcr_advice_afterOilseed", ROOT="rcr_advice_afterRoot",
    VEGETABLE="rcr_advice_afterVegetable", FORAGE="rcr_advice_afterForage"
}

local FAMILY_MIN_INTERVAL = {
    CEREAL=2, OILSEED=3, LEGUME=3, VEGETABLE=3, ROOT=4
}

local function cropFamily(cropName)
    if cropName == nil or cropName == "" then return "UNKNOWN" end
    if RealisticCropRotation.isFallowCrop(cropName) then return "FALLOW" end
    local config = RealisticCropRotation.cropConfig
    return config ~= nil and config.families ~= nil
        and config.families[string.upper(tostring(cropName))] or "UNKNOWN"
end

local function cropDiseases(cropName)
    if cropName == nil or cropName == "" then return {} end
    local config = RealisticCropRotation.cropConfig
    return config ~= nil and config.diseases ~= nil
        and config.diseases[string.upper(tostring(cropName))] or {}
end

function RealisticCropRotationPresentationService.getCropDisplayName(cropName, i18n)
    if cropName == nil or cropName == "" then return "" end
    local name = string.upper(tostring(cropName))
    if RealisticCropRotation.isFallowCrop(name) then return i18n:getText("rcr_fallow") end
    local fill = g_fillTypeManager ~= nil
        and type(g_fillTypeManager.getFillTypeByName) == "function"
        and g_fillTypeManager:getFillTypeByName(name) or nil
    if fill ~= nil and fill.title ~= nil and fill.title ~= "" then return fill.title end
    local fruit = g_fruitTypeManager ~= nil
        and type(g_fruitTypeManager.getFruitTypeByName) == "function"
        and g_fruitTypeManager:getFruitTypeByName(name) or nil
    if fruit ~= nil and fruit.fillType ~= nil and fruit.fillType.title ~= nil
        and fruit.fillType.title ~= "" then
        return fruit.fillType.title
    end
    return name:sub(1, 1) .. string.lower(name:sub(2))
end

local function findPlanSlot(plan, cropName)
    local wanted = cropName ~= nil and string.upper(tostring(cropName)) or nil
    if wanted == nil or wanted == "" then return nil end
    for index = 1, 4 do
        if plan[index] == wanted then return index end
    end
    return nil
end

local function familyText(i18n, family)
    if family == "COVER" then return i18n:getText("rcr_cover_crop") end
    if family == "FALLOW" then return i18n:getText("rcr_fallow") end
    return i18n:getText("rcr_family_" .. string.lower(family))
end

function RealisticCropRotationPresentationService.evaluateRotationStep(cropA, cropB, i18n)
    if cropA == nil or cropA == "" or cropB == nil or cropB == ""
        or RealisticCropRotation.isFallowCrop(cropA)
        or RealisticCropRotation.isFallowCrop(cropB) then
        return nil
    end
    local familyA, familyB = cropFamily(cropA), cropFamily(cropB)
    local familyInterval = familyA == familyB and familyA ~= "UNKNOWN"
        and FAMILY_MIN_INTERVAL[familyA] or nil
    local baseline = familyInterval ~= nil and math.max(1, familyInterval) or 1
    local intervals = RealisticCropRotation.cropConfig ~= nil
        and RealisticCropRotation.cropConfig.diseaseIntervals or {}
    local worstGroup, worstInterval = nil, 0
    for group in pairs(cropDiseases(cropA)) do
        if cropDiseases(cropB)[group] then
            local interval = tonumber(intervals[group])
            if interval ~= nil and baseline < interval and interval > worstInterval then
                worstGroup, worstInterval = group, interval
            end
        end
    end
    if worstGroup ~= nil then
        local disease = RealisticCropRotation.disease
        return {
            kind="disease",
            label=disease ~= nil and disease:getDisplayName(worstGroup) or tostring(worstGroup),
            yearsRemaining=worstInterval - 1,
            minInterval=worstInterval
        }
    end
    if familyInterval ~= nil and familyInterval > 1 then
        return {
            kind="family", label=familyText(i18n, familyA),
            yearsRemaining=familyInterval - 1, minInterval=familyInterval
        }
    end
    return nil
end

function RealisticCropRotationPresentationService.getWorstPressureAdviceText(farmlandId, cropName, i18n)
    local disease, D = RealisticCropRotation.disease, RealisticCropRotationDisease
    local hosted = cropDiseases(cropName)
    if disease == nil or D == nil or next(hosted) == nil then return nil end
    local load = disease:getPressure(farmlandId)
    local bestBand, bestGroup, bestPressure = nil, nil, 0
    for group in pairs(hosted) do
        local pressure = tonumber(load[group]) or 0
        local band = pressure >= D.RISK_BAND_HIGH_THRESHOLD and "high"
            or (pressure >= D.RISK_BAND_MODERATE_THRESHOLD and "moderate" or nil)
        local better = band ~= nil and ((bestBand == nil)
            or (band == "high" and bestBand == "moderate")
            or (band == bestBand and pressure > bestPressure))
        if better then bestBand, bestGroup, bestPressure = band, group, pressure end
    end
    if bestGroup == nil then return nil end
    local key = "rcr_advice_pressure_" .. bestBand .. "_" .. string.lower(bestGroup)
    if type(i18n.hasText) ~= "function" or not i18n:hasText(key) then return nil end
    local text = i18n:getText(key)
    if bestGroup == "BCN" then
        text = text .. " " .. i18n:getText("rcr_advice_nematicide_duration")
    end
    return text
end

function RealisticCropRotationPresentationService.getWorstActiveDisease(farmlandId)
    local disease = RealisticCropRotation.disease
    if disease == nil then return nil, 0 end
    return disease:getWorstGroup(farmlandId)
end

local function soilAnalysisState(manager, farmlandId)
    local found = false
    local n = manager:getNitrogenLevel(farmlandId)
    if n == false then return false end
    found = found or n ~= nil
    local ph = manager:getPHLevel(farmlandId)
    if ph == false then return false end
    found = found or ph ~= nil
    return found and true or nil
end

function RealisticCropRotationPresentationService.buildAdvice(manager, farmlandId, cropName, i18n)
    i18n = i18n or g_i18n
    local family = cropFamily(cropName)
    local result = {
        visualState="Neutral", badgeSymbol="i", title="", text=nil,
        status="neutral", actionRequired=false, reasonCodes={}
    }
    local disease = RealisticCropRotation.disease
    local worstGroup, worstSeverity =
        RealisticCropRotationPresentationService.getWorstActiveDisease(farmlandId)
    if worstGroup ~= nil then
        result.visualState, result.badgeSymbol = "Danger", "!"
        result.status, result.actionRequired = "danger", true
        result.reasonCodes[1] = "activeDiseaseOutbreak"
        result.title = i18n:getText("rcr_advice_title_outbreak")
        result.text = string.format(i18n:getText("rcr_advice_outbreak"),
            disease:getDisplayName(worstGroup), math.floor(worstSeverity * 100 + 0.5),
            disease:getTreatmentName(worstGroup))
        if worstGroup == "BCN" then
            result.text = result.text .. " " .. i18n:getText("rcr_advice_nematicide_duration")
        end
    elseif family == "FALLOW" then
        result.visualState, result.badgeSymbol = "Ready", "OK"
        result.status, result.reasonCodes[1] = "ready", "currentFallow"
        result.title = i18n:getText("rcr_advice_title_fallow")
        result.text = i18n:getText("rcr_advice_fallow_current")
    else
        local plan = manager:getRotationPlan(farmlandId) or {"","","",""}
        local slot = family ~= "UNKNOWN" and findPlanSlot(plan, cropName) or nil
        local nextCrop = slot ~= nil and plan[(slot % 4) + 1] or nil
        nextCrop = nextCrop ~= nil and nextCrop ~= "" and nextCrop or nil
        if nextCrop ~= nil then
            local nextSlot = (slot % 4) + 1
            local year = i18n:getText("rcr_plan_year" .. nextSlot)
            if RealisticCropRotation.isFallowCrop(nextCrop) then
                result.visualState, result.badgeSymbol = "Ready", "OK"
                result.status, result.reasonCodes[1] = "ready", "plannedFallow"
                result.title = i18n:getText("rcr_advice_title_fallow")
                result.text = string.format(i18n:getText("rcr_advice_plan_next_fallow"), year)
            else
                local conflict = RealisticCropRotationPresentationService.evaluateRotationStep(
                    cropName, nextCrop, i18n)
                if conflict == nil then
                    result.visualState, result.badgeSymbol = "Ready", "OK"
                    result.status, result.reasonCodes[1] = "ready", "plannedStepSupported"
                    result.title = i18n:getText("rcr_advice_title_ready")
                    result.text = string.format(i18n:getText("rcr_advice_plan_next_ok"),
                        RealisticCropRotationPresentationService.getCropDisplayName(nextCrop, i18n), year)
                else
                    result.visualState, result.badgeSymbol = "Warning", "!"
                    result.status, result.actionRequired = "warning", true
                    result.reasonCodes[1] = conflict.kind == "disease"
                        and "plannedDiseaseConflict" or "plannedFamilyConflict"
                    result.title = i18n:getText("rcr_advice_title_warning")
                    local key = conflict.kind == "disease"
                        and "rcr_advice_plan_next_conflict_disease"
                        or "rcr_advice_plan_next_conflict_family"
                    result.text = string.format(i18n:getText(key),
                        RealisticCropRotationPresentationService.getCropDisplayName(nextCrop, i18n), year, conflict.label,
                        conflict.yearsRemaining, conflict.minInterval)
                end
            end
        else
            local key = ADVICE_KEY[family]
            result.reasonCodes[1] = key ~= nil and "familyAdvice" or "noCurrentCrop"
            result.text = i18n:getText(key or "rcr_advice_no_current_crop")
        end
        local pressure = RealisticCropRotationPresentationService.getWorstPressureAdviceText(
            farmlandId, cropName, i18n)
        if pressure ~= nil then result.text = result.text .. " " .. pressure end
    end
    if soilAnalysisState(manager, farmlandId) == false then
        result.text = result.text .. " " .. i18n:getText("rcr_advice_soil_analysis_required_body")
        result.reasonCodes[#result.reasonCodes + 1] = "soilAnalysisRequired"
    end
    result.cropFamily = family
    return result
end
