<?php

namespace MintyPHP\Tests\Architecture;

use PHPUnit\Framework\TestCase;

/**
 * Enforces GR-UI-LIST-EXPORT: every Grid.js list export must go through
 * the core primitive (no inline fputcsv, no hand-rolled headers, no
 * bespoke URL-building), and the export endpoint must share the data
 * endpoint's filter-schema so the download matches what the grid shows.
 *
 * Add new list exports to ListExportContractFiles::listExportRegistry().
 */
class ListExportContractTest extends TestCase
{
    use ListExportContractFiles;
    use ProjectFileAssertionSupport;

    public function testEveryRegisteredExportEndpointUsesTheCorePrimitive(): void
    {
        foreach ($this->listExportRegistry() as $entry) {
            $content = $this->readProjectFile($entry['endpoint']);

            $this->assertStringContainsString('Guard::requireLogin();', $content, $entry['endpoint']);
            $this->assertStringContainsString('exportRequireGetRequest();', $content, $entry['endpoint']);
            $this->assertStringContainsString('exportCapLimit(', $content, $entry['endpoint']);
            $this->assertStringContainsString('exportSendCsv(', $content, $entry['endpoint']);
            $this->assertStringContainsString('exportResolveFlavor(', $content, $entry['endpoint']);
            $this->assertStringContainsString('new ExportColumn(', $content, $entry['endpoint']);
            $this->assertStringContainsString(
                'MintyPHP\\Service\\Export\\ExportColumn',
                $content,
                $entry['endpoint']
            );
        }
    }

    public function testExportEndpointsBanHandRolledOutputPrimitives(): void
    {
        foreach ($this->listExportRegistry() as $entry) {
            $content = $this->readProjectFile($entry['endpoint']);

            $this->assertStringNotContainsString('fputcsv(', $content, $entry['endpoint']);
            $this->assertStringNotContainsString(
                "header('Content-Type: text/csv",
                $content,
                $entry['endpoint']
            );
            $this->assertStringNotContainsString(
                "header('Content-Disposition:",
                $content,
                $entry['endpoint']
            );
            $this->assertStringNotContainsString('php://output', $content, $entry['endpoint']);
        }
    }

    public function testExportAndDataEndpointsShareTheSameFilterSchema(): void
    {
        foreach ($this->listExportRegistry() as $entry) {
            $export = $this->readProjectFile($entry['endpoint']);
            $data = $this->readProjectFile($entry['data_endpoint']);

            $schemaDir = dirname($entry['filter_schema']);
            $endpointDir = dirname($entry['endpoint']);
            $dataEndpointDir = dirname($entry['data_endpoint']);
            $schemaRefFromExport = $this->relativeSchemaRef($endpointDir, $schemaDir);
            $schemaRefFromData = $this->relativeSchemaRef($dataEndpointDir, $schemaDir);

            $this->assertStringContainsString(
                "gridParseFiltersFromSchemaFile(__DIR__ . '" . $schemaRefFromExport . "/filter-schema.php'",
                $export,
                $entry['endpoint'] . ' must parse the shared filter-schema.php'
            );
            $this->assertStringContainsString(
                "gridParseFiltersFromSchemaFile(__DIR__ . '" . $schemaRefFromData . "/filter-schema.php'",
                $data,
                $entry['data_endpoint'] . ' must parse the same filter-schema.php as the export endpoint'
            );
            $this->assertFileExists(
                $this->projectRootPath() . '/' . $entry['filter_schema'],
                $entry['filter_schema']
            );
        }
    }

    public function testIndexTemplateIncludesExportDropdownAndUsesEndpointUrl(): void
    {
        foreach ($this->listExportRegistry() as $entry) {
            $template = $this->readProjectFile($entry['index_template']);

            $this->assertStringContainsString(
                "templatePath('partials/app-list-export-dropdown.phtml')",
                $template,
                $entry['index_template'] . ' must require the shared export dropdown partial'
            );
            $this->assertStringContainsString(
                "endpointUrl('" . $entry['route_path'] . "')",
                $template,
                $entry['index_template'] . ' must build exportUrl via endpointUrl(), not lurl()'
            );
            $this->assertStringNotContainsString(
                "'exportUrl' => lurl(",
                $template,
                $entry['index_template'] . ' must not use lurl() for exportUrl (use endpointUrl())'
            );
        }
    }

    public function testPageModuleInvokesInitListExportFromCore(): void
    {
        foreach ($this->listExportRegistry() as $entry) {
            $module = $this->readProjectFile($entry['page_module']);

            $this->assertMatchesRegularExpression(
                "#\\bimport\\s*\\{[^}]*\\binitListExport\\b[^}]*\\}\\s*from\\s*['\"](?:\\.\\./core|/js/core)/app-list-export\\.js['\"]#s",
                $module,
                $entry['page_module'] . ' must import initListExport from the core module'
            );
            $this->assertStringContainsString(
                'initListExport(',
                $module,
                $entry['page_module'] . ' must invoke initListExport({ gridConfig, exportUrl })'
            );
        }
    }

    public function testExportDropdownPartialRemainsZeroConfigAndReusesCoreClasses(): void
    {
        $partial = $this->readProjectFile('templates/partials/app-list-export-dropdown.phtml');
        $this->assertStringContainsString('data-list-export="csv"', $partial);
        $this->assertStringContainsString('data-list-export="excel"', $partial);
        $this->assertStringContainsString("<details class=\"dropdown app-list-export-dropdown\"", $partial);
        $this->assertStringNotContainsString('<script', $partial, 'partial must not ship inline JS');
        $this->assertStringNotContainsString('<style', $partial, 'partial must not ship inline CSS');
    }

    private function relativeSchemaRef(string $endpointDir, string $schemaDir): string
    {
        if ($endpointDir === $schemaDir) {
            return '';
        }
        // Schema lives inside the endpoint dir (typical: endpoint sits one level up).
        if (str_starts_with($schemaDir, $endpointDir . '/')) {
            return '/' . substr($schemaDir, strlen($endpointDir) + 1);
        }
        // Schema lives in a sibling directory (endpoint sits in same dir parent).
        $commonParent = dirname($endpointDir);
        if ($schemaDir === $commonParent) {
            return '/..';
        }
        if (str_starts_with($schemaDir, $commonParent . '/')) {
            return '/../' . substr($schemaDir, strlen($commonParent) + 1);
        }
        return '/' . $schemaDir;
    }
}
