<?php

use App\Database\Seeds\DatabaseSeeder;
use App\Services\OtpMailer;
use App\Services\SupportService;
use CodeIgniter\Test\CIUnitTestCase;
use Config\Cookie;
use Config\Filters;
use Config\Routing;
use Config\Session;

/**
 * Production-critical configuration that must not regress.
 *
 * @internal
 */
final class ProductionReadinessTest extends CIUnitTestCase
{
    public function testCsrfRemainsEnabledOutsideTesting(): void
    {
        $source = (string) file_get_contents(APPPATH . 'Config/Filters.php');
        $this->assertStringContainsString("'csrf'", $source);
        $this->assertMatchesRegularExpression("/if \(ENVIRONMENT === 'testing'\)/", $source);
        $this->assertSame('testing', ENVIRONMENT);
        $this->assertNotContains('csrf', (new Filters())->globals['before']);
    }

    public function testAutoRoutingIsDisabledAndNoDebugRoutesAreDefined(): void
    {
        $this->assertFalse((new Routing())->autoRoute);
        $routes = (string) file_get_contents(APPPATH . 'Config/Routes.php');
        $this->assertStringNotContainsString('__hot-reload', $routes);
        $this->assertDoesNotMatchRegularExpression("/->get\\([^)]*(delete|complete)/", $routes);
        $this->assertStringContainsString("['filter' => 'auth']", $routes);
        $this->assertStringContainsString("get('logout'", $routes);
        $this->assertStringContainsString("['filter' => 'guest']", $routes);
    }

    public function testSessionPathCannotLiveUnderPublic(): void
    {
        $session = new Session();
        $save    = str_replace('\\', '/', $session->savePath);
        $public  = str_replace('\\', '/', rtrim(FCPATH, '/\\'));
        $this->assertStringContainsString('/writable/session', $save);
        $this->assertFalse(str_starts_with($save, $public . '/'));
    }

    public function testExampleEnvHasPlaceholdersNotMailboxProduction(): void
    {
        $env = (string) file_get_contents(ROOTPATH . 'env');
        $this->assertStringContainsString('CI_ENVIRONMENT = development', $env);
        $this->assertStringContainsString('app.appTimezone = UTC', $env);
        $this->assertStringContainsString('your_db_password', $env);
        $this->assertStringContainsString('encryption.key =', $env);
        $this->assertStringNotContainsString('hex2bin:', $env);
        $this->assertStringContainsString('email.protocol = mailbox', $env);
        $this->assertStringContainsString('Production MUST use protocol = smtp', $env);
        $this->assertStringNotContainsString('127.0.0.1:43123', (string) file_get_contents(APPPATH . 'Config/App.php'));
    }

    public function testGitignoreCoversSecretsAndRuntime(): void
    {
        $ignore = (string) file_get_contents(ROOTPATH . '.gitignore');
        $this->assertStringContainsString('.env', $ignore);
        $this->assertStringContainsString('/writable/session/*', $ignore);
        $this->assertStringContainsString('/writable/mailbox/*', $ignore);
        $this->assertStringContainsString('/public/null', $ignore);
    }

    public function testDatabaseSeederOnlySeedsSupportCategories(): void
    {
        $source = (string) file_get_contents(APPPATH . 'Database/Seeds/DatabaseSeeder.php');
        $this->assertStringContainsString('SupportCategorySeeder', $source);
        $this->assertSame(1, substr_count($source, '->call('));
        $this->assertSame(SupportService::APPROVED_TITLES, SupportService::APPROVED_TITLES);
        $this->assertStringNotContainsString('Home', $source);
        $this->assertTrue(class_exists(DatabaseSeeder::class));
    }

    public function testPriorityLabelsHaveASingleSource(): void
    {
        $appPhp = $this->phpSourcesUnder(APPPATH);
        $this->assertStringNotContainsString('Not Important or Urgent', $appPhp);
        $this->assertStringContainsString("'Not Important & Not Urgent'", (string) file_get_contents(APPPATH . 'Config/Tieasy.php'));
    }

    public function testMailboxProtocolIsRejectedInProduction(): void
    {
        $mailer = new OtpMailer();
        $this->assertTrue($mailer->mailboxAllowed('development'));
        $this->assertTrue($mailer->mailboxAllowed('testing'));
        $this->assertFalse($mailer->mailboxAllowed('production'));
    }

    public function testCookieConfigForcesSecureInProductionSource(): void
    {
        $source = (string) file_get_contents(APPPATH . 'Config/Cookie.php');
        $this->assertStringContainsString("ENVIRONMENT === 'production'", $source);
        $this->assertStringContainsString('$this->secure = true', $source);
        $cookie = new Cookie();
        $this->assertFalse($cookie->secure);
        $this->assertTrue($cookie->httponly);
        $this->assertSame('Lax', $cookie->samesite);
    }

    public function testNoStaticHashPeppersRemain(): void
    {
        $otp   = (string) file_get_contents(APPPATH . 'Services/OtpService.php');
        $login = (string) file_get_contents(APPPATH . 'Services/PersistentLoginService.php');
        $this->assertStringNotContainsString('tieasy-otp-pepper', $otp);
        $this->assertStringNotContainsString('tieasy-login-pepper', $login);
        $this->assertStringContainsString('encryption.key is not configured', $otp);
        $this->assertStringContainsString('encryption.key is not configured', $login);
    }

    public function testHamburgerUsesLogoutAndApprovedItems(): void
    {
        $layout = (string) file_get_contents(APPPATH . 'Views/layouts/mobile.php');
        $this->assertStringContainsString('>Logout</a>', $layout);
        $this->assertStringNotContainsString('Sign Out', $layout);
        $this->assertSame(1, substr_count($layout, 'Contact Support'));
        $this->assertSame(1, substr_count($layout, 'Category Manager'));
        $this->assertSame(1, substr_count($layout, 'id="hamburger-btn"'));
        $this->assertSame(3, substr_count($layout, 'class="nav-item'));
        $this->assertSame(1, substr_count($layout, 'class="nav-fab"'));
    }

    private function phpSourcesUnder(string $dir): string
    {
        $chunks = [];
        $it     = new RecursiveIteratorIterator(new RecursiveDirectoryIterator($dir));
        foreach ($it as $file) {
            if ($file->isFile() && str_ends_with($file->getFilename(), '.php')) {
                $chunks[] = (string) file_get_contents($file->getPathname());
            }
        }

        return implode("\n", $chunks);
    }
}
