<?php

declare(strict_types=1);

namespace Plugin\tnt_mcp\src\Mcp\Admin;

use JTL\DB\DbInterface;
use JTL\DB\ReturnType;

/**
 * Write-side companion to {@see TokenAuditOverview}: deletes revoke/remove entries shown on the
 * "Tokens & Audit" tab. The OAuth tables carry no DB-level foreign keys, so related rows are
 * cascaded here by hand inside a single call each. Access tokens are never referenced by their raw
 * value — the UI passes a SHA-256 handle (see {@see TokenAuditOverview::oauthTokens()}) which is
 * matched with MySQL SHA2(), so no secret is ever transmitted back from the browser.
 */
final class TokenAuditActions
{
    public function __construct(private readonly DbInterface $db)
    {
    }

    /** Delete a legacy static bearer token by its integer primary key. */
    public function deleteLegacyToken(int $kToken): int
    {
        if ($kToken <= 0) {
            return 0;
        }

        return $this->db->delete('tnt_mcp_token', 'kToken', $kToken);
    }

    /**
     * Delete an OAuth client and everything issued under it: auth codes, access tokens and the
     * refresh tokens chained to those access tokens.
     */
    public function deleteOauthClient(string $clientId): int
    {
        if ($clientId === '') {
            return 0;
        }

        $this->db->queryPrepared(
            'DELETE r FROM tnt_mcp_oauth_refresh_token r
               INNER JOIN tnt_mcp_oauth_access_token a ON a.cToken = r.cAccessTokenId
              WHERE a.cClientId = :cid',
            ['cid' => $clientId]
        );
        $this->db->delete('tnt_mcp_oauth_access_token', 'cClientId', $clientId);
        $this->db->delete('tnt_mcp_oauth_auth_code', 'cClientId', $clientId);

        return $this->db->delete('tnt_mcp_oauth_client', 'cClientId', $clientId);
    }

    /**
     * Delete a single OAuth access token (identified by the SHA-256 handle of its raw value) plus
     * any refresh token chained to it.
     */
    public function deleteOauthAccessTokenByRef(string $ref): int
    {
        if (!\preg_match('/^[0-9a-f]{64}$/', $ref)) {
            return 0;
        }

        $this->db->queryPrepared(
            'DELETE FROM tnt_mcp_oauth_refresh_token
              WHERE SHA2(cAccessTokenId, 256) = :ref',
            ['ref' => $ref]
        );

        // queryPrepared() is typed as the union of every ReturnType's result; with AFFECTED_ROWS it
        // is an int, so this only narrows the declared type — the fallback is unreachable.
        $affected = $this->db->queryPrepared(
            'DELETE FROM tnt_mcp_oauth_access_token
              WHERE SHA2(cToken, 256) = :ref',
            ['ref' => $ref],
            ReturnType::AFFECTED_ROWS
        );

        return \is_int($affected) ? $affected : 0;
    }
}
