#!/usr/bin/env php
<?php

use Composer\InstalledVersions;
use Illuminate\Support\Str;
use function Termwind\{ask, render};

require 'vendor/autoload.php';

render(<<<'HTML'
    <div>
        <div class="space-y-1">
            <div class="py-1 px-3 bg-amber-600 text-amber-50">
                <strong>Filament v3</strong> Upgrade
            </div>

            <div>
                Welcome to the <strong>Filament v3</strong> upgrade process!

                <br />

                This script will attempt to handle most of the breaking changes for you.

                <br />

                If you have any questions, please reach out to us on <a href="https://filamentphp.com/discord" class="underline font-bold">Discord</a> or <a href="https://github.com/filamentphp/filament/discussions/new?category=q-a" class="underline font-bold">GitHub</a>.
            </div>

            <div>
                To begin, please ensure that you are using a version control system such as Git.

                <br />

                We will make changes directly to your files, and you will need to be able to revert them if something goes wrong.

                <br />

                <strong>Please commit any changes you have made to your project before continuing.</strong>
            </div>
        </div>

        <br />
    </div>
HTML);

$isReady = strtolower(trim(ask(<<<HTML
    <span class="bg-amber-600 text-amber-50 mr-1">
        Are you ready to continue? y/n
    </span>
HTML))) === 'y';

if (! $isReady) {
    render(<<<HTML
        <p class="bg-rose-600 text-rose-50">
            Aborting upgrade process.
        </p>
    HTML);

    return;
}

render(<<<HTML
    <p class="bg-green-600 text-green-50">
        Starting upgrade...
    </p>
HTML);

if (file_exists('config/filament-support.php')) {
    unlink('config/filament-support.php');

    render(<<<HTML
        <p>
            Removed <strong>config/filament-support.php</strong>. All config files have been merged into <strong>config/filament.php</strong>.
        </p>
    HTML);
}

if (file_exists('config/forms.php')) {
    unlink('config/forms.php');

    render(<<<HTML
        <p>
            Removed <strong>config/forms.php</strong>. All config files have been merged into <strong>config/filament.php</strong>.
        </p>
    HTML);
}

if (file_exists('config/notifications.php')) {
    unlink('config/notifications.php');

    render(<<<HTML
        <p>
            Removed <strong>config/notifications.php</strong>. All config files have been merged into <strong>config/filament.php</strong>.
        </p>
    HTML);
}

if (file_exists('config/tables.php')) {
    unlink('config/tables.php');

    render(<<<HTML
        <p>
            Removed <strong>config/tables.php</strong>. All config files have been merged into <strong>config/filament.php</strong>.
        </p>
    HTML);
}

if (InstalledVersions::isInstalled('filament/filament')) {
    require 'create-filament-v3-panel-provider.php';

    render(<<<HTML
        <p>
            Created a panel provider.
        </p>
    HTML);
}

$hasIndividualFilamentPackagesInstalled = false;

foreach ([
    ...glob('resources/css/*.css'),
    ...glob('resources/css/**/*.css'),
] as $file) {
    $fileContents = file_get_contents($file);

    if (str_contains($fileContents, 'filament/forms/dist/module.esm.css')) {
        $hasIndividualFilamentPackagesInstalled = true;

        $fileContents = preg_replace('/.*filament\/forms\/dist\/module\.esm\.css.*\n/', '', $fileContents);

        render(<<<HTML
            <p>
                Removed path to the form builder CSS file in <strong>{$file}</strong>.
            </p>
        HTML);
    }

    file_put_contents($file, $fileContents);
}

foreach ([
    ...glob('resources/js/*.js'),
    ...glob('resources/js/**/*.js'),
] as $file) {
    $fileContents = file_get_contents($file);

    if (
        str_contains($fileContents, 'FormsAlpinePlugin') ||
        str_contains($fileContents, 'NotificationsAlpinePlugin')
    ) {
        $hasIndividualFilamentPackagesInstalled = true;

        render(<<<HTML
            <p>
                Removed Filament Alpine.js plugins from <strong>{$file}</strong>.
            </p>
        HTML);
    }

    $fileContents = preg_replace('/.*FormsAlpinePlugin.*\n/', '', $fileContents);
    $fileContents = preg_replace('/.*NotificationsAlpinePlugin.*\n/', '', $fileContents);

    file_put_contents($file, $fileContents);
}

if (file_exists('tailwind.config.js')) {
    $config = file_get_contents('tailwind.config.js');

    if (! str_contains($config, 'darkMode')) {
        $config = str_replace('module.exports = {', "module.exports = {\n    darkMode: 'class',", $config);

        file_put_contents('tailwind.config.js', $config);

        render(<<<HTML
            <p>
                Enabled class-based dark mode in <strong>tailwind.config.js</strong> to prevent potential design inconsistencies.
            </p>
        HTML);
    }
}

foreach ([
    ...glob('resources/views/*.blade.php'),
    ...glob('resources/views/**/*.blade.php'),
] as $file) {
    $fileContents = file_get_contents($file);

    if (str_contains($fileContents, 'x-filament::page')) {
        $fileContents = str_replace('x-filament::page', 'x-filament-panels::page', $fileContents);

        render(<<<HTML
            <p>
                Replaced <strong><x-filament::page></strong> with <strong><x-filament-panels::page></strong> in <strong>{$file}</strong>.
            </p>
        HTML);
    }

    if (str_contains($fileContents, 'x-filament::widget')) {
        $fileContents = str_replace('x-filament::widget', 'x-filament-widgets::widget', $fileContents);

        render(<<<HTML
            <p>
                Replaced <strong><x-filament::widget></strong> with <strong><x-filament-widgets::widget></strong> in <strong>{$file}</strong>.
            </p>
        HTML);
    }

    if ($hasIndividualFilamentPackagesInstalled) {
        if (
            str_contains($fileContents, '@livewireScripts') &&
            (! str_contains($fileContents, '@filamentScripts'))
        ) {
            $fileContents = str_replace('@livewireScripts', "@filamentScripts", $fileContents);

            render(<<<HTML
                <p>
                    Replaced <strong>@livewireScripts</strong> with <strong>@filamentScripts</strong> in <strong>{$file}</strong>.
                </p>
            HTML);
        }

        if (
            str_contains($fileContents, '@livewireStyles') &&
            (! str_contains($fileContents, '@filamentStyles'))
        ) {
            $fileContents = str_replace('@livewireStyles', "@filamentStyles", $fileContents);

            render(<<<HTML
                <p>
                    Replaced <strong>@livewireStyles</strong> with <strong>@filamentStyles</strong> in <strong>{$file}</strong>.
                </p>
            HTML);
        }
    }

    file_put_contents($file, $fileContents);
}

$getEnvValue = function (string $env, string $variable): ?string {
    preg_match("/^{$variable}=(.*)$/m", $env, $matches);

    $value = $matches[1] ?? null;

    if ($value === null) {
        return null;
    }

    $value = trim($value);

    if ($value === '') {
        return null;
    }

    return $value;
};

$hasEnvVariable = function (string $env, string $variable): bool {
    return preg_match("/^{$variable}=(.*)$/m", $env) === 1;
};

$removeEnvVariable = function (string &$env, string $variable): string {
    return $env = preg_replace("/^{$variable}=(.*)$/m", '', $env);
};

$renameEnvVariable = function (string &$env, string $oldVariable, string $newVariable): string {
    return $env = preg_replace("/^{$oldVariable}=(.*)$/m", "{$newVariable}=$1", $env);
};

$setEnvVariable = function (string &$env, string $variable, string $value): string {
    return $env = preg_replace("/^{$variable}=(.*)$/m", "{$variable}={$value}", $env);
};

foreach (['.env', '.env.ci', '.env.example', '.env.testing'] as $envFile) {
    if (file_exists($envFile)) {
        $env = file_get_contents($envFile);

        $env = (function (string $env) use ($envFile, $getEnvValue, $hasEnvVariable, $removeEnvVariable, $renameEnvVariable, $setEnvVariable) {
            $disk = $getEnvValue($env, 'FILAMENT_FILESYSTEM_DRIVER') ??
                $getEnvValue($env, 'FORMS_FILESYSTEM_DRIVER') ??
                $getEnvValue($env, 'TABLES_FILESYSTEM_DRIVER');

            if ($disk === null) {
                $removeEnvVariable($env, 'FILAMENT_FILESYSTEM_DRIVER');
                $removeEnvVariable($env, 'FORMS_FILESYSTEM_DRIVER');
                $removeEnvVariable($env, 'TABLES_FILESYSTEM_DRIVER');

                return $env;
            }

            render(<<<HTML
                <p>
                    Replacing Filament <strong>FILESYSTEM_DRIVER</strong> .env variables with a single <strong>FILAMENT_FILESYSTEM_DISK</strong> in {$envFile}.
                </p>
            HTML);

            if ($hasEnvVariable($env, 'FILAMENT_FILESYSTEM_DRIVER')) {
                $renameEnvVariable($env, 'FILAMENT_FILESYSTEM_DRIVER', 'FILAMENT_FILESYSTEM_DISK');
                $setEnvVariable($env, 'FILAMENT_FILESYSTEM_DISK', $disk);

                $removeEnvVariable($env, 'FORMS_FILESYSTEM_DRIVER');
                $removeEnvVariable($env, 'TABLES_FILESYSTEM_DRIVER');

                return $env;
            }

            if ($hasEnvVariable($env, 'FORMS_FILESYSTEM_DRIVER')) {
                $renameEnvVariable($env, 'FORMS_FILESYSTEM_DRIVER', 'FILAMENT_FILESYSTEM_DISK');
                $setEnvVariable($env, 'FILAMENT_FILESYSTEM_DISK', $disk);

                $removeEnvVariable($env, 'TABLES_FILESYSTEM_DRIVER');

                return $env;
            }

            if ($hasEnvVariable($env, 'TABLES_FILESYSTEM_DRIVER')) {
                $renameEnvVariable($env, 'TABLES_FILESYSTEM_DRIVER', 'FILAMENT_FILESYSTEM_DISK');
                $setEnvVariable($env, 'FILAMENT_FILESYSTEM_DISK', $disk);

                return $env;
            }

            return $env;
        })($env);

        file_put_contents($envFile, $env);
    }
}

if (file_exists('resources/views/vendor/filament/brand.blade.php')) {
    rename(
        'resources/views/vendor/filament/brand.blade.php',
        'resources/views/vendor/filament-panels/logo.blade.php',
    );

    render(<<<HTML
        <p>
            Renamed <strong>brand.blade.php</strong> to <strong>logo.blade.php</strong>.
        </p>
    HTML);
}

$appDirectory = $argv[1] ?? 'app';

render(<<<HTML
    <p>
        Start processing <strong>/{$appDirectory}</strong> to fix code affected by breaking changes.
    </p>
HTML);

$rectorScriptPath = implode(DIRECTORY_SEPARATOR, ['vendor', 'bin', 'rector']);

exec("{$rectorScriptPath} process {$appDirectory} --config vendor/filament/upgrade/src/rector.php --clear-cache");

render(<<<HTML
    <p class="pt-2">
        Finished processing <strong>/{$appDirectory}</strong>.
    </p>
HTML);

$requireCommands = [];

foreach (json_decode(file_get_contents('composer.json'), true)['require'] as $package => $version) {
    if ($package === 'filament/upgrade') {
        continue;
    }

    if (! str_starts_with($package, 'filament/')) {
        continue;
    }

    $requireCommands[] = "composer require {$package}:\"^3.2\" -W --no-update";
}

$requireCommands = implode("</strong><br /><strong>", $requireCommands);

render(<<<HTML
    <div>
        <span class="bg-green-600 text-green-50">
            Now you're ready to update your Composer dependencies!
        </span>

        <br /> <br />

        First require new versions of Filament packages:

        <br />

        <strong>{$requireCommands}</strong>

        <br /> <br />

        If you have any 3rd party plugins that need to be upgraded, you should bump those dependencies as well.

        <br /> <br />

        And then run:

        <br />

        <strong>composer update</strong>

        <br /> <br />

        If you have any questions, please reach out to us on <a href="https://filamentphp.com/discord" class="underline font-bold">Discord</a> or <a href="https://github.com/filamentphp/filament/discussions/new?category=q-a" class="underline font-bold">GitHub</a>.
    </div>
HTML);
