<?php

declare(strict_types=1);

namespace Drupal\Tests\visitors\Kernel\Plugin\Block;

use Drupal\KernelTests\KernelTestBase;
use Symfony\Component\Yaml\Yaml;

require_once __DIR__ . '/../../../visitors.install';

/**
 * Tests the hook_update_30000() function.
 *
 * @group visitors
 */
class HookUpdate30000Test extends KernelTestBase {

  /**
   * {@inheritdoc}
   */
  protected $strictConfigSchema = FALSE;

  /**
   * {@inheritdoc}
   */
  protected static $modules = [
    'user',
    'visitors',
  ];

  /**
   * {@inheritdoc}
   */
  protected function setUp(): void {
    parent::setUp();

    $this->installSchema('visitors', ['visitors_counter']);
    $this->installSchema('user', ['users_data']);

    $block = $this->config('visitors.config');
    $block->setData($this->getLegacyConfig());
    $block->save();
  }

  /**
   * Tests the hook_update_30000() function.
   */
  public function testHookUpdate30000() {
    $sandbox = [];

    // Step 0: Migrate config from visitors.config to visitors.settings.
    visitors_update_30000($sandbox);
    $this->assertEquals(1, $sandbox['current']);
    $this->assertEquals(4, $sandbox['max']);
    $this->assertEquals(0.25, $sandbox['#finished']);

    // Verify config migration.
    $settings = $this->config('visitors.settings')->getRawData();
    $expected_settings = $this->getLegacyConfig();
    $this->assertEquals($expected_settings, $settings);

    // Verify old config is deleted (config object exists but data is empty)
    $legacy_config = $this->config('visitors.config');
    $this->assertEmpty($legacy_config->getRawData());

    // Step 1: Create database tables.
    visitors_update_30000($sandbox);
    $this->assertEquals(2, $sandbox['current']);
    $this->assertEquals(4, $sandbox['max']);
    $this->assertEquals(0.5, $sandbox['#finished']);

    // Verify tables are created.
    $schema = \Drupal::database()->schema();
    $this->assertTrue($schema->tableExists('visitors_visit'));
    $this->assertTrue($schema->tableExists('visitors_event'));

    // Step 2: Install configuration files.
    visitors_update_30000($sandbox);
    $this->assertEquals(3, $sandbox['current']);
    $this->assertEquals(4, $sandbox['max']);
    $this->assertEquals(0.75, $sandbox['#finished']);

    visitors_update_30000($sandbox);
    $this->assertEquals(4, $sandbox['current']);
    $this->assertEquals(4, $sandbox['max']);
    $this->assertEquals(1, $sandbox['#finished']);
  }

  /**
   * Tests that the hook update completes all steps without errors.
   */
  public function testHookUpdate30000CompletesWithoutErrors() {
    $sandbox = [];

    // Run through all steps and verify no errors occur.
    try {
      for ($i = 0; $i < 4; $i++) {
        visitors_update_30000($sandbox);
      }

      // Verify final state.
      $this->assertEquals(4, $sandbox['current']);
      $this->assertEquals(4, $sandbox['max']);
      $this->assertEquals(1, $sandbox['#finished']);

      // Verify the update completed successfully.
      $this->assertTrue($sandbox['#finished'] === 1, 'Update should complete successfully');
    }
    catch (\Exception $e) {
      $this->fail('Hook update should complete without errors: ' . $e->getMessage());
    }
  }

  /**
   * Tests the hook_update_30000() function with visitors_geoip module.
   */
  public function testHookUpdate30000WithGeoipModule() {
    // Install the visitors_geoip module first.
    $this->enableModules(['visitors_geoip']);

    $sandbox = [];

    // Run through all steps.
    for ($i = 0; $i < 4; $i++) {
      visitors_update_30000($sandbox);
    }

    // Verify final state.
    $this->assertEquals(4, $sandbox['current']);
    $this->assertEquals(4, $sandbox['max']);
    $this->assertEquals(1, $sandbox['#finished']);

  }

  /**
   * Tests the hook_update_30000() function with existing geoip view.
   */
  public function testHookUpdate30000WithExistingGeoipView() {
    // Create a mock geoip view configuration.
    $geoip_view_data = [
      'id' => 'visitors_visit',
      'label' => 'Visitors',
      'module' => 'visitors',
      'description' => 'Test visitors view',
      'tag' => 'visitors',
      'base_table' => 'visitors_visit',
      'base_field' => 'id',
      'display' => [
        'default' => [
          'id' => 'default',
          'display_title' => 'Default',
          'display_plugin' => 'default',
          'position' => 0,
          'display_options' => [],
        ],
      ],
    ];

    // Install the geoip view configuration.
    $this->config('views.view.visitors_geoip')
      ->setData($geoip_view_data)
      ->save();

    // Verify it exists before the update.
    $this->assertNotNull($this->config('views.view.visitors_geoip'));

    $sandbox = [];

    // Run through all steps.
    for ($i = 0; $i < 4; $i++) {
      visitors_update_30000($sandbox);
    }

    // Verify final state.
    $this->assertEquals(4, $sandbox['current']);
    $this->assertEquals(4, $sandbox['max']);
    $this->assertEquals(1, $sandbox['#finished']);

    // Verify the geoip view is deleted (config object exists but data is empty)
    $geoip_config = $this->config('views.view.visitors_geoip');
    $this->assertEmpty($geoip_config->getRawData());

  }

  /**
   * Tests the settings schema.
   */
  protected function getLegacyConfig(): array {
    $yaml = <<<YAML
flush_log_timer: 0
bot_retention_log: 0
items_per_page: 10
disable_tracking: false
track:
  userid: true
counter:
  enabled: true
  entity_types:
    - 'node'
  display_max_age: 3600
visibility:
  request_path_mode: 0
  request_path_pages: ""
  user_role_mode: 0
  user_role_roles: {}
  user_account_mode: 1
  exclude_user1: false
script_type: minified
YAML;

    $config_array = Yaml::parse($yaml);

    return $config_array;
  }

  /**
   * Tests the hook_update_30000() function with empty sandbox.
   */
  public function testHookUpdate30000WithEmptySandbox() {
    $sandbox = [];

    // Run the update.
    visitors_update_30000($sandbox);

    // Verify sandbox is properly initialized.
    $this->assertEquals(1, $sandbox['current']);
    $this->assertEquals(4, $sandbox['max']);
    $this->assertEquals(0.25, $sandbox['#finished']);
  }

  /**
   * Tests the hook_update_30000() function with partial sandbox.
   */
  public function testHookUpdate30000WithPartialSandbox() {
    $sandbox = [
      'current' => 2,
      'max' => 4,
    ];

    // Run the update.
    visitors_update_30000($sandbox);

    // Verify sandbox is properly updated.
    $this->assertEquals(3, $sandbox['current']);
    $this->assertEquals(4, $sandbox['max']);
    $this->assertEquals(0.75, $sandbox['#finished']);
  }

  /**
   * Tests the hook_update_30000() function completion.
   */
  public function testHookUpdate30000Completion() {
    $sandbox = [
      'current' => 3,
      'max' => 4,
    ];

    // Run the update.
    visitors_update_30000($sandbox);

    // Verify sandbox is completed.
    $this->assertEquals(4, $sandbox['current']);
    $this->assertEquals(4, $sandbox['max']);
    $this->assertEquals(1, $sandbox['#finished']);
  }

  /**
   * Tests that the hook update handles missing configuration files gracefully.
   */
  public function testHookUpdate30000HandlesMissingFiles() {
    $sandbox = [];

    // Run through all steps - should handle missing files gracefully.
    try {
      for ($i = 0; $i < 4; $i++) {
        visitors_update_30000($sandbox);
      }

      // Verify the update completes even with missing files.
      $this->assertEquals(4, $sandbox['current']);
      $this->assertEquals(4, $sandbox['max']);
      $this->assertEquals(1, $sandbox['#finished']);
    }
    catch (\Exception $e) {
      // If files are missing, the update should still complete
      // We're testing that it doesn't crash, not that it succeeds.
      $this->assertStringContainsString('file_get_contents', $e->getMessage());
    }
  }

}
