<?php

namespace Modules\SmartStorage\Http\Controllers;

use Illuminate\Http\Request;
use Illuminate\Routing\Controller;
use Illuminate\Support\Facades\Storage;
use Illuminate\Support\Str;
use Modules\SmartStorage\Entities\SsSetting;
use Modules\SmartStorage\Entities\Services\StorageDriverRegistry;

class SettingsController extends Controller
{
    protected function authorizeManage(): void
    {
        if (!auth()->user()->can('smart_storage.manage')) {
            abort(403, 'Unauthorized action.');
        }
    }

    public function index()
    {
        $this->authorizeManage();

        $settings = SsSetting::current();
        $backends = StorageDriverRegistry::all();
        $creds    = $settings->credentialsArray();

        $submodes = [
            'lan_only'         => 'Local network only (LAN)',
            'specific_devices' => 'Specific approved devices only',
            'tunnel'           => 'Reachable via internet tunnel (+ LAN)',
            'this_device_only' => 'This device only',
        ];

        return view('smartstorage::settings.index', compact('settings', 'backends', 'creds', 'submodes'));
    }

    public function update(Request $request)
    {
        $this->authorizeManage();

        $settings = SsSetting::current();

        $settings->active_backend     = $request->input('active_backend', 'laravel-default');
        $settings->encryption_enabled = $request->boolean('encryption_enabled');
        $settings->otp_enabled        = $request->boolean('otp_enabled');
        $settings->otp_ttl_minutes    = (int) $request->input('otp_ttl_minutes', 15);
        $settings->otp_channel        = $request->input('otp_channel', 'auto');
        $settings->local_submode      = $request->input('local_submode', 'this_device_only');
        $settings->lan_cidrs          = $request->input('lan_cidrs');
        $settings->tunnel_url         = $request->input('tunnel_url');
        $settings->geo_provider       = $request->input('geo_provider', 'none');
        $settings->track_reads        = $request->boolean('track_reads');
        $settings->track_writes       = $request->boolean('track_writes');

        // Rotate the bound device when switching to / re-binding this-device mode.
        if ($request->boolean('rebind_device')) {
            $settings->bound_device_fingerprint = null;
        }

        // Tunnel secret: only overwrite when a new value is provided.
        if ($request->filled('tunnel_secret')) {
            $settings->tunnel_secret = $request->input('tunnel_secret');
        }

        // Per-backend credentials. Empty password-style inputs are ignored so the
        // existing stored secret is preserved.
        $creds = $settings->credentialsArray();

        $creds['s3'] = array_filter([
            'key'      => $request->input('s3_key',    $creds['s3']['key']    ?? null),
            'secret'   => $request->filled('s3_secret') ? $request->input('s3_secret') : ($creds['s3']['secret'] ?? null),
            'region'   => $request->input('s3_region', $creds['s3']['region'] ?? null),
            'bucket'   => $request->input('s3_bucket', $creds['s3']['bucket'] ?? null),
            'endpoint' => $request->input('s3_endpoint', $creds['s3']['endpoint'] ?? null),
            'prefix'   => $request->input('s3_prefix', $creds['s3']['prefix'] ?? null),
        ], fn ($v) => $v !== null && $v !== '');

        $creds['dropbox'] = array_filter([
            'token'  => $request->filled('dropbox_token') ? $request->input('dropbox_token') : ($creds['dropbox']['token'] ?? null),
            'prefix' => $request->input('dropbox_prefix', $creds['dropbox']['prefix'] ?? null),
        ], fn ($v) => $v !== null && $v !== '');

        $creds['gdrive'] = array_filter([
            'client_id'      => $request->input('gdrive_client_id', $creds['gdrive']['client_id'] ?? null),
            'client_secret'  => $request->filled('gdrive_client_secret') ? $request->input('gdrive_client_secret') : ($creds['gdrive']['client_secret'] ?? null),
            'refresh_token'  => $request->filled('gdrive_refresh_token') ? $request->input('gdrive_refresh_token') : ($creds['gdrive']['refresh_token'] ?? null),
            'root_folder_id' => $request->input('gdrive_root_folder_id', $creds['gdrive']['root_folder_id'] ?? null),
        ], fn ($v) => $v !== null && $v !== '');

        $settings->setCredentialsArray($creds);
        $settings->save();
        SsSetting::flushCache();

        // Drop the resolved disk so the new backend takes effect this request.
        Storage::forgetDisk(config('smartstorage.target_disk', 'local'));

        return redirect()->route('smart-storage.index')
            ->with('status', ['success' => 1, 'msg' => 'Smart Storage settings saved.']);
    }

    /**
     * Validate the selected backend's config and do a write/read/delete round-trip.
     */
    public function testBackend(Request $request)
    {
        $this->authorizeManage();

        $key = $request->input('backend', SsSetting::current()->active_backend);
        $driver = StorageDriverRegistry::get($key);

        if (!$driver) {
            return response()->json(['success' => false, 'msg' => "Unknown backend: {$key}"]);
        }
        if (!$driver->isAvailable()) {
            return response()->json(['success' => false, 'msg' => "{$driver->label()} is not available on this server."]);
        }

        $errors = $driver->validateConfig(SsSetting::current());
        if (!empty($errors)) {
            return response()->json(['success' => false, 'msg' => implode('; ', $errors)]);
        }

        try {
            $adapter = $driver->makeAdapter(SsSetting::current());
            $fs = new \League\Flysystem\Filesystem($adapter);
            $probe = '_smartstorage_probe_' . Str::random(8) . '.txt';
            $fs->write($probe, 'ok');
            $ok = $fs->read($probe) === 'ok';
            $fs->delete($probe);

            return response()->json([
                'success' => $ok,
                'msg'     => $ok ? "{$driver->label()}: connection OK." : "{$driver->label()}: read-back mismatch.",
            ]);
        } catch (\Throwable $e) {
            return response()->json(['success' => false, 'msg' => $driver->label() . ': ' . $e->getMessage()]);
        }
    }
}
