CREATE OR REPLACE FUNCTION rlarp.guidance_r1(doc jsonb)
RETURNS jsonb
LANGUAGE plv8
AS $function$

    function getAdjValue(number) {
      const data = [
        {f: 2.001, t: 1000,      snap: 3,  adj: 0   },
        {f: 1.001, t: 2,         snap: 2,  adj: 0   },
        {f: 0.1,   t: 1,         snap: 1,  adj: 0   },
        {f: 0,     t: 0.1,       snap: 0,  adj: 0   },
        {f: -1,    t: -0.00001,  snap: -1, adj: 0.05},
        {f: -2,    t: -0.999999, snap: -2, adj: 0.05},
        {f: -1000, t: -2.001,    snap: -3, adj: 0.10},
      ];
      const match = data.find(row => number >= row.f && number <= row.t);
      return match ? match.adj : null;
    }

    function lowestPrice(priceObject) {
        let Price = Infinity;
        let Reason = '';
        let Source = '';
        let Snapped = Infinity;

        //console.log(priceObject["targ"][0]);
        // Iterate over each property in the object
        for (let key in priceObject) {
            // Ignore markPrice unless targPrice is null
            if (key === "mark" && priceObject["targ"][0] !== null) {
                continue;
            }
            if (priceObject.hasOwnProperty(key)) {
                let [cprice, creason, csource, csnap] = priceObject[key];
                // Check if the current price is lower than the  found so far
                if (cprice && cprice < Price) {
                    Price = cprice;
                    Reason = creason;
                    Source = csource;
                    Snapped = csnap;
                }
            }
        }
        return {Reason, Price, Source, Snapped};
    }

    function ceiling(value, significance) {
        return Math.ceil(value / significance) * significance;
    }

    function r5(value) {
        return Number(value.toFixed(5));
    }

    function pp(value) {
        // Multiplies by 1000 and rounds to the nearest 2 decimals
        var result = Math.round(value * 1000 * 100) / 100;

        // Converts the number to a string with commas for thousand separators
        return result.toLocaleString('en-US', {minimumFractionDigits: 2, maximumFractionDigits: 2});
    }

    // --------------------extract incoming data------------------------------------------------------
    const targetPrice     = doc.pricing?.v1tp ?? doc.pricing?.v0tp;
    const priceBand       = doc.pricing?.v1stdv ?? doc.pricing?.v0stdv;
    const earlyCustPrice  = doc.hist?.cust?.early_price;
    const earlyCustSeason = doc.hist?.cust?.early_season;
    const earlyMarkPrice  = doc.hist?.market?.early_price;
    const earlyMarkSeason = doc.hist?.market?.early_season;
    const bridgePremium   = doc.pricing?.bridgePremium;
    const bridgedPrice    = Number((earlyCustPrice * (bridgePremium ?? 1.00)).toFixed(5));
    const altHist         = doc.hist?.cust?.ds;
    const iidx            = doc.pricing?.iidx;
    const curr            = doc.customer?.curr;
    const fxrate          = doc.customer?.fxrate ?? 1.0;
    const qty             = doc.inputs?.qty;
    const pltq            = doc.product?.pltq;
    const inflation       = iidx ? Math.max(...Object.keys(iidx).map(Number)) : null;
    const inflationFactor = iidx ? iidx[inflation] : 0;
    const list            = doc.pricing?.list && doc.product?.itemrel === "2" ? doc.pricing?.list : null;
    const listUSD         = list ? list * fxrate :null;
    const stlc            = doc.inputs.stlc;

    // ------------------calculate price adders------------------------------------------------------
    let   ltp             = stlc.includes("SDD") || stlc.includes("HZP") ? 0 : (qty < pltq ? 0.15 : null);
    let   anchor_sd       = priceBand ? ((bridgedPrice - targetPrice) / priceBand).toFixed(2) : 0
    let   optimization    = getAdjValue(anchor_sd);
    let   custAdder       = (ltp ?? 0) + optimization + inflationFactor;
    let   markAdder       = (ltp ?? 0) + inflationFactor;
    let   inflReason      = (inflationFactor ?? 0) !== 0 ? ` + ${(inflationFactor * 100).toFixed(1)}% infl`: "";
    let   ltpReason       = ltp                          ? ` + ${(ltp * 100).toFixed(1)}% ltp` : "";
    let   optReason       = (optimization ?? 0) !== 0    ? ` + ${(optimization * 100).toFixed(1)}% opt`: "";
    let   custAddReason   = `${inflReason}${ltpReason}${optReason}`;
    let   markAddReason   = `${inflReason}${ltpReason}`;

    // ------------------start building price options------------------------------------------------
    
    let snap = .0005;
    
    let   custPrice       = r5(bridgedPrice * (1 + custAdder));
    let   custSeason      = earlyCustSeason;
    let   custReason      = bridgePremium 
                                ? `${custSeason}  (similar ${altHist} price ${pp(earlyCustPrice)} x ${bridgePremium} = ${pp(bridgedPrice)})${custAddReason}`
                                : `${custSeason} price ${pp(bridgedPrice)}${custAddReason}`;
    let   markPrice       = r5(earlyMarkPrice * (1 + markAdder));
    let   markReason      = `${earlyMarkSeason} ASP ${pp(earlyMarkPrice)}${markAddReason}`;
    let   targPrice       = targetPrice ? r5(targetPrice * (1 + markAdder)) : null;
    let   targReason      = `Target price ${pp(targetPrice)}${markAddReason}`;
    let   listPrice       = listUSD;
    let   listReason      = fxrate === 1 ? `list ${pp(list)}` : `list ${pp(list)} CAD ${pp(listUSD)} USD`;

    let   prices = {
        cust: [custPrice, custReason, "cust", r5(ceiling(custPrice,snap))],
        mark: [markPrice, markReason, "mark", r5(ceiling(markPrice,snap))],
        targ: [targPrice, targReason, "targ", r5(ceiling(targPrice,snap))],
        list: [listPrice, listReason, "list", r5(ceiling(listPrice,snap))]
    }

    let finalPrice = lowestPrice(prices);
    let guidance = {
        prices
        ,finalPrice
        ,targetPrice
        ,listUSD
        ,ltp
        ,inflationFactor
        ,optimization
    }
    doc.guidance = guidance;
    return doc;

$function$;
