<?php

namespace Tests\Feature;

use Illuminate\Foundation\Testing\RefreshDatabase;
use Illuminate\Support\Facades\File;
use Tests\TestCase;

class ConfigMergeCommandTest extends TestCase
{
    protected $testConfigPath;
    protected $testConfigExamplePath;
    protected $backupDir;

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

        $this->testConfigPath = base_path('config/test-config.php');
        $this->testConfigExamplePath = base_path('config/test-config.php.example');
        $this->backupDir = storage_path('config-backups');

        // Clean up any existing test files
        $this->cleanupTestFiles();
    }

    protected function tearDown(): void
    {
        $this->cleanupTestFiles();
        parent::tearDown();
    }

    protected function cleanupTestFiles(): void
    {
        if (File::exists($this->testConfigPath)) {
            File::delete($this->testConfigPath);
        }
        if (File::exists($this->testConfigExamplePath)) {
            File::delete($this->testConfigExamplePath);
        }
        if (File::isDirectory($this->backupDir)) {
            $backups = File::glob("{$this->backupDir}/test-config.php.backup.*");
            foreach ($backups as $backup) {
                File::delete($backup);
            }
        }
    }

    /** @test */
    public function it_detects_new_keys_in_dry_run_mode()
    {
        // Create current config
        $currentConfig = [
            'existing_key' => 'existing_value',
            'nested' => [
                'old_key' => 'old_value',
            ],
        ];
        File::put($this->testConfigPath, "<?php\n\nreturn " . var_export($currentConfig, true) . ";\n");

        // Create example config with new keys
        $exampleConfig = [
            'existing_key' => 'example_value',
            'new_key' => 'new_value',
            'nested' => [
                'old_key' => 'old_value',
                'new_nested_key' => 'new_nested_value',
            ],
        ];
        File::put($this->testConfigExamplePath, "<?php\n\nreturn " . var_export($exampleConfig, true) . ";\n");

        // Run command with dry-run
        $this->artisan('config:merge', ['file' => 'test-config', '--dry-run' => true])
            ->expectsOutput('Found 2 new configuration key(s):')
            ->assertExitCode(0);

        // Verify original config unchanged
        $loadedConfig = include $this->testConfigPath;
        $this->assertEquals('existing_value', $loadedConfig['existing_key']);
        $this->assertArrayNotHasKey('new_key', $loadedConfig);
    }

    /** @test */
    public function it_merges_new_keys_while_preserving_existing_values()
    {
        // Create current config with custom values
        $currentConfig = [
            'existing_key' => 'CUSTOM_VALUE',
            'nested' => [
                'old_key' => 'CUSTOM_OLD_VALUE',
            ],
        ];
        File::put($this->testConfigPath, "<?php\n\nreturn " . var_export($currentConfig, true) . ";\n");

        // Create example config with new keys and different values
        $exampleConfig = [
            'existing_key' => 'example_value',
            'new_key' => 'new_value',
            'nested' => [
                'old_key' => 'example_old_value',
                'new_nested_key' => 'new_nested_value',
            ],
        ];
        File::put($this->testConfigExamplePath, "<?php\n\nreturn " . var_export($exampleConfig, true) . ";\n");

        // Run command with force flag
        $this->artisan('config:merge', ['file' => 'test-config', '--force' => true])
            ->assertExitCode(0);

        // Verify merge
        $mergedConfig = include $this->testConfigPath;

        // Existing values should be preserved
        $this->assertEquals('CUSTOM_VALUE', $mergedConfig['existing_key']);
        $this->assertEquals('CUSTOM_OLD_VALUE', $mergedConfig['nested']['old_key']);

        // New keys should be added
        $this->assertEquals('new_value', $mergedConfig['new_key']);
        $this->assertEquals('new_nested_value', $mergedConfig['nested']['new_nested_key']);
    }

    /** @test */
    public function it_creates_backup_before_merge()
    {
        // Create current config
        $currentConfig = ['existing_key' => 'value'];
        File::put($this->testConfigPath, "<?php\n\nreturn " . var_export($currentConfig, true) . ";\n");

        // Create example config
        $exampleConfig = ['existing_key' => 'value', 'new_key' => 'new_value'];
        File::put($this->testConfigExamplePath, "<?php\n\nreturn " . var_export($exampleConfig, true) . ";\n");

        // Run command
        $this->artisan('config:merge', ['file' => 'test-config', '--force' => true])
            ->assertExitCode(0);

        // Verify backup was created
        $backups = File::glob("{$this->backupDir}/test-config.php.backup.*");
        $this->assertCount(1, $backups);

        // Verify backup content matches original
        $backupContent = include $backups[0];
        $this->assertEquals($currentConfig, $backupContent);
    }

    /** @test */
    public function it_handles_deep_nested_arrays()
    {
        // Create current config with deep nesting
        $currentConfig = [
            'level1' => [
                'level2' => [
                    'level3' => [
                        'existing' => 'value',
                    ],
                ],
            ],
        ];
        File::put($this->testConfigPath, "<?php\n\nreturn " . var_export($currentConfig, true) . ";\n");

        // Create example config with new deep nested key
        $exampleConfig = [
            'level1' => [
                'level2' => [
                    'level3' => [
                        'existing' => 'example_value',
                        'new_deep' => 'deep_value',
                    ],
                    'new_level3' => 'value',
                ],
            ],
        ];
        File::put($this->testConfigExamplePath, "<?php\n\nreturn " . var_export($exampleConfig, true) . ";\n");

        // Run command
        $this->artisan('config:merge', ['file' => 'test-config', '--force' => true])
            ->assertExitCode(0);

        // Verify deep merge
        $mergedConfig = include $this->testConfigPath;
        $this->assertEquals('value', $mergedConfig['level1']['level2']['level3']['existing']);
        $this->assertEquals('deep_value', $mergedConfig['level1']['level2']['level3']['new_deep']);
        $this->assertEquals('value', $mergedConfig['level1']['level2']['new_level3']);
    }

    /** @test */
    public function it_reports_no_changes_when_config_is_up_to_date()
    {
        // Create identical configs
        $config = ['key' => 'value'];
        File::put($this->testConfigPath, "<?php\n\nreturn " . var_export($config, true) . ";\n");
        File::put($this->testConfigExamplePath, "<?php\n\nreturn " . var_export($config, true) . ";\n");

        // Run command
        $this->artisan('config:merge', ['file' => 'test-config', '--dry-run' => true])
            ->expectsOutput('  test-config: No new keys found')
            ->assertExitCode(0);
    }

    /** @test */
    public function it_handles_missing_example_file()
    {
        // Create only current config
        File::put($this->testConfigPath, "<?php\n\nreturn [];\n");

        // Run command (no example file)
        $this->artisan('config:merge', ['file' => 'test-config', '--dry-run' => true])
            ->expectsOutput('⊘ test-config: Example file not found (config/test-config.php.example)')
            ->assertExitCode(0);
    }

    /** @test */
    public function it_handles_missing_current_config()
    {
        // Create only example file
        File::put($this->testConfigExamplePath, "<?php\n\nreturn [];\n");

        // Run command (no current config)
        $this->artisan('config:merge', ['file' => 'test-config', '--dry-run' => true])
            ->expectsOutput('⊘ test-config: Active config not found (config/test-config.php) - run deployment first')
            ->assertExitCode(0);
    }

    /** @test */
    public function it_validates_merged_config_can_be_loaded()
    {
        // This test verifies the validation step
        // We can't easily test a failure case without causing actual errors
        // But we can verify success case

        $currentConfig = ['key' => 'value'];
        $exampleConfig = ['key' => 'value', 'new_key' => 'new_value'];

        File::put($this->testConfigPath, "<?php\n\nreturn " . var_export($currentConfig, true) . ";\n");
        File::put($this->testConfigExamplePath, "<?php\n\nreturn " . var_export($exampleConfig, true) . ";\n");

        // Run command
        $this->artisan('config:merge', ['file' => 'test-config', '--force' => true])
            ->assertExitCode(0);

        // Verify merged config can be loaded without errors
        $this->assertIsArray(include $this->testConfigPath);
    }

    /** @test */
    public function it_keeps_only_last_5_backups()
    {
        // Ensure backup directory exists
        if (!File::isDirectory($this->backupDir)) {
            File::makeDirectory($this->backupDir, 0755, true);
        }

        // Create 7 old backups manually
        for ($i = 1; $i <= 7; $i++) {
            $timestamp = date('Y-m-d_His', strtotime("-{$i} days"));
            $backupPath = "{$this->backupDir}/test-config.php.backup.{$timestamp}";
            File::put($backupPath, "<?php\n\nreturn ['backup' => {$i}];\n");
            // Adjust file modification time to make them appear older
            touch($backupPath, strtotime("-{$i} days"));
        }

        // Verify we have 7 backups
        $this->assertCount(7, File::glob("{$this->backupDir}/test-config.php.backup.*"));

        // Create configs and run merge (this will create 8th backup)
        File::put($this->testConfigPath, "<?php\n\nreturn ['key' => 'value'];\n");
        File::put($this->testConfigExamplePath, "<?php\n\nreturn ['key' => 'value', 'new' => 'val'];\n");

        $this->artisan('config:merge', ['file' => 'test-config', '--force' => true])
            ->assertExitCode(0);

        // Should now have only 5 backups (oldest 3 deleted)
        $remainingBackups = File::glob("{$this->backupDir}/test-config.php.backup.*");
        $this->assertCount(5, $remainingBackups);
    }

    /** @test */
    public function it_handles_array_values_correctly()
    {
        $currentConfig = [
            'array_key' => ['item1', 'item2'],
        ];
        $exampleConfig = [
            'array_key' => ['item1', 'item2'],
            'new_array' => ['new1', 'new2', 'new3'],
        ];

        File::put($this->testConfigPath, "<?php\n\nreturn " . var_export($currentConfig, true) . ";\n");
        File::put($this->testConfigExamplePath, "<?php\n\nreturn " . var_export($exampleConfig, true) . ";\n");

        $this->artisan('config:merge', ['file' => 'test-config', '--force' => true])
            ->assertExitCode(0);

        $mergedConfig = include $this->testConfigPath;
        $this->assertEquals(['item1', 'item2'], $mergedConfig['array_key']);
        $this->assertEquals(['new1', 'new2', 'new3'], $mergedConfig['new_array']);
    }

    /** @test */
    public function it_handles_boolean_and_null_values()
    {
        $currentConfig = [
            'bool_true' => true,
            'bool_false' => false,
            'null_value' => null,
        ];
        $exampleConfig = [
            'bool_true' => false, // Different value
            'bool_false' => true, // Different value
            'null_value' => 'not null', // Different value
            'new_bool' => true,
            'new_null' => null,
        ];

        File::put($this->testConfigPath, "<?php\n\nreturn " . var_export($currentConfig, true) . ";\n");
        File::put($this->testConfigExamplePath, "<?php\n\nreturn " . var_export($exampleConfig, true) . ";\n");

        $this->artisan('config:merge', ['file' => 'test-config', '--force' => true])
            ->assertExitCode(0);

        $mergedConfig = include $this->testConfigPath;

        // Existing values preserved
        $this->assertTrue($mergedConfig['bool_true']);
        $this->assertFalse($mergedConfig['bool_false']);
        $this->assertNull($mergedConfig['null_value']);

        // New values added
        $this->assertTrue($mergedConfig['new_bool']);
        $this->assertNull($mergedConfig['new_null']);
    }

    /** @test */
    public function it_handles_numeric_keys()
    {
        $currentConfig = [
            'indexed' => [0 => 'first', 1 => 'second'],
        ];
        $exampleConfig = [
            'indexed' => [0 => 'first', 1 => 'second', 2 => 'third'],
        ];

        File::put($this->testConfigPath, "<?php\n\nreturn " . var_export($currentConfig, true) . ";\n");
        File::put($this->testConfigExamplePath, "<?php\n\nreturn " . var_export($exampleConfig, true) . ";\n");

        $this->artisan('config:merge', ['file' => 'test-config', '--force' => true])
            ->assertExitCode(0);

        $mergedConfig = include $this->testConfigPath;
        $this->assertArrayHasKey(2, $mergedConfig['indexed']);
        $this->assertEquals('third', $mergedConfig['indexed'][2]);
    }
}
