<?php
namespace boru\ai\Tests\Persistence\Db;

use boru\ai\Persistence\Db\DbConfig;
use boru\ai\Persistence\Db\DbConnection;
use boru\ai\Persistence\Db\DbPersistenceProvider;
use PHPUnit\Framework\TestCase;

class DbPersistencePruneTest extends TestCase
{
    public function testPruneConversationScopeCountsDependentsInDryRun()
    {
        $provider = new RecordingPruneProvider();

        $summary = $provider->prune(array(
            'before' => '2026-04-01 00:00:00',
            'scope' => 'conversations',
            'dry_run' => true,
        ));

        $this->assertSame('2026-04-01 00:00:00', $summary['before']);
        $this->assertSame(2, $summary['conversations']);
        $this->assertSame(3, $summary['turns']);
        $this->assertSame(4, $summary['tool_calls']);
        $this->assertSame(3, $summary['conversation_notes']);
        $this->assertSame(array(), $provider->deletedConversations);
        $this->assertSame(array(), $provider->deletedTurns);
        $this->assertSame(array(), $provider->deletedToolCalls);
    }

    public function testPruneTurnScopeDeletesTurnsAndDependentToolCalls()
    {
        $provider = new RecordingPruneProvider();

        $summary = $provider->prune(array(
            'before' => '2026-04-01 00:00:00',
            'scope' => 'turns',
            'dry_run' => false,
        ));

        $this->assertSame(0, $summary['conversations']);
        $this->assertSame(2, $summary['turns']);
        $this->assertSame(3, $summary['tool_calls']);
        $this->assertSame(array(11, 22), $provider->deletedTurns);
        $this->assertSame(array(), $provider->deletedConversations);
    }

    public function testPruneToolCallScopeDeletesOnlyToolCalls()
    {
        $provider = new RecordingPruneProvider();

        $summary = $provider->prune(array(
            'before' => '2026-04-01 00:00:00',
            'scope' => 'tool_calls',
            'dry_run' => false,
        ));

        $this->assertSame(0, $summary['conversations']);
        $this->assertSame(0, $summary['turns']);
        $this->assertSame(2, $summary['tool_calls']);
        $this->assertSame(array(111, 222), $provider->deletedToolCalls);
    }

    public function testPruneAllScopeAvoidsDoubleCountingNestedRows()
    {
        $provider = new RecordingPruneProvider();

        $summary = $provider->prune(array(
            'before' => '2026-04-01 00:00:00',
            'scope' => 'all',
            'dry_run' => true,
        ));

        $this->assertSame(2, $summary['conversations']);
        $this->assertSame(4, $summary['turns']);
        $this->assertSame(6, $summary['tool_calls']);
        $this->assertSame(3, $summary['conversation_notes']);
    }

    public function testPruneRejectsInvalidBeforeValue()
    {
        $this->expectException(\InvalidArgumentException::class);
        $this->expectExceptionMessage('Unable to parse prune before timestamp');

        $provider = new RecordingPruneProvider();
        $provider->prune(array(
            'before' => 'definitely-not-a-date',
            'scope' => 'conversations',
            'dry_run' => true,
        ));
    }
}

class RecordingPruneProvider extends DbPersistenceProvider
{
    public $deletedConversations = array();
    public $deletedTurns = array();
    public $deletedToolCalls = array();

    public function __construct()
    {
        parent::__construct(new DbConnection(new DbConfig()));
    }

    protected function listConversationRowIdsBefore($before)
    {
        return array(1, 2);
    }

    protected function summarizeConversationDeletion($conversationRowId)
    {
        if ($conversationRowId === 1) {
            return array(
                'conversations' => 1,
                'turns' => 2,
                'tool_calls' => 3,
                'conversation_notes' => 2,
            );
        }

        return array(
            'conversations' => 1,
            'turns' => 1,
            'tool_calls' => 1,
            'conversation_notes' => 1,
        );
    }

    protected function listTurnRowIdsForConversation($conversationRowId)
    {
        if ($conversationRowId === 1) {
            return array(101, 102);
        }

        return array(201);
    }

    protected function listTurnRowIdsBefore($before, array $excludeConversationRowIds = array())
    {
        if (!empty($excludeConversationRowIds)) {
            return array(33);
        }

        return array(11, 22);
    }

    protected function summarizeTurnDeletion($turnRowId)
    {
        return array(
            'turns' => 1,
            'tool_calls' => $turnRowId === 11 ? 2 : 1,
        );
    }

    protected function listToolCallRowIdsBefore($before, array $excludeTurnRowIds = array())
    {
        if (!empty($excludeTurnRowIds)) {
            return array(333);
        }

        return array(111, 222);
    }

    protected function deleteConversationRowById($conversationRowId)
    {
        $this->deletedConversations[] = $conversationRowId;
        return true;
    }

    protected function deleteTurnRowById($turnRowId)
    {
        $this->deletedTurns[] = $turnRowId;
        return true;
    }

    protected function deleteToolCallRowById($toolCallRowId)
    {
        $this->deletedToolCalls[] = $toolCallRowId;
        return true;
    }
}
