<?php

namespace Tests\Feature;

use Illuminate\Foundation\Testing\DatabaseTransactions;
use Mtc\Cms\Models\PageListItemData;
use Mtc\Plugins\CmsAuditLog\Src\AuditLogger;
use Mtc\Plugins\CmsAuditLog\Src\Models\AuditEntry;
use Tests\TestCase;

class CmsAuditLogTest extends TestCase
{
    use DatabaseTransactions;

    protected function setUp(): void
    {
        parent::setUp();

        // Simulate a logged-in content manager user (how the CMS admin
        // session exposes the actor during AJAX saves)
        $_SESSION['content_manager']['user_id'] = 4321;
        $_SESSION['content_manager']['username'] = 'Audit Tester';
    }

    protected function tearDown(): void
    {
        unset($_SESSION['content_manager']);

        parent::tearDown();
    }

    public function test_logger_writes_entry_with_actor_details(): void
    {
        AuditLogger::log(AuditEntry::ACTION_PAGE_UPDATED, 123, 'Test Page', [
            'changes' => ['title' => ['old' => 'Old title', 'new' => 'New title']],
        ]);

        $entry = AuditEntry::query()->orderByDesc('id')->first();

        $this->assertNotNull($entry);
        $this->assertSame(AuditEntry::ACTION_PAGE_UPDATED, $entry->action);
        $this->assertSame(123, (int) $entry->page_id);
        $this->assertSame('Test Page', $entry->page_title);
        $this->assertSame(4321, (int) $entry->admin_user_id);
        $this->assertSame('Audit Tester', $entry->admin_user_name);
        $this->assertSame('New title', $entry->details_array['changes']['title']['new']);
    }

    public function test_logger_respects_disabled_config(): void
    {
        config(['cms_audit_log.enabled' => false]);

        $before = AuditEntry::query()->count();
        AuditLogger::log(AuditEntry::ACTION_PAGE_UPDATED, 123, 'Test Page');

        $this->assertSame($before, AuditEntry::query()->count());

        config(['cms_audit_log.enabled' => true]);
    }

    public function test_long_values_are_truncated(): void
    {
        $value = str_repeat('a', AuditLogger::MAX_VALUE_LENGTH + 100);

        $truncated = AuditLogger::truncateValue($value);

        $this->assertStringEndsWith('... [truncated]', $truncated);
        $this->assertLessThan(mb_strlen($value), mb_strlen($truncated));
        $this->assertNull(AuditLogger::truncateValue(null));
        $this->assertSame('short', AuditLogger::truncateValue('short'));
    }

    public function test_content_field_update_is_logged_with_old_and_new_values(): void
    {
        $data = $this->makePageListItemData('Original content');

        $data->value = 'Changed content';
        $data->save();

        $entry = AuditEntry::query()
            ->where('action', AuditEntry::ACTION_CONTENT_UPDATED)
            ->orderByDesc('id')
            ->first();

        $this->assertNotNull($entry);
        $this->assertSame((int) $data->page_id, (int) $entry->page_id);
        $this->assertSame('Original content', $entry->details_array['old_value']);
        $this->assertSame('Changed content', $entry->details_array['new_value']);
        $this->assertSame(4321, (int) $entry->admin_user_id);
    }

    public function test_content_field_save_without_value_change_is_not_logged(): void
    {
        $data = $this->makePageListItemData('Same content');

        $before = AuditEntry::query()
            ->where('action', AuditEntry::ACTION_CONTENT_UPDATED)
            ->count();

        $data->order = 5;
        $data->save();

        $this->assertSame($before, AuditEntry::query()
            ->where('action', AuditEntry::ACTION_CONTENT_UPDATED)
            ->count());
    }

    public function test_rebinding_events_to_populated_dispatcher_does_not_stack_observers(): void
    {
        $data = $this->makePageListItemData('Original content');

        // The provider re-registers its listeners on every 'events' rebind.
        // Rebinding to the already-populated dispatcher must not stack the
        // observer, or every save would be audited twice.
        $this->app->instance('events', $this->app->make('events'));

        $before = AuditEntry::query()
            ->where('action', AuditEntry::ACTION_CONTENT_UPDATED)
            ->count();

        $data->value = 'Changed content';
        $data->save();

        $this->assertSame($before + 1, AuditEntry::query()
            ->where('action', AuditEntry::ACTION_CONTENT_UPDATED)
            ->count());
    }

    public function test_page_update_hook_logs_page_field_changes(): void
    {
        $old_page = new \stdClass();
        $old_page->id = 555;
        $old_page->title = 'Before title';
        $old_page->slug = 'before-slug';
        $old_page->published = 1;

        $page = clone $old_page;
        $page->title = 'After title';
        $page->type = 'default';

        \HooksAdapter::do_action('ContentManager/processUpdateRequestPageUpdate', [
            'request' => [],
            'page' => $page,
            'old_page' => $old_page,
            'history_page_id' => 0,
        ]);

        $entry = AuditEntry::query()
            ->where('action', AuditEntry::ACTION_PAGE_UPDATED)
            ->orderByDesc('id')
            ->first();

        $this->assertNotNull($entry);
        $this->assertSame(555, (int) $entry->page_id);
        $this->assertSame('After title', $entry->page_title);
        $this->assertSame('Before title', $entry->details_array['changes']['title']['old']);
        $this->assertSame('After title', $entry->details_array['changes']['title']['new']);
        $this->assertArrayNotHasKey('slug', $entry->details_array['changes']);
    }

    public function test_page_update_hook_skips_temp_preview_pages(): void
    {
        $page = new \stdClass();
        $page->id = 556;
        $page->title = 'Preview page';
        $page->type = 'temp';

        $before = AuditEntry::query()->count();

        \HooksAdapter::do_action('ContentManager/processUpdateRequestPageUpdate', [
            'request' => [],
            'page' => $page,
            'old_page' => null,
            'history_page_id' => 0,
        ]);

        $this->assertSame($before, AuditEntry::query()->count());
    }

    public function test_versioned_save_is_filed_under_live_page_id(): void
    {
        // Versioned saves turn the saved page into a history copy whose
        // draft_for_page_id points at the live page
        $page = new \stdClass();
        $page->id = 9001;
        $page->title = 'Versioned page';
        $page->type = 'history';
        $page->draft_for_page_id = 42;

        \HooksAdapter::do_action('ContentManager/processUpdateRequestPageUpdate', [
            'request' => [],
            'page' => $page,
            'old_page' => null,
            'history_page_id' => 9001,
        ]);

        $entry = AuditEntry::query()
            ->where('action', AuditEntry::ACTION_PAGE_UPDATED)
            ->orderByDesc('id')
            ->first();

        $this->assertNotNull($entry);
        $this->assertSame(42, (int) $entry->page_id);
        $this->assertSame(9001, (int) $entry->details_array['saved_page_id']);
    }

    public function test_admin_user_name_is_stored_encrypted_when_enabled(): void
    {
        if (!config('encryption.enabled')) {
            $this->markTestSkipped('Encryption is not enabled in this environment.');
        }

        AuditLogger::log(AuditEntry::ACTION_PAGE_UPDATED, 1, 'Encryption check');

        $entry = AuditEntry::query()->orderByDesc('id')->first();
        $raw = \Illuminate\Support\Facades\DB::table('cms_audit_log')
            ->where('id', $entry->id)
            ->value('admin_user_name');

        $this->assertSame('Audit Tester', $entry->admin_user_name);
        $this->assertNotSame('Audit Tester', $raw);
    }

    private function makePageListItemData(string $value): PageListItemData
    {
        $data = new PageListItemData();
        $data->page_id = 999999;
        $data->page_list_id = 999999;
        $data->page_list_item_id = 999999;
        $data->name = 'audit_test_field';
        $data->info = '';
        $data->type = 'text';
        $data->datatype = '';
        $data->value = $value;
        $data->datetime_value = date('Y-m-d H:i:s');
        $data->regex = '';
        $data->mime = '';
        $data->settings = '';
        $data->css_class = '';
        $data->options = '';
        $data->language = '';
        $data->uuid = '';
        $data->data1 = '';
        $data->data2 = '';
        $data->data3 = '';
        $data->save();

        return $data;
    }
}
