<?php

namespace App\Console\Commands;

use App\Models\DpaVersion;
use App\Services\Legal\DpaRenderer;
use Illuminate\Console\Command;
use Illuminate\Support\Facades\Storage;

/**
 * Render the processing agreement and its annex, store them, put them in force.
 *
 * The documents are generated from the repository (resources/views/legal/dpa),
 * so a change to the wording is a change somebody can review in a diff — and the
 * company name, the sub-processors and the deletion deadlines come from the same
 * places the rest of the application reads them, which is what stops the
 * agreement drifting away from what the software does.
 *
 * The console's upload form still exists and is unchanged: a lawyer's revision
 * arrives as a PDF and becomes a version like any other. This command is for the
 * version this application maintains itself.
 */
class PublishProcessingAgreement extends Command
{
    protected $signature = 'clupilot:publish-dpa
        {version : What the document calls itself, e.g. 1.0}
        {--draft : Store it without putting it in force}';

    protected $description = 'Render the processing agreement and its annex, and put them in force';

    public function handle(DpaRenderer $renderer): int
    {
        $version = (string) $this->argument('version');

        if (DpaVersion::query()->where('version', $version)->exists()) {
            $this->error("Version {$version} already exists. Pick another name — two documents under one name makes every acceptance ambiguous.");

            return self::FAILURE;
        }

        $agreementPath = 'dpa/av-vertrag-'.$this->slug($version).'.pdf';
        $measuresPath = 'dpa/tom-'.$this->slug($version).'.pdf';

        // Visibility "public" is about the FILE MODE, not about the web: this
        // disk's root is outside the document root and nothing here becomes
        // reachable by URL. What it changes is 0755/0644 instead of 0700/0600 —
        // and that is the difference between a document the web process can read
        // and one it cannot.
        //
        // It matters because this command is normally run as root (an artisan
        // call in a container is root; PHP-FPM is www-data). The first run left
        // a directory only root could enter, the route's exists() check answered
        // false, and every link on the page 404'd with nothing in any log to say
        // why.
        Storage::disk('local')->put($agreementPath, $renderer->agreement($version), 'public');
        Storage::disk('local')->put($measuresPath, $renderer->measures($version), 'public');

        $row = DpaVersion::query()->create([
            'version' => $version,
            'agreement_path' => $agreementPath,
            'measures_path' => $measuresPath,
            // Publishing is the point of the command; --draft is for looking at
            // it first. Every customer who accepted an earlier version is
            // outstanding again from this moment.
            'published_at' => $this->option('draft') ? null : now(),
        ]);

        foreach ([$agreementPath, $measuresPath] as $path) {
            if (Storage::disk('local')->getVisibility($path) !== 'public') {
                $this->warn("{$path} is not readable by the web process. Run this as the user PHP runs as, or fix the mode — the page will 404 otherwise.");
            }
        }

        $this->info(($this->option('draft') ? 'Drafted' : 'Published').' version '.$row->version.'.');
        $this->line('  '.$agreementPath.' ('.number_format(strlen(Storage::disk('local')->get($agreementPath)) / 1024, 0).' KB)');
        $this->line('  '.$measuresPath.' ('.number_format(strlen(Storage::disk('local')->get($measuresPath)) / 1024, 0).' KB)');

        return self::SUCCESS;
    }

    private function slug(string $version): string
    {
        return preg_replace('/[^a-z0-9.-]+/i', '-', $version) ?: 'version';
    }
}
