<?php

namespace App\Services\License;

use Illuminate\Support\Facades\Storage;

/**
 * Read/write license JSON files.
 *
 * Each license is a single JSON file under
 * storage/app/{licenses_subdirectory}/. The filename combines the
 * license `type` (main_app or addon) with a `slug` so a workspace
 * with multiple addons can have one file per addon without
 * collisions.
 *
 * Stored shape:
 *   {
 *     "type":          "main_app" | "addon",
 *     "slug":          "...",                // 'mailtrixy' for main_app, addon dir name otherwise
 *     "purchase_code": "envato-uuid",
 *     "product_id":    "62602989",
 *     "domain":        "example.com",        // verified at install time
 *     "verified_at":   "ISO8601 UTC",
 *     "envato_buyer":  "envato-username"     // for support/audit only
 *   }
 *
 * No HMAC. The point of Phase 1 is just "did we ever verify this
 * purchase code?" — tampering protection comes in Phase 3 when (if)
 * we wire the remote check.
 */
class LicenseStorage
{
    /**
     * Persist a freshly-verified license. Overwrites any prior file
     * for the same type+slug so the customer can reinstall without
     * having to manually delete the old license file.
     *
     * Stored as pretty-printed JSON so a support agent can `cat`
     * the file on a customer's server and read it directly.
     */
    public function save(string $type, string $slug, array $data): string
    {
        $path = $this->path($type, $slug);

        Storage::disk('local')->put(
            $path,
            json_encode($data, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES),
        );

        return $path;
    }

    /**
     * Read a license file or null if it doesn't exist yet (= not
     * licensed). Callers treat null as "verification has never been
     * completed for this type+slug" — they should not assume "the
     * file was deleted by a malicious actor" because we have no way
     * to tell the two cases apart at this layer.
     */
    public function read(string $type, string $slug): ?array
    {
        $path = $this->path($type, $slug);

        if (!Storage::disk('local')->exists($path)) {
            return null;
        }

        $json = Storage::disk('local')->get($path);
        $data = json_decode((string) $json, true);

        return is_array($data) ? $data : null;
    }

    /**
     * True if a license file exists for this type+slug. Cheap probe
     * for the installer's `IS_INSTALLED` redirect logic — we don't
     * have to deserialise the file to know whether the customer has
     * ever passed the verification step.
     */
    public function exists(string $type, string $slug): bool
    {
        return Storage::disk('local')->exists($this->path($type, $slug));
    }

    /**
     * Delete the license file. Used during uninstall flows; not part
     * of the install path.
     */
    public function delete(string $type, string $slug): void
    {
        $path = $this->path($type, $slug);
        if (Storage::disk('local')->exists($path)) {
            Storage::disk('local')->delete($path);
        }
    }

    /**
     * Resolve the storage-disk-relative path for a license file.
     *
     * Slug is run through preg_replace to strip anything that isn't
     * `[a-z0-9_-]` so a path like `../etc/passwd` can't escape the
     * licenses directory. The installer always passes 'mailtrixy' for
     * the main app and addon directory names for addons (both already
     * safe), but this is belt-and-braces against future callers.
     */
    private function path(string $type, string $slug): string
    {
        $type = preg_replace('/[^a-z0-9_-]/i', '', $type) ?: 'unknown';
        $slug = preg_replace('/[^a-z0-9_-]/i', '', $slug) ?: 'unknown';

        $dir = (string) config('license.licenses_subdirectory', 'licenses');

        return "{$dir}/{$type}_{$slug}.json";
    }
}
