<?php

declare(strict_types=1);

namespace Plugin\tnt_mcp\src\Mcp\Settings;

use JTL\Backend\Settings\Manager;
use JTL\DB\DbInterface;
use JTL\GeneralDataProtection\IpAnonymizer;
use JTL\Helpers\Request;
use JTL\L10n\GetText;
use JTL\Services\JTL\AlertServiceInterface;
use JTL\Smarty\JTLSmarty;

/**
 * The ONLY shop-adjacent shim in the settings bridge. Extends the shop's own
 * {@see Manager} so replaying `SectionFactory::getSection($id,$manager)->update(...)` sees an
 * object graph identical to an admin-form save — with one deliberate difference: it never
 * constructs an {@see \JTL\Backend\AdminAccount}. That constructor boots a *backend* session
 * (`Backend::getInstance()` + `Shop::setIsFrontend(false)`, see AdminAccount.php:57-58), which
 * would be wrong to trigger from the frontend MCP route (see SETTINGS_BRIDGE_DESIGN.md
 * "Constraint found").
 *
 * Consequence: this class never calls `parent::__construct()` (which requires an AdminAccount) —
 * it assigns the inherited protected properties itself and repeats only the GetText locale loads
 * the parent performs, so labels/descriptions still localize correctly.
 *
 * Because there is no AdminAccount, `addLog()` is overridden: `Manager::addLog()` needs a
 * `kAdminlogin` (Manager.php:62, via `$adminAccount->getID()`), which we don't have. Per Tim's
 * confirmed attribution decision (SETTINGS_BRIDGE_DESIGN.md §5), every MCP-driven write is
 * attributed to the MASTER admin (lowest `kAdminlogin`) with a `(via tnt_mcp)` marker appended to
 * `cAdminname` in the `teinstellungenlog` row. No dedicated `tnt_mcp` admin user is created — the
 * real actor (OAuth token / client) is already recorded in the plugin's own audit log.
 */
final class McpSettingsManager extends Manager
{
    public function __construct(
        DbInterface $db,
        JTLSmarty $smarty,
        GetText $getText,
        AlertServiceInterface $alertService,
    ) {
        $this->db           = $db;
        $this->smarty       = $smarty;
        $this->getText      = $getText;
        $this->alertService = $alertService;

        $getText->loadAdminLocale('configs/configs');
        $getText->loadConfigLocales(true, true);
    }

    /**
     * @inheritdoc
     *
     * Identical to {@see Manager::addLog()} (skip on null/unchanged value, redact `*_pass`
     * settings) except the `kAdminlogin` is resolved to the master admin instead of
     * `$this->adminAccount->getID()`, and `cAdminname` carries the `(via tnt_mcp)` marker.
     */
    public function addLog(string $setting, ?string $oldValue, ?string $newValue): void
    {
        if ($oldValue === null || $newValue === null || $oldValue === $newValue) {
            return;
        }
        if (\str_ends_with($setting, '_pass')) {
            $oldValue = '***';
            $newValue = '***';
        }

        // No AdminAccount available (see class docblock) — attribute to the master admin (lowest
        // kAdminlogin) instead of a session-bound admin id.
        $this->db->queryPrepared(
            'INSERT INTO teinstellungenlog (kAdminlogin, cAdminname, cIP, cEinstellungenName,
                               cEinstellungenWertAlt, cEinstellungenWertNeu, dDatum)
                SELECT tadminlogin.kAdminlogin, CONCAT(tadminlogin.cName, \' (via tnt_mcp)\'), :cIP,
                               :cEinstellungenName, :cEinstellungenWertAlt, :cEinstellungenWertNeu, NOW()
                FROM tadminlogin
                ORDER BY tadminlogin.kAdminlogin ASC
                LIMIT 1',
            [
                'cIP'                   => (new IpAnonymizer(Request::getRealIP()))->anonymize(),
                'cEinstellungenName'    => $setting,
                'cEinstellungenWertAlt' => $oldValue,
                'cEinstellungenWertNeu' => $newValue,
            ],
        );
    }
}
