<?php

declare(strict_types=1);

namespace App\Services;

final class BuildService
{
    /**
     * Prépare une construction.
     */
    public function build(array $configuration = []): array
    {
        $buildId = 'BUILD-' . date('Ymd-His');

        $buildRoot = BASE_PATH . '/storage/builds';
        $buildPath = $buildRoot . '/' . $buildId;

        if (!is_dir($buildRoot)) {
            mkdir($buildRoot, 0775, true);
        }

        mkdir($buildPath, 0775, true);
        mkdir($buildPath . '/package', 0775, true);
        mkdir($buildPath . '/logs', 0775, true);
        mkdir($buildPath . '/reports', 0775, true);

        /*
        |--------------------------------------------------------------------------
        | Copie des éléments du projet
        |--------------------------------------------------------------------------
        */

        $policy = require BASE_PATH . '/app/Config/BuildPolicy.php';

$items = $policy['include'];

        $excluded = $policy['exclude'];

foreach ($items as $item) {

    if (in_array($item, $excluded, true)) {
        continue;
    }

    $source = BASE_PATH . DIRECTORY_SEPARATOR . $item;
    $destination = $buildPath . '/package/' . $item;

    $this->copy($source, $destination, $excluded);
}

        /*
        |--------------------------------------------------------------------------
        | Manifest
        |--------------------------------------------------------------------------
        */

        $version = is_file(BASE_PATH . '/VERSION')
            ? trim(file_get_contents(BASE_PATH . '/VERSION'))
            : 'unknown';

        $manifest = [
            'build'         => $buildId,
            'version'       => $version,
            'status'        => 'created',
            'created_at'    => date('c'),
            'configuration' => $configuration,
        ];

        file_put_contents(
            $buildPath . '/manifest.json',
            json_encode(
                $manifest,
                JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES
            )
        );

        /*
        |--------------------------------------------------------------------------
        | Création du ZIP
        |--------------------------------------------------------------------------
        */

        $zipFile = sprintf(
            '%s/AXIOM_UPDATE_%s_%s.zip',
            $buildPath,
            $version,
            $buildId
        );

        $zip = new \ZipArchive();

        if ($zip->open(
            $zipFile,
            \ZipArchive::CREATE | \ZipArchive::OVERWRITE
        ) !== true) {
            throw new \RuntimeException(
                'Impossible de créer le package ZIP.'
            );
        }

        $this->zipDirectory(
            $buildPath . '/package',
            $zip
        );

        $zip->close();

        return [
            'success'       => true,
            'status'        => 'created',
            'build'         => $buildId,
            'message'       => 'Construction initialisée : ' . $buildId,
            'configuration' => $configuration,
            'started_at'    => date('Y-m-d H:i:s'),
        ];
    }

    /**
     * Copie récursive.
     */
   private function copy(
    string $source,
    string $destination,
    array $excluded = []
): void
{
    $relative = str_replace(
        '\\',
        '/',
        substr($source, strlen(BASE_PATH) + 1)
    );

    foreach ($excluded as $exclude) {

        if (
            $relative === $exclude ||
            str_starts_with($relative, $exclude . '/')
        ) {
            return;
        }
    }

    if (is_file($source)) {

        copy($source, $destination);

        return;
    }

    if (!is_dir($source)) {
        return;
    }

    if (!is_dir($destination)) {
        mkdir($destination, 0775, true);
    }

    foreach (scandir($source) as $file) {

        if ($file === '.' || $file === '..') {
            continue;
        }

        $this->copy(
            $source . DIRECTORY_SEPARATOR . $file,
            $destination . DIRECTORY_SEPARATOR . $file,
            $excluded
        );
    }
}

    /**
     * Ajoute récursivement un dossier dans un ZIP.
     */
    private function zipDirectory(
        string $directory,
        \ZipArchive $zip,
        string $inside = ''
    ): void {

        foreach (scandir($directory) as $file) {

            if ($file === '.' || $file === '..') {
                continue;
            }

            $path = $directory . DIRECTORY_SEPARATOR . $file;
            $entry = ltrim($inside . '/' . $file, '/');

            if (is_dir($path)) {

                $zip->addEmptyDir($entry);

                $this->zipDirectory(
                    $path,
                    $zip,
                    $entry
                );

            } else {

                $zip->addFile(
                    $path,
                    $entry
                );

            }
        }
    }
}