Uname:Linux server17213-10344.hostycare.online 5.14.0-687.38.1.el9_8.x86_64 #1 SMP PREEMPT_DYNAMIC Wed Aug 12 17:19:12 EDT 2026 x86_64

403WebShell
403Webshell
Server IP : 103.243.232.44  /  Your IP : 216.73.216.250
Web Server : LiteSpeed
System : Linux server17213-10344.hostycare.online 5.14.0-687.38.1.el9_8.x86_64 #1 SMP PREEMPT_DYNAMIC Wed Aug 12 17:19:12 EDT 2026 x86_64
User : iamakash ( 1400)
PHP Version : 8.1.34
Disable Function : NONE
MySQL : OFF  |  cURL : ON  |  WGET : ON  |  Perl : ON  |  Python : OFF  |  Sudo : OFF  |  Pkexec : OFF
Directory :  /home/iamakash/public_html/shaurya.meals28.com/

Upload File :
current_dir [ Writeable ] document_root [ Writeable ]

 

Command :


[ Back ]     

Current File : /home/iamakash/public_html/shaurya.meals28.com/calculate.php
<?php
require __DIR__ . '/vendor/autoload.php';
require __DIR__ . '/includes/auth.php';
requireLogin();

$pdo = getPDO();
$baseYear = '2011-12';

// Get available period range for dropdowns (2011-12 series drives the assessment date)
$range = $pdo->query(
    "SELECT MIN(period_year) AS min_y, MAX(period_year) AS max_y,
            MIN(period_year * 100 + period_month) AS min_ym,
            MAX(period_year * 100 + period_month) AS max_ym
       FROM monthly_indices
      WHERE index_value IS NOT NULL AND base_year = '2011-12'"
)->fetch();

$maxYear  = ($range && $range['max_y']  !== null) ? (int)$range['max_y']          : (int)date('Y');
$maxMonth = ($range && $range['max_ym'] !== null) ? (int)$range['max_ym'] % 100   : (int)date('m');
$minYear  = ($range && $range['min_y']  !== null) ? (int)$range['min_y']          : 2012;
$minMonth = ($range && $range['min_ym'] !== null) ? (int)$range['min_ym'] % 100   : 4;

// Series options (FY start month)
$seriesOptions = [
    1  => 'January – December',
    2  => 'February – January',
    3  => 'March – February',
    4  => 'April – March',
    5  => 'May – April',
    6  => 'June – May',
    7  => 'July – June',
    8  => 'August – July',
    9  => 'September – August',
    10 => 'October – September',
    11 => 'November – October',
    12 => 'December – November',
];

$monthNames = ['','Jan','Feb','Mar','Apr','May','Jun','Jul','Aug','Sep','Oct','Nov','Dec'];
$monthFull  = ['','January','February','March','April','May','June','July','August','September','October','November','December'];

function getQuarterLabel(int $monthOffset): string {
    return 'Q' . (intdiv($monthOffset, 3) + 1);
}
function getHalfLabel(int $monthOffset): string {
    return $monthOffset < 6 ? 'H1' : 'H2';
}

// ---------- CALCULATION ----------
$results         = null;
$assessLabel     = '';
$commNames       = [];
$errors          = [];
$frequency       = 'monthly';
$seriesStart     = 4;
$assessYear      = 0;
$assessMonth     = 0;
$assessPeriodNum = 1;
$showIndex          = false;
$showExtrapolation  = true;
$beginMonth         = 0;
$beginYear          = 0;
$extrapolatedValues = [];
$extrapMonths       = [];
$extrapLabel        = $monthNames[(int)date('n')] . ' ' . date('Y');
$showInterpolation  = false;
$interpolatedValues = [];
$interpMonths       = [];
$overlapPrefer      = '2011-12';
$hasOldData         = false;
$newToOld           = [];
$oldOverrides       = [];
$needsOldOverride   = [];
$overrideOptions    = [];

if ($_SERVER['REQUEST_METHOD'] === 'POST') {
    $codes             = array_filter(array_map('trim', explode(',', $_POST['codes'] ?? '')));
    $showIndex         = isset($_POST['show_index']);
    $showExtrapolation  = isset($_POST['show_extrapolation']);
    $showInterpolation  = isset($_POST['show_interpolation']);
    $beginMonth        = (int)($_POST['begin_month'] ?? 0);
    $beginYear         = (int)($_POST['begin_year']  ?? 0);
    $frequency       = $_POST['frequency']    ?? 'monthly';
    $seriesStart     = (int)($_POST['series_start'] ?? 4);
    $assessYear      = (int)($_POST['assess_year']  ?? 0);
    $assessMonth     = (int)($_POST['assess_month'] ?? 0);
    $overlapPrefer   = in_array($_POST['overlap_prefer'] ?? '', ['2004-05','2011-12'], true)
                       ? $_POST['overlap_prefer'] : '2011-12';
    foreach ($_POST['old_override'] ?? [] as $nc => $oc) {
        $nc = trim($nc); $oc = trim($oc);
        if ($nc !== '' && $oc !== '') $oldOverrides[$nc] = $oc;
    }
    $assessPeriodNum = $frequency === 'monthly'
        ? $assessMonth
        : (int)($_POST['assess_period'] ?? 1);

    if (!$codes) {
        $errors[] = 'Select at least one commodity.';
    }
    if ($assessYear < ($showInterpolation ? 1920 : $minYear)) {
        $errors[] = 'Select a valid assessment year.';
    }
    if ($frequency === 'monthly' && ($assessMonth < 1 || $assessMonth > 12)) {
        $errors[] = 'Select a valid assessment month.';
    }
    if ($showExtrapolation) {
        if ($beginMonth < 1 || $beginMonth > 12 || $beginYear < 2000) {
            $errors[] = 'Select a valid beginning period for extrapolation.';
        } elseif ($beginYear * 100 + $beginMonth >= $maxYear * 100 + $maxMonth) {
            $errors[] = 'Beginning period must be earlier than the latest available period (' . $monthNames[$maxMonth] . ' ' . $maxYear . ').';
        }
    }

    if (!$errors) {
        // Determine assessment period label and months to average
        if ($frequency === 'monthly') {
            $assessLabel        = $monthNames[$assessPeriodNum] . ' ' . $assessYear;
            $assessPeriodMonths = [['year' => $assessYear, 'month' => $assessPeriodNum]];
        } elseif ($frequency === 'quarterly') {
            $startOff    = ($assessPeriodNum - 1) * 3;
            $assessLabel = "Q{$assessPeriodNum} - {$assessYear}";
            $assessPeriodMonths = [];
            for ($i = 0; $i < 3; $i++) {
                $m = (($seriesStart - 1 + $startOff + $i) % 12) + 1;
                $y = $m >= $seriesStart ? $assessYear : $assessYear + 1;
                $assessPeriodMonths[] = ['year' => $y, 'month' => $m];
            }
        } elseif ($frequency === 'half-yearly') {
            $startOff    = ($assessPeriodNum - 1) * 6;
            $assessLabel = "H{$assessPeriodNum} - {$assessYear}";
            $assessPeriodMonths = [];
            for ($i = 0; $i < 6; $i++) {
                $m = (($seriesStart - 1 + $startOff + $i) % 12) + 1;
                $y = $m >= $seriesStart ? $assessYear : $assessYear + 1;
                $assessPeriodMonths[] = ['year' => $y, 'month' => $m];
            }
        } else { // annually
            $assessLabel = (string)$assessYear;
            $assessPeriodMonths = [];
            for ($i = 0; $i < 12; $i++) {
                $m = (($seriesStart - 1 + $i) % 12) + 1;
                $y = $m >= $seriesStart ? $assessYear : $assessYear + 1;
                $assessPeriodMonths[] = ['year' => $y, 'month' => $m];
            }
        }

        $assessLastYm = 0;
        foreach ($assessPeriodMonths as $apm) {
            $assessLastYm = max($assessLastYm, $apm['year'] * 100 + $apm['month']);
        }
        if ($showExtrapolation && $assessLastYm <= $maxYear * 100 + $maxMonth) {
            $showExtrapolation = false;
        }
        // Fetch assessment-period indices (always from 2011-12 series)
        $placeholders = implode(',', array_fill(0, count($codes), '?'));
        $assessRawPerYm = [];
        if ($frequency === 'monthly') {
            $stmt = $pdo->prepare(
                "SELECT comm_code, index_value FROM monthly_indices
                  WHERE comm_code IN ($placeholders) AND base_year = ?
                    AND period_year = ? AND period_month = ?"
            );
            $stmt->execute(array_merge($codes, [$baseYear, $assessYear, $assessPeriodNum]));
            $assessIndices = [];
            foreach ($stmt->fetchAll() as $r) {
                $assessIndices[$r['comm_code']] = (float)$r['index_value'];
            }
        } else {
            $mConds  = implode(' OR ', array_fill(0, count($assessPeriodMonths), '(period_year=? AND period_month=?)'));
            $mParams = [];
            foreach ($assessPeriodMonths as $pm) {
                $mParams[] = $pm['year'];
                $mParams[] = $pm['month'];
            }
            $stmt = $pdo->prepare(
                "SELECT comm_code, period_year, period_month, index_value FROM monthly_indices
                  WHERE comm_code IN ($placeholders) AND base_year = ?
                    AND ($mConds) AND index_value IS NOT NULL"
            );
            $stmt->execute(array_merge($codes, [$baseYear], $mParams));
            $assessRaw = [];
            foreach ($stmt->fetchAll() as $r) {
                $assessRaw[$r['comm_code']][] = (float)$r['index_value'];
                $assessRawPerYm[$r['comm_code']][(int)$r['period_year'] * 100 + (int)$r['period_month']] = (float)$r['index_value'];
            }
            $assessIndices = [];
            foreach ($assessRaw as $c => $vals) {
                $assessIndices[$c] = count($vals) ? round(array_sum($vals) / count($vals), 2) : null;
            }
        }

        // Extrapolation Part A
        if ($showExtrapolation) {
            $ey = $maxYear; $em_i = $maxMonth;
            while (true) {
                $em_i++; if ($em_i > 12) { $em_i = 1; $ey++; }
                if ($ey * 100 + $em_i > $assessLastYm) break;
                $extrapMonths[] = ['year' => $ey, 'month' => $em_i];
            }

            if ($extrapMonths) {
                $stmt = $pdo->prepare(
                    "SELECT comm_code, index_value FROM monthly_indices
                      WHERE comm_code IN ($placeholders) AND base_year = ?
                        AND period_year = ? AND period_month = ?"
                );
                $stmt->execute(array_merge($codes, [$baseYear, $beginYear, $beginMonth]));
                $beginIndices = [];
                foreach ($stmt->fetchAll() as $r) {
                    $beginIndices[$r['comm_code']] = (float)$r['index_value'];
                }

                $stmt = $pdo->prepare(
                    "SELECT comm_code, index_value FROM monthly_indices
                      WHERE comm_code IN ($placeholders) AND base_year = ?
                        AND period_year = ? AND period_month = ?"
                );
                $stmt->execute(array_merge($codes, [$baseYear, $maxYear, $maxMonth]));
                $endIndices = [];
                foreach ($stmt->fetchAll() as $r) {
                    $endIndices[$r['comm_code']] = (float)$r['index_value'];
                }

                $beginPeriodYrs = $beginYear + $beginMonth / 12;
                $endPeriodYrs   = $maxYear   + $maxMonth   / 12;
                $periodDiff     = $endPeriodYrs - $beginPeriodYrs;
                $maxDbYm        = $maxYear * 100 + $maxMonth;

                foreach ($codes as $code) {
                    $bIdx = $beginIndices[$code] ?? null;
                    $eIdx = $endIndices[$code]   ?? null;
                    if ($bIdx !== null && $eIdx !== null && $bIdx > 0 && $periodDiff > 0) {
                        $cagr = pow($eIdx / $bIdx, 1 / $periodDiff) - 1;
                        foreach ($extrapMonths as $ep) {
                            $epYrs = $ep['year'] + $ep['month'] / 12;
                            $extrapolatedValues[$code][$ep['year'] * 100 + $ep['month']] =
                                round($eIdx * pow(1 + $cagr, $epYrs - $endPeriodYrs), 2);
                        }
                    }
                }

                foreach ($codes as $code) {
                    if (empty($extrapolatedValues[$code])) continue;
                    if ($frequency === 'monthly') {
                        $ym = $assessYear * 100 + $assessPeriodNum;
                        $assessIndices[$code] = $extrapolatedValues[$code][$ym] ?? null;
                    } else {
                        $vals = [];
                        foreach ($assessPeriodMonths as $apm) {
                            $apmYm = $apm['year'] * 100 + $apm['month'];
                            $v = $apmYm <= $maxDbYm
                                ? ($assessRawPerYm[$code][$apmYm] ?? null)
                                : ($extrapolatedValues[$code][$apmYm] ?? null);
                            if ($v !== null) $vals[] = $v;
                        }
                        $assessIndices[$code] = count($vals) ? round(array_sum($vals) / count($vals), 2) : null;
                    }
                }
            }
        }

        // Interpolation Part A: back-project to Jan 1920 using full-series CAGR
        if ($showInterpolation) {
            $iy = $minYear; $im = $minMonth;
            while (true) {
                $im--; if ($im < 1) { $im = 12; $iy--; }
                if ($iy < 1920) break;
                $interpMonths[] = ['year' => $iy, 'month' => $im];
            }

            if ($interpMonths) {
                $stmt = $pdo->prepare(
                    "SELECT comm_code, index_value FROM monthly_indices
                      WHERE comm_code IN ($placeholders) AND base_year = ?
                        AND period_year = ? AND period_month = ?"
                );
                $stmt->execute(array_merge($codes, [$baseYear, $minYear, $minMonth]));
                $minIndices = [];
                foreach ($stmt->fetchAll() as $r) {
                    $minIndices[$r['comm_code']] = (float)$r['index_value'];
                }

                $stmt->execute(array_merge($codes, [$baseYear, $maxYear, $maxMonth]));
                $maxIndicesI = [];
                foreach ($stmt->fetchAll() as $r) {
                    $maxIndicesI[$r['comm_code']] = (float)$r['index_value'];
                }

                $minPeriodYrs = $minYear + $minMonth / 12;
                $maxPeriodYrs = $maxYear + $maxMonth / 12;
                $periodDiffI  = $maxPeriodYrs - $minPeriodYrs;
                $minDbYm      = $minYear * 100 + $minMonth;

                foreach ($codes as $code) {
                    $mIdx = $minIndices[$code]  ?? null;
                    $xIdx = $maxIndicesI[$code] ?? null;
                    if ($mIdx !== null && $xIdx !== null && $mIdx > 0 && $periodDiffI > 0) {
                        $cagr = pow($xIdx / $mIdx, 1 / $periodDiffI) - 1;
                        foreach ($interpMonths as $ep) {
                            $epYrs = $ep['year'] + $ep['month'] / 12;
                            $interpolatedValues[$code][$ep['year'] * 100 + $ep['month']] =
                                round($mIdx * pow(1 + $cagr, $epYrs - $minPeriodYrs), 2);
                        }
                    }
                }

                foreach ($codes as $code) {
                    if (empty($interpolatedValues[$code])) continue;
                    if ($frequency === 'monthly') {
                        $ym = $assessYear * 100 + $assessPeriodNum;
                        if ($ym < $minDbYm)
                            $assessIndices[$code] = $interpolatedValues[$code][$ym] ?? null;
                    } else {
                        $vals = [];
                        foreach ($assessPeriodMonths as $apm) {
                            $apmYm = $apm['year'] * 100 + $apm['month'];
                            $v = $apmYm >= $minDbYm
                                ? ($assessRawPerYm[$code][$apmYm] ?? null)
                                : ($interpolatedValues[$code][$apmYm] ?? null);
                            if ($v !== null) $vals[] = $v;
                        }
                        if (count($vals)) $assessIndices[$code] = round(array_sum($vals) / count($vals), 2);
                    }
                }
            }
        }

        // Fetch commodity details (prefer 2011-12, fall back to 2004-05)
        $stmt = $pdo->prepare(
            "SELECT comm_code, comm_name, level, display_name FROM commodities
              WHERE comm_code IN ($placeholders) AND base_year = '2011-12'"
        );
        $stmt->execute($codes);
        foreach ($stmt->fetchAll() as $r) {
            $commNames[$r['comm_code']] = $r;
        }
        $missing = array_diff($codes, array_keys($commNames));
        if ($missing) {
            $mp   = implode(',', array_fill(0, count($missing), '?'));
            $stmt = $pdo->prepare(
                "SELECT comm_code, comm_name, level, display_name FROM commodities
                  WHERE comm_code IN ($mp) AND base_year = '2004-05'"
            );
            $stmt->execute(array_values($missing));
            foreach ($stmt->fetchAll() as $r) {
                $commNames[$r['comm_code']] = $r;
            }
        }

        // Fetch 2011-12 series monthly data
        $stmt = $pdo->prepare(
            "SELECT comm_code, period_year, period_month, index_value, is_provisional
               FROM monthly_indices
              WHERE comm_code IN ($placeholders) AND base_year = '2011-12'
                AND index_value IS NOT NULL"
        );
        $stmt->execute($codes);
        $newRaw = []; // [code][ym] = [value, provisional]
        foreach ($stmt->fetchAll() as $r) {
            $ym = (int)$r['period_year'] * 100 + (int)$r['period_month'];
            $newRaw[$r['comm_code']][$ym] = [(float)$r['index_value'], (int)$r['is_provisional']];
        }

        // Resolve 2004-05 codes via crosswalk
        $cwStmt = $pdo->prepare(
            "SELECT new_code, old_code FROM comm_crosswalk WHERE new_code IN ($placeholders)"
        );
        $cwStmt->execute($codes);
        $newToOld = [];
        foreach ($cwStmt->fetchAll() as $r) {
            $newToOld[$r['new_code']] = $r['old_code'];
        }

        // Detect commodities where no crosswalk entry exists but the same code in 2004-05
        // belongs to a different commodity (code collision) or has no 2004-05 entry at all.
        $fallbackCodes = array_values(array_filter($codes, fn($c) => !isset($newToOld[$c]) && !isset($oldOverrides[$c])));
        if ($fallbackCodes) {
            $fbPh = implode(',', array_fill(0, count($fallbackCodes), '?'));
            $fbStmt = $pdo->prepare(
                "SELECT comm_code, comm_name FROM commodities WHERE comm_code IN ($fbPh) AND base_year = '2004-05'"
            );
            $fbStmt->execute($fallbackCodes);
            $old04Names = [];
            foreach ($fbStmt->fetchAll() as $r) $old04Names[$r['comm_code']] = $r['comm_name'];
            foreach ($fallbackCodes as $c) {
                $newName  = $commNames[$c]['comm_name'] ?? null;
                $old04Name = $old04Names[$c] ?? null;
                if ($newName === null || $old04Name !== $newName) {
                    $needsOldOverride[$c] = true;
                }
            }
        }

        // Fetch dropdown options: 2004-05 children of the same parent group, with data only
        foreach ($needsOldOverride as $c => $_) {
            if (isset($oldOverrides[$c])) continue;
            $parStmt = $pdo->prepare(
                "SELECT parent_code FROM commodities WHERE comm_code = ? AND base_year = '2011-12'"
            );
            $parStmt->execute([$c]);
            $parentCode = $parStmt->fetchColumn();
            if ($parentCode) {
                $optStmt = $pdo->prepare(
                    "SELECT c.comm_code, c.comm_name
                       FROM commodities c
                      WHERE (c.comm_code = ? OR c.parent_code = ?) AND c.base_year = '2004-05'
                        AND EXISTS (
                            SELECT 1 FROM monthly_indices m
                             WHERE m.comm_code = c.comm_code AND m.base_year = '2004-05'
                        )
                      ORDER BY c.comm_name"
                );
                $optStmt->execute([$parentCode, $parentCode]);
            } else {
                // Fallback: same level
                $lvl = (int)($commNames[$c]['level'] ?? 6);
                $optStmt = $pdo->prepare(
                    "SELECT comm_code, comm_name FROM commodities
                      WHERE base_year = '2004-05' AND level = ?
                      ORDER BY comm_name"
                );
                $optStmt->execute([$lvl]);
            }
            $overrideOptions[$c] = $optStmt->fetchAll();
        }

        // Build effective old-code map: crosswalk > manual override > same-code (validated safe)
        $effectiveOldMap = [];
        foreach ($codes as $c) {
            if (isset($oldOverrides[$c]))        $effectiveOldMap[$c] = $oldOverrides[$c];
            elseif (isset($newToOld[$c]))        $effectiveOldMap[$c] = $newToOld[$c];
            elseif (!isset($needsOldOverride[$c])) $effectiveOldMap[$c] = $c;
            // else: collision/missing and no override yet — skip, no old data fetched
        }

        $oldFetchCodes = array_values($effectiveOldMap);
        $oldToNew      = [];
        foreach ($effectiveOldMap as $nc => $oc) $oldToNew[$oc] = $nc;

        // Fetch 2004-05 series monthly data
        $oldRaw = [];
        if ($oldFetchCodes) {
            $oldPh = implode(',', array_fill(0, count($oldFetchCodes), '?'));
            $stmt  = $pdo->prepare(
                "SELECT comm_code, period_year, period_month, index_value, is_provisional
                   FROM monthly_indices
                  WHERE comm_code IN ($oldPh) AND base_year = '2004-05'
                    AND index_value IS NOT NULL"
            );
            $stmt->execute($oldFetchCodes);
            foreach ($stmt->fetchAll() as $r) {
                $ym  = (int)$r['period_year'] * 100 + (int)$r['period_month'];
                $key = $oldToNew[$r['comm_code']] ?? $r['comm_code'];
                $oldRaw[$key][$ym] = [(float)$r['index_value'], (int)$r['is_provisional']];
            }
        }

        // Linking factor from first common period (both series have data)
        // Monthly: first common month's index values
        // Q/H/Y: average of both series over the period that contains the first common month
        $links = [];
        foreach ($codes as $code) {
            $commonYms = array_intersect(array_keys($newRaw[$code] ?? []), array_keys($oldRaw[$code] ?? []));
            if (!$commonYms) continue;
            sort($commonYms);
            $minYm = $commonYms[0];
            $fcY   = intdiv($minYm, 100);
            $fcM   = $minYm % 100;

            if ($frequency === 'monthly') {
                $links[$code] = [
                    $oldRaw[$code][$minYm][0],
                    $newRaw[$code][$minYm][0],
                    $monthNames[$fcM] . ' ' . $fcY,  // trans_label
                ];
            } else {
                $offset = ($fcM - $seriesStart + 12) % 12;
                $fy     = $fcM >= $seriesStart ? $fcY : $fcY - 1;
                if ($frequency === 'quarterly') {
                    $pNum      = intdiv($offset, 3) + 1;
                    $pStartOff = ($pNum - 1) * 3;
                    $periodLen = 3;
                    $transLabel = "Q{$pNum} {$fy}";
                } elseif ($frequency === 'half-yearly') {
                    $pNum      = $offset < 6 ? 1 : 2;
                    $pStartOff = ($pNum - 1) * 6;
                    $periodLen = 6;
                    $transLabel = "H{$pNum} {$fy}";
                } else {
                    $pNum = 1; $pStartOff = 0; $periodLen = 12;
                    $transLabel = (string)$fy;
                }
                $oldVals = []; $newVals = [];
                for ($gi = 0; $gi < $periodLen; $gi++) {
                    $gm  = (($seriesStart - 1 + $pStartOff + $gi) % 12) + 1;
                    $gy  = $gm >= $seriesStart ? $fy : $fy + 1;
                    $gym = $gy * 100 + $gm;
                    if (isset($oldRaw[$code][$gym]) && isset($newRaw[$code][$gym])) {
                        $oldVals[] = $oldRaw[$code][$gym][0];
                        $newVals[] = $newRaw[$code][$gym][0];
                    }
                }
                if ($oldVals && $newVals) {
                    $links[$code] = [
                        round(array_sum($oldVals) / count($oldVals), 4),
                        round(array_sum($newVals) / count($newVals), 4),
                        $transLabel,
                    ];
                }
            }
        }

        // Build unified allData: merge both series, compute composite per period
        // Composite is always expressed in 2011-12 scale.
        // Overlap (Apr 2012–Mar 2017): use $overlapPrefer to decide which drives composite.
        $allData    = [];
        $hasOldData = false;
        foreach ($codes as $code) {
            $allYms = array_unique(array_merge(
                array_keys($newRaw[$code] ?? []),
                array_keys($oldRaw[$code] ?? [])
            ));
            sort($allYms);
            $link = $links[$code] ?? null; // [old_avg, new_avg]

            foreach ($allYms as $ym) {
                $yr  = intdiv($ym, 100);
                $mo  = $ym % 100;
                $ne  = $newRaw[$code][$ym] ?? null; // [value, prov]
                $oe  = $oldRaw[$code][$ym] ?? null;
                $nv  = $ne !== null ? $ne[0] : null;
                $ov  = $oe !== null ? $oe[0] : null;

                // Composite expressed in the selected series' scale.
                // 2004-05 preferred: old periods → old directly; new-only → new × (old_avg/new_avg)
                // 2011-12 preferred: new periods → new directly; old-only → old × (new_avg/old_avg)
                if ($overlapPrefer === '2004-05') {
                    // Primary: new × (old_avg/new_avg); fallback to old when no new data
                    if ($nv !== null) {
                        $composite = ($link && $link[1] > 0)
                            ? round($nv * ($link[0] / $link[1]), 2)
                            : $nv;
                    } elseif ($ov !== null) {
                        $composite = $ov;
                    } else {
                        $composite = null;
                    }
                } else {
                    // Prefer 2011-12 series where available; fall back to old as-is
                    if ($nv !== null) {
                        $composite = $nv;
                    } elseif ($ov !== null) {
                        $composite = $ov;
                    } else {
                        $composite = null;
                    }
                }

                if ($ov !== null) $hasOldData = true;

                $allData[$code][] = [
                    'comm_code'      => $code,
                    'period_year'    => $yr,
                    'period_month'   => $mo,
                    'old_index'      => $ov,
                    'new_index'      => $nv,
                    'composite'      => $composite,
                    'index_value'    => $composite, // used by grouped logic
                    'is_provisional' => max($ne ? $ne[1] : 0, $oe ? $oe[1] : 0),
                ];
            }
        }

        // Convert assessment index to match the composite's scale.
        // 2004-05 preferred: composite in old scale; assessment (2011-12) → old via × (old_avg/new_avg)
        // 2011-12 preferred: composite in new scale; assessment already in 2011-12, no conversion.
        $assessComposites = [];
        foreach ($codes as $code) {
            $ai = $assessIndices[$code] ?? null;
            $lk = $links[$code] ?? null;
            if ($overlapPrefer === '2004-05') {
                $assessComposites[$code] = ($ai !== null && $lk && $lk[1] > 0)
                    ? round($ai * ($lk[0] / $lk[1]), 2)
                    : $ai;
            } else {
                $assessComposites[$code] = $ai;
            }
        }

        $results = [];

        foreach ($codes as $code) {
            $assessIdx = $assessComposites[$code] ?? null;
            if ($assessIdx === null) continue;
            $rows = $allData[$code] ?? [];

            if ($frequency === 'monthly') {
                foreach ($rows as $r) {
                    $composite = $r['composite'];
                    if ($composite === null) continue;
                    $factor = $composite > 0 ? round($assessIdx / $composite, 4) : null;
                    $period = sprintf('INDX%02d%04d', (int)$r['period_month'], (int)$r['period_year']);
                    $periodLabel = $monthNames[(int)$r['period_month']] . ' ' . $r['period_year'];
                    $results[] = [
                        'comm_code'    => $code,
                        'period'       => $period,
                        'period_label' => $periodLabel,
                        'period_sort'  => (int)$r['period_year'] * 100 + (int)$r['period_month'],
                        'old_index'    => $r['old_index'],
                        'new_index'    => $r['new_index'],
                        'composite'    => $composite,
                        'index_value'  => $composite,
                        'assess_index' => $assessIdx,
                        'factor'       => $factor,
                        'provisional'  => (int)$r['is_provisional'],
                    ];
                }
            } else {
                // Group months into periods based on series start and frequency
                $grouped = [];
                foreach ($rows as $r) {
                    $y = (int)$r['period_year'];
                    $m = (int)$r['period_month'];
                    $offset = ($m - $seriesStart + 12) % 12;
                    $fy = $m >= $seriesStart ? $y : $y - 1;

                    if ($frequency === 'quarterly') {
                        $qNum = intdiv($offset, 3) + 1;
                        $key = "Q{$qNum} - {$fy}";
                        $sort = $fy * 10 + $qNum;
                        $grouped[$key]['period_num'] = $qNum;
                    } elseif ($frequency === 'half-yearly') {
                        $hNum = $offset < 6 ? 1 : 2;
                        $key = "H{$hNum} - {$fy}";
                        $sort = $fy * 10 + $hNum;
                        $grouped[$key]['period_num'] = $hNum;
                    } else {
                        $key = (string)$fy;
                        $sort = $fy;
                        $grouped[$key]['period_num'] = 1;
                    }

                    if ($r['composite'] !== null) $grouped[$key]['values'][]     = $r['composite'];
                    if ($r['old_index'] !== null) $grouped[$key]['old_values'][] = $r['old_index'];
                    if ($r['new_index'] !== null) $grouped[$key]['new_values'][] = $r['new_index'];

                    $grouped[$key]['sort'] = $sort;
                    $grouped[$key]['fy']   = $fy;
                    $grouped[$key]['prov'] = max($grouped[$key]['prov'] ?? 0, (int)$r['is_provisional']);
                }

                // Fill extrapolated months into partially-covered periods
                if ($showExtrapolation && !empty($extrapolatedValues[$code])) {
                    $maxDbYmLocal = $maxYear * 100 + $maxMonth;
                    $periodLen    = $frequency === 'quarterly' ? 3 : ($frequency === 'half-yearly' ? 6 : 12);
                    $startStep    = $frequency === 'quarterly' ? 3 : ($frequency === 'half-yearly' ? 6 : 0);
                    $lkQ = $links[$code] ?? null;
                    foreach ($grouped as $gKey => &$gData) {
                        $gFy       = $gData['fy'];
                        $gPNum     = $gData['period_num'];
                        $gStartOff = ($gPNum - 1) * $startStep;
                        for ($gi = 0; $gi < $periodLen; $gi++) {
                            $gm  = (($seriesStart - 1 + $gStartOff + $gi) % 12) + 1;
                            $gy  = $gm >= $seriesStart ? $gFy : $gFy + 1;
                            $gym = $gy * 100 + $gm;
                            if ($gym <= $maxDbYmLocal) continue;
                            $ev = $extrapolatedValues[$code][$gym] ?? null;
                            if ($ev !== null) {
                                $evComp = ($overlapPrefer === '2004-05' && $lkQ && $lkQ[1] > 0)
                                    ? round($ev * ($lkQ[0] / $lkQ[1]), 2)
                                    : $ev;
                                $gData['values'][]     = $evComp;
                                $gData['new_values'][] = $ev;
                                $gData['extrapolated'] = true;
                            }
                        }
                    }
                    unset($gData);

                    $aAssessSort = $frequency === 'annually'
                        ? $assessYear
                        : $assessYear * 10 + $assessPeriodNum;
                    foreach ($extrapMonths as $ep) {
                        $em     = $ep['month']; $ey = $ep['year'];
                        $offset = ($em - $seriesStart + 12) % 12;
                        $pFy    = $em >= $seriesStart ? $ey : $ey - 1;
                        if ($frequency === 'quarterly') {
                            $pNum  = intdiv($offset, 3) + 1;
                            $pKey  = "Q{$pNum} - {$pFy}";
                            $pSort = $pFy * 10 + $pNum;
                        } elseif ($frequency === 'half-yearly') {
                            $pNum  = $offset < 6 ? 1 : 2;
                            $pKey  = "H{$pNum} - {$pFy}";
                            $pSort = $pFy * 10 + $pNum;
                        } else {
                            $pNum  = 1;
                            $pKey  = (string)$pFy;
                            $pSort = $pFy;
                        }
                        if (isset($grouped[$pKey]) || $pSort > $aAssessSort) continue;
                        $pStartOff = ($pNum - 1) * $startStep;
                        $pVals = $pNewVals = [];
                        for ($gi = 0; $gi < $periodLen; $gi++) {
                            $gm  = (($seriesStart - 1 + $pStartOff + $gi) % 12) + 1;
                            $gy  = $gm >= $seriesStart ? $pFy : $pFy + 1;
                            $gym = $gy * 100 + $gm;
                            $ev  = $gym <= $maxDbYmLocal
                                ? ($assessRawPerYm[$code][$gym] ?? null)
                                : ($extrapolatedValues[$code][$gym] ?? null);
                            if ($ev !== null) {
                                $evComp = ($overlapPrefer === '2004-05' && $lkQ && $lkQ[1] > 0)
                                    ? round($ev * ($lkQ[0] / $lkQ[1]), 2)
                                    : $ev;
                                $pVals[]    = $evComp;
                                $pNewVals[] = $ev;
                            }
                        }
                        if ($pVals) {
                            $grouped[$pKey] = [
                                'values'      => $pVals,
                                'new_values'  => $pNewVals,
                                'old_values'  => [],
                                'sort'        => $pSort,
                                'fy'          => $pFy,
                                'period_num'  => $pNum,
                                'prov'        => 0,
                                'extrapolated'=> true,
                            ];
                        }
                    }
                }

                // Fill interpolated months into partially-covered periods (Q/H/A)
                if ($showInterpolation && !empty($interpolatedValues[$code])) {
                    $minDbYmLocal = $minYear * 100 + $minMonth;
                    $periodLen    = $frequency === 'quarterly' ? 3 : ($frequency === 'half-yearly' ? 6 : 12);
                    $startStep    = $frequency === 'quarterly' ? 3 : ($frequency === 'half-yearly' ? 6 : 0);
                    $lkI = $links[$code] ?? null;
                    foreach ($grouped as $gKey => &$gData) {
                        $gFy       = $gData['fy'];
                        $gPNum     = $gData['period_num'];
                        $gStartOff = ($gPNum - 1) * $startStep;
                        for ($gi = 0; $gi < $periodLen; $gi++) {
                            $gm  = (($seriesStart - 1 + $gStartOff + $gi) % 12) + 1;
                            $gy  = $gm >= $seriesStart ? $gFy : $gFy + 1;
                            $gym = $gy * 100 + $gm;
                            if ($gym >= $minDbYmLocal) continue;
                            $iv = $interpolatedValues[$code][$gym] ?? null;
                            if ($iv !== null) {
                                $ivComp = ($overlapPrefer === '2004-05' && $lkI && $lkI[1] > 0)
                                    ? round($iv * ($lkI[0] / $lkI[1]), 2) : $iv;
                                $gData['values'][]        = $ivComp;
                                $gData['new_values'][]    = $iv;
                                $gData['interpolated']    = true;
                            }
                        }
                    }
                    unset($gData);

                    $aAssessSortI = $frequency === 'annually'
                        ? $assessYear : $assessYear * 10 + $assessPeriodNum;
                    foreach ($interpMonths as $ep) {
                        $em     = $ep['month']; $ey = $ep['year'];
                        $offset = ($em - $seriesStart + 12) % 12;
                        $pFy    = $em >= $seriesStart ? $ey : $ey - 1;
                        if ($frequency === 'quarterly') {
                            $pNum  = intdiv($offset, 3) + 1;
                            $pKey  = "Q{$pNum} - {$pFy}";
                            $pSort = $pFy * 10 + $pNum;
                        } elseif ($frequency === 'half-yearly') {
                            $pNum  = $offset < 6 ? 1 : 2;
                            $pKey  = "H{$pNum} - {$pFy}";
                            $pSort = $pFy * 10 + $pNum;
                        } else {
                            $pNum  = 1;
                            $pKey  = (string)$pFy;
                            $pSort = $pFy;
                        }
                        if (isset($grouped[$pKey]) || $pSort < $aAssessSortI) continue;
                        $pStartOff = ($pNum - 1) * $startStep;
                        $pVals = $pNewVals = [];
                        for ($gi = 0; $gi < $periodLen; $gi++) {
                            $gm  = (($seriesStart - 1 + $pStartOff + $gi) % 12) + 1;
                            $gy  = $gm >= $seriesStart ? $pFy : $pFy + 1;
                            $gym = $gy * 100 + $gm;
                            $iv  = $gym < $minDbYmLocal
                                ? ($interpolatedValues[$code][$gym] ?? null)
                                : ($assessRawPerYm[$code][$gym] ?? null);
                            if ($iv !== null) {
                                $ivComp = ($overlapPrefer === '2004-05' && $lkI && $lkI[1] > 0)
                                    ? round($iv * ($lkI[0] / $lkI[1]), 2) : $iv;
                                $pVals[]    = $ivComp;
                                $pNewVals[] = $iv;
                            }
                        }
                        if ($pVals) {
                            $grouped[$pKey] = [
                                'values'       => $pVals,
                                'new_values'   => $pNewVals,
                                'old_values'   => [],
                                'sort'         => $pSort,
                                'fy'           => $pFy,
                                'period_num'   => $pNum,
                                'prov'         => 0,
                                'interpolated' => true,
                            ];
                        }
                    }
                }

                foreach ($grouped as $label => $g) {
                    $avg    = !empty($g['values'])     ? round(array_sum($g['values'])     / count($g['values']), 2)     : null;
                    $oldAvg = !empty($g['old_values']) ? round(array_sum($g['old_values']) / count($g['old_values']), 2) : null;
                    $newAvg = !empty($g['new_values']) ? round(array_sum($g['new_values']) / count($g['new_values']), 2) : null;
                    $factor = ($avg && $avg > 0) ? round($assessIdx / $avg, 4) : null;
                    $results[] = [
                        'comm_code'    => $code,
                        'period'       => $label,
                        'period_label' => $label,
                        'period_sort'  => $g['sort'],
                        'old_index'    => $oldAvg,
                        'new_index'    => $newAvg,
                        'composite'    => $avg,
                        'index_value'  => $avg,
                        'assess_index' => $assessIdx,
                        'factor'       => $factor,
                        'provisional'  => $g['prov'],
                        'extrapolated' => $g['extrapolated'] ?? false,
                        'interpolated' => $g['interpolated'] ?? false,
                    ];
                }
            }
        }

        // Save to history
        $histUser = currentUser();
        if ($histUser) {
            $commLabels = [];
            foreach ($codes as $c) {
                $commLabels[] = [$c, $commNames[$c]['comm_name'] ?? $c];
            }
            $pdo->prepare(
                'INSERT INTO calc_history
                   (user_id, codes, comm_labels, frequency, series_start, assess_year, assess_period,
                    assess_label, show_index, show_extrap, begin_month, begin_year, result_count)
                 VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?)'
            )->execute([
                $histUser['id'],
                implode(',', $codes),
                json_encode($commLabels, JSON_UNESCAPED_UNICODE),
                $frequency,
                $seriesStart,
                $assessYear,
                $assessPeriodNum,
                $assessLabel,
                $showIndex         ? 1 : 0,
                $showExtrapolation ? 1 : 0,
                $showExtrapolation ? $beginMonth : null,
                $showExtrapolation ? $beginYear  : null,
                0,
            ]);
            $histLastId = (int)$pdo->lastInsertId();
        }

        // Drop periods after the assessment period
        if ($frequency === 'monthly') {
            $assessPeriodSort = $assessYear * 100 + $assessPeriodNum;
        } elseif ($frequency === 'quarterly' || $frequency === 'half-yearly') {
            $assessPeriodSort = $assessYear * 10 + $assessPeriodNum;
        } else {
            $assessPeriodSort = $assessYear;
        }
        $results = array_values(array_filter($results, fn($r) => $r['period_sort'] <= $assessPeriodSort));

        usort($results, function ($a, $b) {
            $c = strcmp($a['comm_code'], $b['comm_code']);
            return $c !== 0 ? $c : $b['period_sort'] - $a['period_sort'];
        });

        if (!empty($histLastId)) {
            $pdo->prepare('UPDATE calc_history SET result_count = ? WHERE id = ?')
                ->execute([count($results), $histLastId]);
        }

        // Extrapolation Part B: inject extrapolated rows at top (monthly only)
        if ($showExtrapolation && $extrapMonths && $frequency === 'monthly') {
            $newResults = [];
            $seenCodes  = [];
            foreach ($results as $row) {
                if (!isset($seenCodes[$row['comm_code']])) {
                    $seenCodes[$row['comm_code']] = true;
                    $code = $row['comm_code'];
                    foreach (array_reverse($extrapMonths) as $ep) {
                        $ym  = $ep['year'] * 100 + $ep['month'];
                        $ev  = $extrapolatedValues[$code][$ym] ?? null;
                        if ($ev === null) continue;
                        $aIdx = $assessComposites[$code] ?? null;
                        $lk   = $links[$code] ?? null;
                        $evComp = ($overlapPrefer === '2004-05' && $lk && $lk[1] > 0)
                            ? round($ev * ($lk[0] / $lk[1]), 2)
                            : $ev;
                        $factor = ($evComp > 0 && $aIdx !== null) ? round($aIdx / $evComp, 4) : null;
                        $newResults[] = [
                            'comm_code'    => $code,
                            'period'       => sprintf('INDX%02d%04d', $ep['month'], $ep['year']),
                            'period_label' => $monthNames[$ep['month']] . ' ' . $ep['year'],
                            'period_sort'  => $ym,
                            'old_index'    => null,
                            'new_index'    => $ev,
                            'composite'    => $evComp,
                            'index_value'  => $evComp,
                            'assess_index' => $aIdx ?? 0,
                            'factor'       => $factor,
                            'provisional'  => 0,
                            'extrapolated' => true,
                            'interpolated' => false,
                        ];
                    }
                }
                $newResults[] = $row;
            }
            $results = $newResults;
        }

        // Interpolation Part B: append interpolated rows at bottom (monthly only)
        if ($showInterpolation && $interpMonths && $frequency === 'monthly') {
            $seenCodes = [];
            $interpRows = [];
            foreach ($results as $row) {
                if (!isset($seenCodes[$row['comm_code']])) {
                    $seenCodes[$row['comm_code']] = true;
                    $code = $row['comm_code'];
                    $aIdx = $assessComposites[$code] ?? null;
                    $lk   = $links[$code] ?? null;
                    foreach ($interpMonths as $ep) {
                        $ym  = $ep['year'] * 100 + $ep['month'];
                        $iv  = $interpolatedValues[$code][$ym] ?? null;
                        if ($iv === null) continue;
                        $ivComp = ($overlapPrefer === '2004-05' && $lk && $lk[1] > 0)
                            ? round($iv * ($lk[0] / $lk[1]), 2) : $iv;
                        $factor = ($ivComp > 0 && $aIdx !== null) ? round($aIdx / $ivComp, 4) : null;
                        $interpRows[] = [
                            'comm_code'    => $code,
                            'period'       => sprintf('INTP%02d%04d', $ep['month'], $ep['year']),
                            'period_label' => $monthNames[$ep['month']] . ' ' . $ep['year'],
                            'period_sort'  => $ym,
                            'old_index'    => null,
                            'new_index'    => $iv,
                            'composite'    => $ivComp,
                            'index_value'  => $ivComp,
                            'assess_index' => $aIdx ?? 0,
                            'factor'       => $factor,
                            'provisional'  => 0,
                            'extrapolated' => false,
                            'interpolated' => true,
                        ];
                    }
                }
            }
            $results = array_merge($results, $interpRows);
        }
    }
}

$pageTitle = 'Calculate Index Factor — Shaurya';
require __DIR__ . '/includes/header.php';
?>

<div class="row g-4">
    <div class="col-lg-4">
        <div class="card"><div class="card-body">
            <h5 class="mb-3">Index Factor Calculator</h5>

            <?php if ($errors): ?>
                <div class="alert alert-danger alert-sm py-2">
                    <?php foreach ($errors as $e): ?><div><?= h($e) ?></div><?php endforeach; ?>
                </div>
            <?php endif; ?>

            <form method="post" id="calcForm">
                <!-- Commodity search -->
                <div class="mb-3">
                    <label class="form-label">Commodities</label>
                    <input type="text" id="commSearch" class="form-control" placeholder="Search by name or code..." autocomplete="off">
                    <div id="searchResults" class="list-group mt-1" style="max-height:200px;overflow-y:auto;display:none;position:absolute;z-index:99;width:calc(100% - 2rem);"></div>
                    <input type="hidden" name="codes" id="codesInput" value="">
                    <div id="selectedComms" class="mt-2"></div>
                </div>

                <!-- Frequency -->
                <div class="mb-3">
                    <label class="form-label">Frequency</label>
                    <select name="frequency" id="freqSelect" class="form-select">
                        <option value="monthly" <?= ($frequency ?? 'monthly') === 'monthly' ? 'selected' : '' ?>>Monthly</option>
                        <option value="quarterly" <?= ($frequency ?? '') === 'quarterly' ? 'selected' : '' ?>>Quarterly</option>
                        <option value="half-yearly" <?= ($frequency ?? '') === 'half-yearly' ? 'selected' : '' ?>>Half-yearly</option>
                        <option value="annually" <?= ($frequency ?? '') === 'annually' ? 'selected' : '' ?>>Annually</option>
                    </select>
                </div>

                <!-- Financial Year Series (Q/H/A only) -->
                <div class="mb-3" id="seriesGroup" style="display:none;">
                    <label class="form-label">Financial Year Series</label>
                    <select name="series_start" class="form-select">
                        <?php foreach ($seriesOptions as $sm => $slabel): ?>
                            <option value="<?= $sm ?>" <?= $sm === ($seriesStart ?? 4) ? 'selected' : '' ?>><?= h($slabel) ?></option>
                        <?php endforeach; ?>
                    </select>
                    <div class="form-text">Defines which month starts the financial year.</div>
                </div>

                <!-- Assessment Period + Year -->
                <div class="row mb-3">
                    <div class="col-6" id="assessPeriodCol">
                        <label class="form-label" id="assessPeriodLabel">Assessment Month</label>
                        <select name="assess_month" id="assessMonthSel" class="form-select">
                            <?php for ($m = 1; $m <= 12; $m++): ?>
                                <option value="<?= $m ?>" <?= $m === ($assessMonth ?: $maxMonth) ? 'selected' : '' ?>><?= $monthFull[$m] ?></option>
                            <?php endfor; ?>
                        </select>
                        <select name="assess_period" id="assessPeriodSel" class="form-select" style="display:none;" disabled></select>
                    </div>
                    <div class="col-6">
                        <label class="form-label">Assessment Year</label>
                        <select name="assess_year" id="assessYearSel" class="form-select">
                            <?php
                            $yearFloor = $showInterpolation ? 1920 : $minYear;
                            for ($y = $maxYear; $y >= $yearFloor; $y--): ?>
                                <option value="<?= $y ?>" <?= $y === ($assessYear ?: $maxYear) ? 'selected' : '' ?>><?= $y ?></option>
                            <?php endfor; ?>
                        </select>
                    </div>
                </div>

                <!-- Overlap series preference -->
                <div class="mb-3 p-2 border rounded bg-light">
                    <div class="form-label mb-1 small fw-semibold">Overlap period preference <span class="text-muted fw-normal">(Apr 2012 – Mar 2017)</span></div>
                    <div class="d-flex gap-3">
                        <div class="form-check mb-0">
                            <input class="form-check-input" type="radio" name="overlap_prefer" id="overlapNew" value="2011-12"
                                <?= $overlapPrefer === '2011-12' ? 'checked' : '' ?>>
                            <label class="form-check-label small" for="overlapNew">2011&#8209;12 series</label>
                        </div>
                        <div class="form-check mb-0">
                            <input class="form-check-input" type="radio" name="overlap_prefer" id="overlapOld" value="2004-05"
                                <?= $overlapPrefer === '2004-05' ? 'checked' : '' ?>>
                            <label class="form-check-label small" for="overlapOld">2004&#8209;05 series</label>
                        </div>
                    </div>
                    <div class="form-text">For periods where both series have data, which drives the composite index.</div>
                </div>

                <div class="mb-2 form-check form-switch">
                    <input class="form-check-input" type="checkbox" name="show_index" id="showIndexToggle" <?= $showIndex ? 'checked' : '' ?>>
                    <label class="form-check-label" for="showIndexToggle">Show Index Values</label>
                </div>

                <div class="mb-2 form-check form-switch" id="extrapToggleWrap">
                    <input class="form-check-input" type="checkbox" name="show_extrapolation" id="showExtrapToggle" <?= $showExtrapolation ? 'checked' : '' ?>>
                    <label class="form-check-label" for="showExtrapToggle">Show Extrapolated Value</label>
                </div>

                <div class="mb-2 form-check form-switch">
                    <input class="form-check-input" type="checkbox" name="show_interpolation" id="showInterpToggle" <?= $showInterpolation ? 'checked' : '' ?>>
                    <label class="form-check-label" for="showInterpToggle">Show Back-interpolated Values <span class="text-muted small">(to 1920)</span></label>
                </div>

                <div id="extrapOptions" style="<?= $showExtrapolation ? '' : 'display:none;' ?>">
                    <div class="row g-2 mb-1">
                        <div class="col-6">
                            <label class="form-label form-label-sm mb-1">Beginning Month</label>
                            <select name="begin_month" class="form-select form-select-sm">
                                <?php for ($m = 1; $m <= 12; $m++): ?>
                                    <option value="<?= $m ?>" <?= $m === ($beginMonth ?: $maxMonth) ? 'selected' : '' ?>><?= $monthFull[$m] ?></option>
                                <?php endfor; ?>
                            </select>
                        </div>
                        <div class="col-6">
                            <label class="form-label form-label-sm mb-1">Beginning Year</label>
                            <select name="begin_year" class="form-select form-select-sm">
                                <?php
                                $defaultBeginYear = max($minYear, $maxYear - 10);
                                for ($y = $maxYear - 1; $y >= $minYear; $y--): ?>
                                    <option value="<?= $y ?>" <?= $y === ($beginYear ?: $defaultBeginYear) ? 'selected' : '' ?>><?= $y ?></option>
                                <?php endfor; ?>
                            </select>
                        </div>
                    </div>
                    <div class="small text-muted mb-2">
                        Latest available: <?= $monthNames[$maxMonth] ?> <?= $maxYear ?>.
                        Will extrapolate up to the selected assessment period.
                    </div>
                </div>

                <button type="submit" class="btn btn-primary w-100">Calculate</button>
            </form>
            <div class="text-center mt-2">
                <a href="<?= base_url('history.php') ?>" class="small text-muted">View calculation history</a>
            </div>
        </div></div>
    </div>

    <div class="col-lg-8">
        <?php if ($results !== null): ?>
            <style>
                .table-compact { font-size: 0.75rem; }
                .table-compact th, .table-compact td { padding: 0.18rem 0.35rem !important; line-height: 1.3; }
                .table-compact .code-chip { font-size: 0.68rem; }
                .extrap-row td { background-color: rgba(255,193,7,0.18) !important; font-style: italic; }
                .interp-row td { background-color: rgba(13,202,240,0.12) !important; font-style: italic; }
                .col-old  { background-color: rgba(13,110,253,0.06); }
                .col-new  { background-color: rgba(25,135,84,0.06); }
                .col-comp { background-color: rgba(108,117,125,0.08); font-weight: 500; }
            </style>
            <div class="card"><div class="card-body p-2">
                <div class="d-flex justify-content-between align-items-center mb-2">
                    <div>
                        <span class="fw-semibold" style="font-size:0.85rem;">Results — Assessment: <?= h($assessLabel) ?></span>
                        <?php if ($hasOldData): ?>
                            <span class="badge bg-info text-dark ms-2" style="font-size:0.65rem;">Series linked</span>
                        <?php endif; ?>
                    </div>
                    <?php if ($results): ?>
                        <a href="<?= base_url('export.php') ?>?<?= http_build_query($_POST) ?>" class="btn btn-sm btn-success">Export Excel</a>
                    <?php endif; ?>
                </div>

                <?php if (!empty($overrideOptions)): ?>
                <div class="alert alert-warning py-2 px-3 mb-2" style="font-size:0.82rem;">
                    <strong>2004&#8209;05 data not available</strong> for the following commodit<?= count($overrideOptions) > 1 ? 'ies' : 'y' ?>.
                    Select a reference commodity from the 2004&#8209;05 series to include historical data in the calculation.
                    <form method="post" class="mt-2 mb-0">
                        <?php
                        // Preserve all original calculation params
                        $preserveFields = ['codes','frequency','series_start','assess_year','assess_month',
                                           'assess_period','begin_month','begin_year','overlap_prefer'];
                        foreach ($preserveFields as $f):
                            if (isset($_POST[$f])): ?>
                        <input type="hidden" name="<?= h($f) ?>" value="<?= h($_POST[$f]) ?>">
                        <?php endif; endforeach;
                        if ($showIndex):        ?><input type="hidden" name="show_index" value="1"><?php endif;
                        if ($showExtrapolation): ?><input type="hidden" name="show_extrapolation" value="1"><?php endif;
                        // Preserve already-resolved overrides
                        foreach ($oldOverrides as $nc => $oc): ?>
                        <input type="hidden" name="old_override[<?= h($nc) ?>]" value="<?= h($oc) ?>">
                        <?php endforeach; ?>
                        <?php foreach ($overrideOptions as $nc => $opts):
                            $commLabel = $commNames[$nc]['comm_name'] ?? $nc;
                            // Get parent name for context label
                            $parNameStmt = $pdo->prepare(
                                "SELECT p.comm_name FROM commodities c
                                   JOIN commodities p ON p.comm_code = c.parent_code AND p.base_year = '2004-05'
                                  WHERE c.comm_code = (
                                      SELECT parent_code FROM commodities WHERE comm_code = ? AND base_year = '2011-12'
                                  ) AND c.base_year = '2004-05' LIMIT 1"
                            );
                            $parNameStmt->execute([$nc]);
                            $parentGroupName = $parNameStmt->fetchColumn() ?: '';
                        ?>
                        <div class="mb-1">
                            <div class="mb-1"><span class="fw-semibold"><?= h($commLabel) ?></span><?php if ($parentGroupName): ?> <span class="text-muted" style="font-size:0.78rem;">— under <?= h($parentGroupName) ?></span><?php endif; ?></div>
                            <select name="old_override[<?= h($nc) ?>]" class="form-select form-select-sm" style="max-width:360px;" required>
                                <option value="">— Select 2004&#8209;05 reference —</option>
                                <?php foreach ($opts as $opt): ?>
                                <option value="<?= h($opt['comm_code']) ?>"><?= h($opt['comm_name']) ?> (<?= h($opt['comm_code']) ?>)</option>
                                <?php endforeach; ?>
                            </select>
                        </div>
                        <?php endforeach; ?>
                        <button type="submit" class="btn btn-warning btn-sm mt-1">Apply &amp; Recalculate</button>
                    </form>
                </div>
                <?php endif; ?>

                <?php if (!$results): ?>
                    <p class="text-muted mb-0">No results. Check that the assessment date has data for the selected commodities.</p>
                <?php else: ?>
                    <div class="table-responsive" style="max-height: 78vh; overflow-y: auto;">
                        <table class="table table-sm table-bordered align-middle text-end table-compact">
                            <thead class="table-light sticky-top">
                                <tr>
                                    <th class="text-start">Commodity</th>
                                    <th class="text-start">Period</th>
                                    <?php
                                        $thOldCodes = implode(', ', array_map(fn($c) => $newToOld[$c] ?? $c, $codes ?? []));
                                        $thNewCodes = implode(', ', $codes ?? []);
                                        $thMfParts  = [];
                                        foreach ($codes ?? [] as $c) {
                                            $lkTh = $links[$c] ?? null;
                                            $mfTh = ($overlapPrefer === '2004-05')
                                                ? (($lkTh && $lkTh[1] > 0) ? round($lkTh[0] / $lkTh[1], 4) : null)
                                                : (($lkTh && $lkTh[0] > 0) ? round($lkTh[1] / $lkTh[0], 4) : null);
                                            if ($mfTh !== null) $thMfParts[] = number_format($mfTh, 4);
                                        }
                                        $thMf = implode(', ', $thMfParts);
                                    ?>
                                    <?php if ($hasOldData && $showIndex): ?>
                                        <th class="col-old">2004&#8209;05 Index<br><code class="code-chip fw-normal text-muted"><?= h($thOldCodes) ?></code></th>
                                        <th class="col-new">2011&#8209;12 Index<br><code class="code-chip fw-normal text-muted"><?= h($thNewCodes) ?></code></th>
                                        <th class="col-comp">Composite (<?= h($overlapPrefer) ?>)<?php if ($thMf): ?><br><span class="fw-normal text-muted" style="font-size:0.68rem;">MF: <?= h($thMf) ?></span><?php endif; ?></th>
                                        <th>Assess Index</th>
                                    <?php elseif ($hasOldData): ?>
                                        <th class="col-old">2004&#8209;05 Index<br><code class="code-chip fw-normal text-muted"><?= h($thOldCodes) ?></code></th>
                                        <th class="col-new">2011&#8209;12 Index<br><code class="code-chip fw-normal text-muted"><?= h($thNewCodes) ?></code></th>
                                        <th class="col-comp">Composite (<?= h($overlapPrefer) ?>)<?php if ($thMf): ?><br><span class="fw-normal text-muted" style="font-size:0.68rem;">MF: <?= h($thMf) ?></span><?php endif; ?></th>
                                    <?php elseif ($showIndex): ?>
                                        <th>Index</th>
                                        <th>Assessment Index</th>
                                    <?php endif; ?>
                                    <th>Index Factor</th>
                                </tr>
                            </thead>
                            <tbody>
                                <?php
                                $prevCode = '';
                                foreach ($results as $r):
                                    $showComm = $r['comm_code'] !== $prevCode;
                                    $prevCode = $r['comm_code'];
                                    $comm = $commNames[$r['comm_code']] ?? null;
                                    $lvl  = $comm ? (int)$comm['level'] : 6;
                                    $safeCode = h($r['comm_code']);
                                    $lk = $links[$r['comm_code']] ?? null;
                                    $mf = ($overlapPrefer === '2004-05')
                                        ? (($lk && $lk[1] > 0) ? round($lk[0] / $lk[1], 4) : null)
                                        : (($lk && $lk[0] > 0) ? round($lk[1] / $lk[0], 4) : null);
                                ?>
                                    <tr class="<?= ($r['provisional'] ?? 0) ? 'table-warning' : (($r['extrapolated'] ?? false) ? 'extrap-row' : (($r['interpolated'] ?? false) ? 'interp-row' : '')) ?> <?= $showComm ? 'comm-header' : 'comm-detail' ?>"
                                        data-comm="<?= $safeCode ?>">
                                        <td class="text-start level-<?= $lvl ?>">
                                            <?php if ($showComm && $comm): ?>
                                                <span class="comm-toggle" style="cursor:pointer;user-select:none;">
                                                    <span class="comm-arrow me-1">&#9660;</span>
                                                    <strong><?= h($comm['comm_name']) ?></strong>
                                                </span>
                                            <?php endif; ?>
                                        </td>
                                        <td class="text-start"><?= h($r['period_label']) ?><?php if ($r['extrapolated'] ?? false): ?> <span class="badge bg-warning text-dark" style="font-size:0.6rem;">extrap</span><?php elseif ($r['interpolated'] ?? false): ?> <span class="badge bg-info text-dark" style="font-size:0.6rem;">interp</span><?php endif; ?></td>
                                        <?php if ($hasOldData && $showIndex): ?>
                                            <td class="col-old"><?= $r['old_index'] !== null ? number_format($r['old_index'], 2) : '&mdash;' ?></td>
                                            <td class="col-new"><?= $r['new_index'] !== null ? number_format($r['new_index'], 2) : '&mdash;' ?></td>
                                            <td class="col-comp"><?= $r['composite'] !== null ? number_format($r['composite'], 2) : '&mdash;' ?></td>
                                            <td><?= number_format($r['assess_index'], 2) ?></td>
                                        <?php elseif ($hasOldData): ?>
                                            <td class="col-old"><?= $r['old_index'] !== null ? number_format($r['old_index'], 2) : '&mdash;' ?></td>
                                            <td class="col-new"><?= $r['new_index'] !== null ? number_format($r['new_index'], 2) : '&mdash;' ?></td>
                                            <td class="col-comp"><?= $r['composite'] !== null ? number_format($r['composite'], 2) : '&mdash;' ?></td>
                                        <?php elseif ($showIndex): ?>
                                            <td><?= $r['index_value'] !== null ? number_format($r['index_value'], 2) : '&mdash;' ?></td>
                                            <td><?= number_format($r['assess_index'], 2) ?></td>
                                        <?php endif; ?>
                                        <td class="fw-semibold"><?= $r['factor'] !== null ? number_format($r['factor'], 4) : '&mdash;' ?></td>
                                    </tr>
                                <?php endforeach; ?>
                            </tbody>
                        </table>
                    </div>
                    <div class="small text-muted mt-2">
                        Yellow = provisional &nbsp;|&nbsp; Amber italic = extrapolated &nbsp;|&nbsp; Cyan italic = back-interpolated.
                        <?php if ($hasOldData): ?>
                            <span class="col-old px-1">&#9646;</span> 2004&#8209;05 raw &nbsp;
                            <span class="col-new px-1">&#9646;</span> 2011&#8209;12 raw &nbsp;
                            <span class="col-comp px-1">&#9646;</span> Composite (<?= h($overlapPrefer) ?> scale)
                            <?php if ($overlapPrefer === '2004-05'): ?>
                                = New Index &times; MF. MF = Old &divide; New Transition Index (shown below composite on first row).
                            <?php else: ?>
                                = New Index directly; old-only periods show raw value. MF shown below composite on first row.
                            <?php endif; ?>
                            Index Factor = Assessment Index &divide; Composite.
                            Overlap preference: <strong><?= h($overlapPrefer) ?></strong>.
                        <?php else: ?>
                            Index Factor = Assessment Index &divide; Period Index.
                        <?php endif; ?>
                        <?= $frequency !== 'monthly' ? 'Period indices are averaged across constituent months.' : '' ?>
                    </div>
                <?php endif; ?>
            </div></div>
        <?php else: ?>
            <div class="card"><div class="card-body text-center text-muted py-5">
                <h5>Select commodities and an assessment date, then click Calculate.</h5>
                <p>Index Factor = Assessment Date Index &divide; Index of the Period</p>
            </div></div>
        <?php endif; ?>
    </div>
</div>

<script>
const selected = {};
const searchInput = document.getElementById('commSearch');
const resultsDiv = document.getElementById('searchResults');
const codesInput = document.getElementById('codesInput');
const selectedDiv = document.getElementById('selectedComms');

let searchTimeout;
searchInput.addEventListener('input', function() {
    clearTimeout(searchTimeout);
    const q = this.value.trim();
    if (q.length < 1) { resultsDiv.style.display = 'none'; return; }
    searchTimeout = setTimeout(() => {
        fetch('<?= base_url("api_search.php") ?>?q=' + encodeURIComponent(q))
            .then(r => r.json())
            .then(data => {
                resultsDiv.innerHTML = '';
                if (!data.length) {
                    resultsDiv.innerHTML = '<div class="list-group-item text-muted">No results</div>';
                }
                data.forEach(c => {
                    if (selected[c.comm_code]) return;
                    const item = document.createElement('a');
                    item.href = '#';
                    item.className = 'list-group-item list-group-item-action py-1 small';
                    item.innerHTML = '<code>' + c.comm_code + '</code> ' + c.comm_name + ' <span class="text-muted">(L' + c.level + ')</span>';
                    item.onclick = (e) => { e.preventDefault(); addComm(c); resultsDiv.style.display = 'none'; searchInput.value = ''; };
                    resultsDiv.appendChild(item);
                });
                resultsDiv.style.display = 'block';
            });
    }, 250);
});

document.addEventListener('click', (e) => {
    if (!resultsDiv.contains(e.target) && e.target !== searchInput) resultsDiv.style.display = 'none';
});

function addComm(c) { selected[c.comm_code] = c; renderSelected(); }
function removeComm(code) { delete selected[code]; renderSelected(); }

function renderSelected() {
    codesInput.value = Object.keys(selected).join(',');
    selectedDiv.innerHTML = '';
    Object.values(selected).forEach(c => {
        const tag = document.createElement('span');
        tag.className = 'badge bg-primary me-1 mb-1';
        tag.style.cursor = 'pointer';
        tag.title = 'Click to remove';
        tag.innerHTML = c.comm_code + ' - ' + c.comm_name + ' &times;';
        tag.onclick = () => removeComm(c.comm_code);
        selectedDiv.appendChild(tag);
    });
}

const freqSelect      = document.getElementById('freqSelect');
const seriesGroup     = document.getElementById('seriesGroup');
const seriesStartSel  = document.querySelector('select[name="series_start"]');
const assessPeriodCol = document.getElementById('assessPeriodCol');
const assessPeriodLbl = document.getElementById('assessPeriodLabel');
const assessMonthSel  = document.getElementById('assessMonthSel');
const assessPeriodSel = document.getElementById('assessPeriodSel');
const assessYearSel   = document.getElementById('assessYearSel');

const periodOptions = {
    quarterly:    [{v:1,t:'Q1'},{v:2,t:'Q2'},{v:3,t:'Q3'},{v:4,t:'Q4'}],
    'half-yearly':[{v:1,t:'H1'},{v:2,t:'H2'}],
};

const maxYm = <?= json_encode(($range && $range['max_ym'] !== null) ? (int)$range['max_ym'] : (int)date('Ym')) ?>;

function getDefaultFyYear(freq, periodNum, seriesStart) {
    let count, startOff;
    if      (freq === 'quarterly')    { count = 3;  startOff = (periodNum - 1) * 3; }
    else if (freq === 'half-yearly')  { count = 6;  startOff = (periodNum - 1) * 6; }
    else                              { count = 12; startOff = 0; }
    const lastOff = startOff + count - 1;
    const lastM   = ((seriesStart - 1 + lastOff) % 12) + 1;
    const base = Math.floor((maxYm - lastM) / 100);
    return lastM >= seriesStart ? base : base - 1;
}

function updateDefaultYear() {
    const freq        = freqSelect.value;
    const seriesStart = parseInt(seriesStartSel.value) || 4;
    const periodNum   = (freq === 'annually') ? 1 : (parseInt(assessPeriodSel.value) || 1);
    const yr          = getDefaultFyYear(freq, periodNum, seriesStart);
    if (assessYearSel.querySelector(`option[value="${yr}"]`)) {
        assessYearSel.value = String(yr);
    }
}

function applyFrequency(freq, restoreVal, skipYearUpdate) {
    const isMonthly  = freq === 'monthly';
    const isAnnually = freq === 'annually';
    seriesGroup.style.display = isMonthly ? 'none' : 'block';
    if (isMonthly) {
        assessPeriodLbl.textContent   = 'Assessment Month';
        assessMonthSel.style.display  = '';
        assessMonthSel.disabled       = false;
        assessPeriodSel.style.display = 'none';
        assessPeriodSel.disabled      = true;
        assessPeriodCol.style.display = '';
    } else if (isAnnually) {
        assessPeriodCol.style.display = 'none';
        assessMonthSel.disabled       = true;
        assessPeriodSel.disabled      = true;
        if (!skipYearUpdate) updateDefaultYear();
    } else {
        assessPeriodLbl.textContent   = freq === 'quarterly' ? 'Assessment Quarter' : 'Assessment Half';
        assessPeriodCol.style.display = '';
        assessMonthSel.style.display  = 'none';
        assessMonthSel.disabled       = true;
        assessPeriodSel.style.display = '';
        assessPeriodSel.disabled      = false;
        assessPeriodSel.innerHTML = periodOptions[freq]
            .map(o => `<option value="${o.v}"${o.v == restoreVal ? ' selected' : ''}>${o.t}</option>`)
            .join('');
        if (!skipYearUpdate) updateDefaultYear();
    }
    updateExtrapToggle();
}

freqSelect.addEventListener('change', function() { applyFrequency(this.value, 1, false); });

function getAssessmentLastYm() {
    const freq        = freqSelect.value;
    const assessYr    = parseInt(assessYearSel.value) || 0;
    const seriesStart = parseInt(seriesStartSel.value) || 4;
    if (freq === 'monthly') {
        const mo = parseInt(assessMonthSel.value) || 0;
        return assessYr * 100 + mo;
    }
    let lastOff;
    if (freq === 'quarterly') {
        const qNum = parseInt(assessPeriodSel.value) || 1;
        lastOff = (qNum - 1) * 3 + 2;
    } else if (freq === 'half-yearly') {
        const hNum = parseInt(assessPeriodSel.value) || 1;
        lastOff = (hNum - 1) * 6 + 5;
    } else {
        lastOff = 11;
    }
    const lastM = ((seriesStart - 1 + lastOff) % 12) + 1;
    const lastY = lastM >= seriesStart ? assessYr : assessYr + 1;
    return lastY * 100 + lastM;
}

let _extrapPrevAllowed = false;
function updateExtrapToggle() {
    const allowed = getAssessmentLastYm() > maxYm;
    const toggle  = document.getElementById('showExtrapToggle');
    const wrap    = document.getElementById('extrapToggleWrap');
    const opts    = document.getElementById('extrapOptions');
    toggle.disabled    = !allowed;
    wrap.style.opacity = allowed ? '1' : '0.4';
    wrap.title         = allowed ? '' : 'Select an assessment period beyond the latest available data to enable extrapolation';
    if (!allowed) {
        toggle.checked        = false;
        opts.style.display    = 'none';
        toggle._userUnchecked = false;
    } else if (!_extrapPrevAllowed || !toggle._userUnchecked) {
        toggle.checked     = true;
        opts.style.display = '';
    }
    _extrapPrevAllowed = allowed;
}

document.getElementById('showExtrapToggle').addEventListener('change', function() {
    this._userUnchecked = !this.checked;
    document.getElementById('extrapOptions').style.display = this.checked ? '' : 'none';
});

(function () {
    const interpToggle = document.getElementById('showInterpToggle');
    const yearSel      = document.getElementById('assessYearSel');
    const minYearDb    = <?= (int)$minYear ?>;
    const maxYearDb    = <?= (int)$maxYear ?>;
    const curYear      = parseInt(yearSel.value) || maxYearDb;

    function rebuildYearOptions(withInterp) {
        const floor   = withInterp ? 1920 : minYearDb;
        const selVal  = parseInt(yearSel.value) || maxYearDb;
        yearSel.innerHTML = '';
        for (let y = maxYearDb; y >= floor; y--) {
            const o = document.createElement('option');
            o.value = y; o.textContent = y;
            if (y === selVal) o.selected = true;
            yearSel.appendChild(o);
        }
    }

    interpToggle.addEventListener('change', function() {
        rebuildYearOptions(this.checked);
        updateExtrapToggle();
    });
})();

assessPeriodSel.addEventListener('change', function() { updateDefaultYear(); updateExtrapToggle(); });
assessMonthSel.addEventListener('change', updateExtrapToggle);
assessYearSel.addEventListener('change', updateExtrapToggle);
seriesStartSel.addEventListener('change', function() { updateDefaultYear(); updateExtrapToggle(); });

const isPostRender = <?= json_encode($_SERVER['REQUEST_METHOD'] === 'POST') ?>;
applyFrequency(freqSelect.value, <?= json_encode($assessPeriodNum ?? 1) ?>, isPostRender);

// Restore pre-selected commodities on POST re-render
<?php if ($_SERVER['REQUEST_METHOD'] === 'POST' && !empty($codes)): ?>
<?php foreach ($codes as $c): ?>
<?php if (isset($commNames[$c])): ?>
selected[<?= json_encode($c) ?>] = {
    comm_code: <?= json_encode($c) ?>,
    comm_name: <?= json_encode($commNames[$c]['comm_name'] ?? $c) ?>,
    level: <?= (int)($commNames[$c]['level'] ?? 6) ?>
};
<?php endif; ?>
<?php endforeach; ?>
renderSelected();
<?php endif; ?>

// Collapsible commodity rows
document.querySelectorAll('tr.comm-header').forEach(headerRow => {
    headerRow.style.cursor = 'pointer';
    headerRow.addEventListener('click', function () {
        const code    = this.dataset.comm;
        const details = document.querySelectorAll(`tr.comm-detail[data-comm="${code}"]`);
        const arrow   = this.querySelector('.comm-arrow');
        const collapsed = details.length && details[0].style.display === 'none';
        details.forEach(r => r.style.display = collapsed ? '' : 'none');
        if (arrow) arrow.textContent = collapsed ? '▼' : '►';
    });
});
</script>

<?php require __DIR__ . '/includes/footer.php'; ?>

Youez - 2016 - github.com/yon3zu
LinuXploit