<?php

declare(strict_types=1);

namespace PpPhp\Modules\Output\Controllers;

use PpPhp\Core\Config;
use PpPhp\Core\DB;
use PpPhp\Core\Security;
use PpPhp\Core\ProjectNav;
use PpPhp\Core\ModuleState;

class OutputController
{
    private array $project = [];
    private string $slug = '';

    public function __construct(
        private Config $config,
    ) {}

    public function index(string $slug): string
    {
        $this->initProject($slug);
        $project = $this->project;

        $outputState = ModuleState::load($slug, 'output');

        $navItems  = ProjectNav::items('/output');
        $baseUrl   = $this->config->get('base_url');
        $assetsUrl = $this->config->get('assets_url');
        $user      = $_SESSION['user'];
        $pageTitle = 'Output';

        $configJson = json_encode($outputState ?: $this->defaultConfig());

        ob_start();
        include __DIR__ . '/../Views/index.php';
        return ob_get_clean();
    }

    public function save(string $slug): void
    {
        header('Content-Type: application/json');
        $this->initProject($slug);
        \PpPhp\Core\Security::RequireCsrf(); // beacon carries the token via the keepalive-fetch shim

        $input = json_decode(file_get_contents('php://input'), true) ?? $_POST;
        $section = $input['section'] ?? '';
        $data    = $input['data'] ?? [];

        if ($section === '' || empty($data)) {
            http_response_code(400);
            echo json_encode(['success' => false, 'message' => 'Section and data are required.']);
            exit;
        }

        $state = ModuleState::load($slug, 'output');

        if ($section === 'all') {
            $state = $data;
        } else {
            $state[$section] = $data;
        }

        ModuleState::save($slug, 'output', $state);

        echo json_encode(['success' => true, 'message' => 'Output settings saved.']);
        exit;
    }

    public function load(string $slug): void
    {
        header('Content-Type: application/json');
        $this->initProject($slug);

        $state = ModuleState::load($slug, 'output');
        if (empty($state)) {
            $state = $this->defaultConfig();
        }

        echo json_encode(['success' => true, 'config' => $state]);
        exit;
    }

    /**
     * Live build information. The last-build figures come from the CENTRAL
     * build record (`BuildRecorder::latestInfo`) — the same source Finished and
     * Deploy read — so every module shows identical numbers. The output-dir
     * status (exists / writable / file count / size) is measured live.
     */
    public function info(string $slug): void
    {
        header('Content-Type: application/json');
        @set_time_limit(30);
        ini_set('display_errors', '0');
        $this->initProject($slug);

        $outputState   = ModuleState::load($slug, 'output') ?: [];
        $finishedState = ModuleState::load($slug, 'finished') ?: [];

        // Central, authoritative last-build record.
        $lastBuild = \PpPhp\Core\Deploy\BuildRecorder::latestInfo((int) ($this->project['id'] ?? 0));

        // Resolve the effective output directory the same way the build does.
        $configured = trim((string) ($outputState['general']['outputDir'] ?? ''));
        $resolved   = $configured;
        if (!empty($lastBuild['output_dir'])) {
            $resolved = $lastBuild['output_dir'];
        } elseif (!empty($finishedState['lastBuild']['outputDir'])) {
            $resolved = $finishedState['lastBuild']['outputDir'];
        }
        if ($resolved === '') {
            $resolved = constant('BASE_PATH') . '/output/' . $slug;
        } elseif (!str_starts_with($resolved, '/')) {
            $resolved = constant('BASE_PATH') . '/' . ltrim($resolved, '/');
        }

        $dir = [
            'configured' => $configured !== '',
            'path'       => $resolved,
            'exists'     => is_dir($resolved),
            'writable'   => is_dir($resolved) ? is_writable($resolved) : @is_writable(dirname($resolved)),
            'fileCount'  => 0,
            'bytes'      => 0,
            'sizeHuman'  => '',
        ];

        if ($dir['exists']) {
            [$count, $bytes] = $this->scanDir($resolved);
            $dir['fileCount'] = $count;
            $dir['bytes']     = $bytes;
            $dir['sizeHuman'] = $this->humanSize($bytes);
        }

        echo json_encode([
            'success' => true,
            'info'    => ['lastBuild' => $lastBuild, 'dir' => $dir],
        ]);
        exit;
    }

    /** Count files + total bytes under a directory (capped for safety). */
    private function scanDir(string $dir): array
    {
        $count = 0;
        $bytes = 0;
        try {
            $it = new \RecursiveIteratorIterator(
                new \RecursiveDirectoryIterator($dir, \FilesystemIterator::SKIP_DOTS)
            );
            foreach ($it as $f) {
                if (!$f->isFile()) {
                    continue;
                }
                $count++;
                $bytes += $f->getSize();
                if ($count >= 100000) {
                    break;
                }
            }
        } catch (\Throwable $e) {
            // Unreadable subtree — return what we have.
        }
        return [$count, $bytes];
    }

    private function humanSize(int $bytes): string
    {
        if ($bytes <= 0) {
            return '0 B';
        }
        $units = ['B', 'KB', 'MB', 'GB', 'TB'];
        $i = (int) floor(log($bytes, 1024));
        $i = max(0, min($i, count($units) - 1));
        return round($bytes / (1024 ** $i), $i ? 1 : 0) . ' ' . $units[$i];
    }

    private function initProject(string $slug): void
    {
        Security::RequireAuth();
        $this->slug = $slug;

        DB::Init();
        $project = DB::Select('projects', ['slug' => $slug, 'created_by' => Security::CurrentUserData()['id']]);
        if (empty($project) && Security::IsAdmin()) {
            $project = DB::Select('projects', ['slug' => $slug]);
        }
        if (empty($project)) {
            http_response_code(404);
            echo 'Project not found.';
            exit;
        }
        $this->project = $project[0];
        // Standalone (database-free) projects have no project DB — Output settings
        // and the build work from files only. Skip the project connection so it
        // doesn't fatal into a blank page (matches Finished/CustomFiles).
        if (\PpPhp\Core\ProjectDatabaseProfile::isStandaloneSlug($slug)) {
            return;
        }
        DB::Init($slug);
        DB::SetConnection($slug);
    }

    private function defaultConfig(): array
    {
        return [
            'general' => [
                'outputDir'       => '',
                'baseUrl'         => '',
                'previewUrl'      => '',
                'buildType'       => 'full',
                'cleanBeforeBuild' => false,
            ],
            'optimization' => [
                'compressJs'      => true,
                'compressCss'     => true,
                'compressHtml'    => false,
                'assetVersioning' => true,
                'minifyInline'    => false,
                'htmlDoctype'     => 'html5',
                'charset'         => 'utf-8',
            ],
            'advanced' => [
                'preBuildHook'     => '',
                'postBuildHook'    => '',
                'filePermissions'  => '0644',
                'lineEndings'      => 'lf',
                'errorReporting'   => 'development',
                'debugMode'        => false,
                'versionLabel'     => '',
                'generateHtaccess'  => true,
                'generateWebConfig' => false,
                'generateRobotsTxt' => false,
                'standaloneInstaller' => false,
            ],
        ];
    }
}
