<?php

declare(strict_types=1);

namespace Maestro\Core\Services;

use Illuminate\Support\Facades\File;
use Maestro\Core\Support\PackagePaths;
use Symfony\Component\Console\Output\ConsoleOutput;

/**
 * Syncs scaffolding files from vendor maestro/core stubs when the package ships
 * newer copies (e.g. after composer update). Runs from CoreServiceProvider during
 * console bootstrap (package:discover, artisan, etc.).
 *
 * Mirrors {@see Maestro\Core\Console\Commands\CoreInstallCommand} managed-only policy:
 * never overwrite customised host files lacking @maestro-core-managed (even when the stub is newer).
 */
final class AutoSyncPackageStubs
{
    /** Same banner as stub files managed by core:install. */
    private const MANAGED_BANNER = '@maestro-core-managed';

    /**
     * Stub path (under package stubs/) => path relative to Laravel project root.
     *
     * @var array<string, string>
     */
    private const SYNC_MAP = [
        'vite.config.js' => 'vite.config.js',
        'resources/css/app.css' => 'resources/css/app.css',
        'resources/js/app.js' => 'resources/js/app.js',
    ];

    /**
     * When the package stub is newer than the managed app copy (or the app copy is missing),
     * copy the stub into the project and print a console notice.
     *
     * @return list<string> Project-relative paths that were updated
     */
    public function syncIfStubsAreNewer(): array
    {
        if (function_exists('app') && app()->runningUnitTests()) {
            return [];
        }

        $updated = [];

        foreach (self::SYNC_MAP as $stubRelative => $projectRelative) {
            $source = PackagePaths::stubPath($stubRelative);
            $destination = base_path($projectRelative);

            if (! File::exists($source)) {
                continue;
            }

            $stubIsNewer = File::exists($destination)
                && File::lastModified($source) > File::lastModified($destination);

            if (! File::exists($destination)) {
                $shouldCopy = true;
            } elseif (! str_contains(File::get($destination), self::MANAGED_BANNER)) {
                $shouldCopy = false;
            } else {
                $shouldCopy = $stubIsNewer;
            }

            if (! $shouldCopy) {
                continue;
            }

            File::ensureDirectoryExists(dirname($destination));
            File::copy($source, $destination);
            touch($destination, File::lastModified($source));
            $updated[] = $projectRelative;
        }

        if ($updated !== [] && $this->appRunningInConsole()) {
            $this->writeNotice($updated);
        }

        return $updated;
    }

    private function appRunningInConsole(): bool
    {
        return function_exists('app') && app()->runningInConsole();
    }

    /**
     * @param  list<string>  $updated
     */
    private function writeNotice(array $updated): void
    {
        if ($updated === ['vite.config.js']) {
            $message = 'Maestro: vite.config.js updated — please restart your dev server and run: rm -rf node_modules/.vite';
        } else {
            $files = implode(', ', $updated);
            $message = 'Maestro: package stubs updated ('.$files.') — please restart your dev server and run: rm -rf node_modules/.vite';
        }

        (new ConsoleOutput)->writeln('<comment>'.$message.'</comment>');
    }
}
