<?php

namespace MintyPHP\Tests\Architecture;

use PHPUnit\Framework\TestCase;
use RecursiveDirectoryIterator;
use RecursiveIteratorIterator;
use FilesystemIterator;

/**
 * Architecture test: verifies that no address-book-specific hardcodings remain
 * in the search infrastructure, layout context builder, or aside templates.
 *
 * Intentionally allowed in Core (not flagged):
 * - OperationsAuthorizationPolicy / UiCapabilityMap / UserAuthorizationPolicy (authorization — uses inline 'address_book.view' string)
 * - modules/addressbook/* (module-owned runtime, pages/assets/providers)
 * - i18n translation files
 * - Module directory (lib/Module/, modules/)
 * - Test files
 */
final class NoAddressBookHardcodingTest extends TestCase
{
    /**
     * Search infrastructure must not contain hardcoded address-book references.
     * The module's AddressBookSearchProvider is the sole source now.
     */
    public function testSearchInfrastructureHasNoAddressBookHardcoding(): void
    {
        $searchDir = dirname(__DIR__, 2) . '/lib/Support/Search';
        $hits = $this->grepDir($searchDir, '/address.book/i', ['*.php']);

        self::assertSame([], $hits, sprintf(
            "Search infrastructure still contains address-book hardcodings:\n%s",
            implode("\n", $hits)
        ));
    }

    /**
     * SearchConfig facade must not contain hardcoded address-book references
     * (module provider bridge handles it dynamically).
     */
    public function testSearchConfigHasNoAddressBookHardcoding(): void
    {
        $file = dirname(__DIR__, 2) . '/lib/Support/SearchConfig.php';
        $content = file_get_contents($file);
        self::assertIsString($content);

        self::assertStringNotContainsString('address-book', $content);
        self::assertStringNotContainsString('address_book', $content);
        self::assertStringNotContainsString('addressBook', $content);
    }

    /**
     * Layout context builder (appBuildLayoutNavContext) must not contain
     * hardcoded addressBook data assembly.
     */
    public function testLayoutContextHasNoHardcodedAddressBookBlock(): void
    {
        $file = dirname(__DIR__, 2) . '/lib/Support/helpers/app.php';
        $content = file_get_contents($file);
        self::assertIsString($content);

        // The function should not contain 'addressBook' as an array key in the $layoutNav builder
        // (the module LayoutContextProvider provides it dynamically)
        self::assertDoesNotMatchRegularExpression(
            "/'\s*addressBook\s*'\s*=>\s*\[/",
            $content,
            'appBuildLayoutNavContext() still has hardcoded addressBook array block'
        );
    }

    /**
     * Aside icon-bar template must not have hardcoded People tab.
     */
    public function testIconBarHasNoHardcodedPeopleTab(): void
    {
        $file = dirname(__DIR__, 2) . '/templates/partials/app-main-aside-icon-bar.phtml';
        $content = file_get_contents($file);
        self::assertIsString($content);

        self::assertStringNotContainsString('aside-tab-people', $content,
            'Icon-bar still has hardcoded People tab (should come from module slot)');
        self::assertStringNotContainsString('$canViewAddressBook', $content,
            'Icon-bar still references $canViewAddressBook variable');
        self::assertStringNotContainsString('$addressBookUrl', $content,
            'Icon-bar still references $addressBookUrl variable');
    }

    /**
     * Aside panel template must not have hardcoded People panel/search entries.
     */
    public function testAsideHasNoHardcodedPeoplePanel(): void
    {
        $file = dirname(__DIR__, 2) . '/templates/partials/app-main-aside.phtml';
        $content = file_get_contents($file);
        self::assertIsString($content);

        // The hardcoded People panel used id="aside-panel-people" with data-aside-panel="people"
        self::assertStringNotContainsString('id="aside-panel-people"', $content,
            'Aside still has hardcoded People panel element');
        // The hardcoded People panel extracted $canViewAddressBook as a standalone variable
        self::assertDoesNotMatchRegularExpression(
            '/^\$canViewAddressBook\s*=/m',
            $content,
            'Aside still defines $canViewAddressBook as a standalone variable'
        );
        // The hardcoded People panel extracted $peopleGroups as a standalone variable
        self::assertDoesNotMatchRegularExpression(
            '/^\$peopleGroups\s*=/m',
            $content,
            'Aside still defines $peopleGroups as a standalone variable'
        );
        self::assertStringNotContainsString(
            'data-search-key="address-book"',
            $content,
            'Aside still has static address-book search item markup'
        );
    }

    public function testCoreNoLongerContainsAddressBookServiceDirectory(): void
    {
        $coreServiceDir = dirname(__DIR__, 2) . '/lib/Service/AddressBook';
        self::assertDirectoryDoesNotExist(
            $coreServiceDir,
            'Addressbook service classes should live in modules/addressbook/lib, not in Core lib/Service'
        );
    }

    /**
     * @return list<string> Lines matching the pattern
     */
    private function grepDir(string $dir, string $pattern, array $extensions): array
    {
        $hits = [];
        if (!is_dir($dir)) {
            return $hits;
        }

        $iterator = new RecursiveIteratorIterator(
            new RecursiveDirectoryIterator($dir, FilesystemIterator::SKIP_DOTS),
            RecursiveIteratorIterator::LEAVES_ONLY
        );

        foreach ($iterator as $file) {
            $ext = $file->getExtension();
            $matchesExt = false;
            foreach ($extensions as $allowedExt) {
                if ('*.' . $ext === $allowedExt) {
                    $matchesExt = true;
                    break;
                }
            }
            if (!$matchesExt) {
                continue;
            }

            $lines = file($file->getPathname(), FILE_IGNORE_NEW_LINES);
            if ($lines === false) {
                continue;
            }
            foreach ($lines as $lineNum => $line) {
                if (preg_match($pattern, $line)) {
                    $relative = str_replace(dirname(__DIR__, 2) . '/', '', $file->getPathname());
                    $hits[] = sprintf('%s:%d: %s', $relative, $lineNum + 1, trim($line));
                }
            }
        }

        return $hits;
    }
}
