<?php

namespace App\Http\Controllers\SystemSettings;

use App\Http\Controllers\Controller;
use App\Services\System\BackupManagementService;
use App\Services\SystemConfig\SystemConfigService;
use Exception;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Log;
use Illuminate\Support\Facades\Storage;
use Illuminate\Validation\Rule;
use Laravel\Socialite\Facades\Socialite;

class BackupRestoreController extends Controller
{
    protected $backupService;

    protected $systemConfig;

    public function __construct(
        BackupManagementService $backupService,
        SystemConfigService $systemConfig
    ) {
        $this->backupService = $backupService;
        $this->systemConfig = $systemConfig;
    }

    public function index(Request $request)
    {
        if ($request->wantsJson() || $request->ajax()) {
            try {
                session_write_close();
                $driver = $request->get('driver');

                return response()->json([
                    'success' => true,
                    'backups' => $this->backupService->getBackupList(),
                    'stats' => $this->backupService->getStorageStats($driver),
                ]);
            } catch (Exception $e) {
                return response()->json(['success' => false, 'message' => $e->getMessage()], 500);
            }
        }

        return view('pages.system_settings.backup-restore', [
            'settings' => $this->systemConfig->all(),
        ]);
    }

    public function create()
    {
        try {
            // Prevent session locking during the long-running backup process
            session_write_close();

            set_time_limit(0);
            ini_set('memory_limit', '512M');

            $this->backupService->createBackup();

            // ✍️ Log "Action Log" backup creation
            activity('backups')
                ->causedBy(auth()->user())
                ->withProperties([
                    'ip' => request()->ip(),
                    'agent' => request()->userAgent(),
                    'details' => __('Manually triggered a system and database backup.'),
                ])
                ->log(__('Generated System Backup'));

            return response()->json([
                'success' => true,
                'message' => __('Backup created successfully'),
                'backups' => $this->backupService->getBackupList(forceRefresh: true),
            ]);
        } catch (Exception $e) {
            Log::error('Backup failed: '.$e->getMessage());

            return response()->json(['success' => false, 'message' => $e->getMessage()], 500);
        }
    }

    private function allowedDisks(): array
    {
        return ['local', 's3', 'gdrive'];
    }

    public function download(Request $request)
    {
        $validated = $request->validate([
            'disk' => ['required', 'string', Rule::in($this->allowedDisks())],
            'path' => ['required', 'string'],
        ]);
        $disk = $validated['disk'];
        $path = $validated['path'];

        if (! Storage::disk($disk)->exists($path)) {
            abort(404, __('File not found'));
        }

        // ✍️ Log "Action Log" backup download
        activity('backups')
            ->causedBy(auth()->user())
            ->withProperties([
                'ip' => $request->ip(),
                'agent' => $request->userAgent(),
                'file' => $path,
                'disk' => $disk,
                'details' => __('Downloaded backup file: :file from :disk storage.', ['file' => $path, 'disk' => $disk]),
            ])
            ->log(__('Downloaded Backup File'));

        return Storage::disk($disk)->download($path);
    }

    public function destroy(Request $request)
    {
        try {
            session_write_close();
            $validated = $request->validate([
                'disk' => ['required', 'string', Rule::in($this->allowedDisks())],
                'path' => ['required', 'string'],
            ]);
            $disk = $validated['disk'];
            $path = $validated['path'];

            if (Storage::disk($disk)->exists($path)) {
                Storage::disk($disk)->delete($path);

                // ✍️ Log "Action Log" backup deletion
                activity('backups')
                    ->causedBy(auth()->user())
                    ->withProperties([
                        'ip' => $request->ip(),
                        'agent' => $request->userAgent(),
                        'file' => $path,
                        'details' => __('Permanently deleted backup file: :file', ['file' => $path]),
                    ])
                    ->log(__('Deleted Backup File'));
            }

            return response()->json([
                'success' => true,
                'message' => __('Backup deleted successfully'),
                'backups' => $this->backupService->getBackupList(forceRefresh: true),
            ]);
        } catch (Exception $e) {
            return response()->json(['success' => false, 'message' => $e->getMessage()], 500);
        }
    }

    public function restore(Request $request)
    {
        try {
            session_write_close();
            set_time_limit(0);
            ini_set('memory_limit', '512M');

            $validated = $request->validate([
                'disk' => ['required', 'string', Rule::in($this->allowedDisks())],
                'path' => ['required', 'string'],
            ]);
            $disk = $validated['disk'];
            $path = $validated['path'];

            $this->backupService->restoreBackup($disk, $path);

            // ✍️ Log "Action Log" system restore
            activity('backups')
                ->causedBy(auth()->user())
                ->withProperties([
                    'ip' => $request->ip(),
                    'agent' => $request->userAgent(),
                    'file' => $path,
                    'details' => __('Successfully restored system from backup: :file', ['file' => $path]),
                ])
                ->log(__('Restored System from Backup'));

            return response()->json([
                'message' => __('System restored successfully. Cache cleared.'),
            ]);
        } catch (Exception $e) {
            Log::error('Restore failed: '.$e->getMessage());

            return response()->json(['message' => $e->getMessage()], 500);
        }
    }

    public function testConnection()
    {
        try {
            $result = $this->backupService->testConnection();

            return response()->json($result);
        } catch (Exception $e) {
            return response()->json(['success' => false, 'message' => $e->getMessage()], 500);
        }
    }

    /**
     * Redirect to Google for authorization.
     */
    public function googleAuth()
    {
        $settings = $this->systemConfig->all();

        if (empty($settings['gdrive_client_id']) || empty($settings['gdrive_client_secret'])) {
            return redirect()->back()->with('error', __('Please save Client ID and Secret first.'));
        }

        // Dynamically set config for Socialite to use settings from database
        config([
            'services.google.client_id' => $settings['gdrive_client_id'],
            'services.google.client_secret' => $settings['gdrive_client_secret'],
            'services.google.redirect' => route('backup-restore.google-callback'),
        ]);

        return Socialite::driver('google')
            ->scopes(['https://www.googleapis.com/auth/drive.file'])
            ->with(['access_type' => 'offline', 'prompt' => 'consent'])
            ->redirect();
    }

    /**
     * Handle Google authorization callback.
     */
    public function googleCallback()
    {
        try {
            $settings = $this->systemConfig->all();

            config([
                'services.google.client_id' => $settings['gdrive_client_id'],
                'services.google.client_secret' => $settings['gdrive_client_secret'],
                'services.google.redirect' => route('backup-restore.google-callback'),
            ]);

            $user = Socialite::driver('google')->user();
            $refreshToken = $user->refreshToken;

            if (! $refreshToken) {
                return redirect()->route('backup-restore.index')->with('error', __('Failed to get refresh token. Please ensure you have not already authorized this app, or try revoking access and authorizing again.'));
            }

            // Save the refresh token to system settings
            $this->systemConfig->update(['gdrive_refresh_token' => $refreshToken]);

            return redirect()->route('backup-restore.index')->with('status', __('Google Drive authorized successfully!'));
        } catch (Exception $e) {
            Log::error('Google Auth Failed: '.$e->getMessage());

            return redirect()->route('backup-restore.index')->with('error', __('Authorization failed: ').$e->getMessage());
        }
    }
}
