<?php
/**
 * PricingVersionOverheads · Slice 8S.
 *
 * Overhead pool lines attached to a pricing version. Distinct from tender_items
 * (buyer's spec) and tender_pricing_lines (priced BOQ). The client sees a subset
 * depending on each line's display_mode:
 *   PRELIMS_LINE   → visible on the client BOQ as a "Preliminaries" line
 *   DISTRIBUTED    → allocated across item line prices pro-rata by line_price
 *   INTERNAL_ONLY  → never surfaces to client (internal margin math only)
 *
 * TenderPricing::rollupVersion is what actually reflects these totals into
 * version.sum_cost / sum_overhead / sum_price.
 */
class PricingVersionOverheads extends Model
{
    public const CATEGORIES = [
        'MANPOWER','EQUIPMENT','TRANSPORT','DISMANTLE_ASSEMBLE','SITE_SETUP',
        'ADMIN_OVERHEAD','CUSTOMS_DUTY','FREIGHT_INSURANCE','CLEARING_HANDLING','OTHER',
    ];

    public const DISPLAY_MODES = ['PRELIMS_LINE','DISTRIBUTED','INTERNAL_ONLY'];

    /**
     * Preset overhead templates. Quick-load buttons on the pricing tab drop these in at zero amounts
     * so PC can fill numbers fast during a tender crunch.
     */
    public const TEMPLATES = [
        'GOODS_LOCAL' => [
            ['category' => 'MANPOWER',           'label' => 'Loading / offloading crew',      'display_mode' => 'DISTRIBUTED'],
            ['category' => 'TRANSPORT',          'label' => 'Local delivery to site',          'display_mode' => 'DISTRIBUTED'],
            ['category' => 'DISMANTLE_ASSEMBLE', 'label' => 'On-site assembly',                'display_mode' => 'PRELIMS_LINE'],
            ['category' => 'SITE_SETUP',         'label' => 'Site protection / access',        'display_mode' => 'PRELIMS_LINE'],
            ['category' => 'ADMIN_OVERHEAD',     'label' => 'PM + admin allocation',           'display_mode' => 'INTERNAL_ONLY'],
        ],
        'GOODS_IMPORT' => [
            ['category' => 'FREIGHT_INSURANCE',  'label' => 'CIF freight + marine insurance',  'display_mode' => 'DISTRIBUTED'],
            ['category' => 'CUSTOMS_DUTY',       'label' => 'Import duty + VAT deferred',      'display_mode' => 'DISTRIBUTED'],
            ['category' => 'CLEARING_HANDLING',  'label' => 'Clearing agent + port handling',  'display_mode' => 'DISTRIBUTED'],
            ['category' => 'TRANSPORT',          'label' => 'Port → site inland transport',    'display_mode' => 'DISTRIBUTED'],
            ['category' => 'DISMANTLE_ASSEMBLE', 'label' => 'On-site assembly',                'display_mode' => 'PRELIMS_LINE'],
            ['category' => 'ADMIN_OVERHEAD',     'label' => 'PM + admin allocation',           'display_mode' => 'INTERNAL_ONLY'],
        ],
    ];

    public function listFor(int $versionId): array
    {
        $db = new DB();
        return $db->mySelect(
            "SELECT * FROM pricing_version_overheads
              WHERE version_id = :v AND deleted = 0
              ORDER BY line_no, c0d",
            [':v' => $versionId]
        ) ?: [];
    }

    public function find(int $id): ?array
    {
        $db = new DB();
        $r = $db->mySelect("SELECT * FROM pricing_version_overheads WHERE c0d = :id AND deleted = 0", [':id' => $id]);
        return $r[0] ?? null;
    }

    public function nextLineNo(int $versionId): int
    {
        $db = new DB();
        $r = $db->mySelect(
            "SELECT COALESCE(MAX(line_no), 0) + 1 AS n
               FROM pricing_version_overheads WHERE version_id = :v AND deleted = 0",
            [':v' => $versionId]
        );
        return (int)($r[0]['n'] ?? 1);
    }

    public function add(int $versionId, int $tenderId, array $row, int $userId, string $userName): array
    {
        $label = trim((string)($row['label'] ?? ''));
        if ($label === '') return ['ok' => false, 'msg' => 'Label is required.'];
        $cat = strtoupper((string)($row['category'] ?? 'OTHER'));
        if (!in_array($cat, self::CATEGORIES, true)) $cat = 'OTHER';
        $mode = strtoupper((string)($row['display_mode'] ?? 'DISTRIBUTED'));
        if (!in_array($mode, self::DISPLAY_MODES, true)) $mode = 'DISTRIBUTED';
        $amount = (float)($row['amount'] ?? 0);
        if ($amount < 0) $amount = 0;

        $db = new DB();
        $r = $db->insertUpdate(
            "INSERT INTO pricing_version_overheads
                (version_id, tender_id, line_no, category, label, amount, display_mode, notes,
                 created_by, created_by_name, created_on)
             VALUES
                (:v, :t, :ln, :c, :l, :a, :dm, :n, :cb, :cbn, NOW())",
            [
                ':v'   => $versionId,
                ':t'   => $tenderId,
                ':ln'  => $this->nextLineNo($versionId),
                ':c'   => $cat,
                ':l'   => $label,
                ':a'   => $amount,
                ':dm'  => $mode,
                ':n'   => (string)($row['notes'] ?? ''),
                ':cb'  => $userId,
                ':cbn' => $userName,
            ]
        );
        return ['ok' => true, 'id' => (int)($r['last_id'] ?? 0)];
    }

    public function update(int $id, array $row, int $userId): array
    {
        $cur = $this->find($id);
        if (!$cur) return ['ok' => false, 'msg' => 'Overhead line not found.'];

        $label = trim((string)($row['label'] ?? $cur['label']));
        if ($label === '') return ['ok' => false, 'msg' => 'Label is required.'];
        $cat = strtoupper((string)($row['category'] ?? $cur['category']));
        if (!in_array($cat, self::CATEGORIES, true)) $cat = $cur['category'];
        $mode = strtoupper((string)($row['display_mode'] ?? $cur['display_mode']));
        if (!in_array($mode, self::DISPLAY_MODES, true)) $mode = $cur['display_mode'];
        $amount = (float)($row['amount'] ?? $cur['amount']);
        if ($amount < 0) $amount = 0;

        $db = new DB();
        $db->insertUpdate(
            "UPDATE pricing_version_overheads
                SET category = :c, label = :l, amount = :a, display_mode = :dm, notes = :n,
                    edited_by = :eb, edited_on = NOW()
              WHERE c0d = :id",
            [
                ':c'  => $cat, ':l' => $label, ':a' => $amount, ':dm' => $mode,
                ':n'  => (string)($row['notes'] ?? $cur['notes']),
                ':eb' => $userId, ':id' => $id,
            ]
        );
        return ['ok' => true];
    }

    public function remove(int $id, int $userId): array
    {
        $cur = $this->find($id);
        if (!$cur) return ['ok' => false, 'msg' => 'Overhead line not found.'];
        $db = new DB();
        $db->insertUpdate(
            "UPDATE pricing_version_overheads SET deleted = 1, edited_by = :eb, edited_on = NOW() WHERE c0d = :id",
            [':eb' => $userId, ':id' => $id]
        );
        return ['ok' => true];
    }

    /**
     * Quick-load a preset template into a version (idempotent by label — skips duplicates).
     * $templateKey is one of GOODS_LOCAL / GOODS_IMPORT.
     */
    public function loadTemplate(int $versionId, int $tenderId, string $templateKey, int $userId, string $userName): array
    {
        $key = strtoupper($templateKey);
        if (!isset(self::TEMPLATES[$key])) return ['ok' => false, 'msg' => "Unknown template: $key"];
        $existingLabels = array_map(fn($r) => strtolower((string)$r['label']), $this->listFor($versionId));
        $added = 0; $skipped = 0;
        foreach (self::TEMPLATES[$key] as $row) {
            if (in_array(strtolower($row['label']), $existingLabels, true)) { $skipped++; continue; }
            $res = $this->add($versionId, $tenderId, $row + ['amount' => 0], $userId, $userName);
            if ($res['ok']) $added++;
        }
        return ['ok' => true, 'added' => $added, 'skipped' => $skipped, 'template' => $key];
    }

    /** Sum of amounts by display_mode. Used by TenderPricing::rollupVersion. */
    public function sumsByMode(int $versionId): array
    {
        $db = new DB();
        $rows = $db->mySelect(
            "SELECT display_mode, IFNULL(SUM(amount), 0) AS s
               FROM pricing_version_overheads
              WHERE version_id = :v AND deleted = 0
              GROUP BY display_mode",
            [':v' => $versionId]
        ) ?: [];
        $out = ['PRELIMS_LINE' => 0.0, 'DISTRIBUTED' => 0.0, 'INTERNAL_ONLY' => 0.0];
        foreach ($rows as $r) $out[$r['display_mode']] = (float)$r['s'];
        return $out;
    }
}
