<?php

namespace App\Http\Controllers;

use Illuminate\Http\Request;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Schema;
use Illuminate\Support\Facades\Storage;

class BackupController extends Controller
{
    public function index()
    {
        $backups = collect(Storage::files('backups'))
            ->filter(fn ($file) => str_ends_with($file, '.json'))
            ->map(fn ($file) => [
                'name' => basename($file),
                'path' => $file,
                'size' => Storage::size($file),
                'date' => Storage::lastModified($file),
            ])
            ->sortByDesc('date')
            ->values();

        return view('backup.index', compact('backups'));
    }

    public function download()
    {
        $payload = [
            'app' => config('app.name'),
            'created_at' => now()->toIso8601String(),
            'tables' => [],
        ];

        foreach ($this->tables() as $table) {
            $payload['tables'][$table] = DB::table($table)->get()->map(fn ($row) => (array) $row)->all();
        }

        Storage::makeDirectory('backups');
        $name = 'tradeerp-backup-'.now()->format('Ymd-His').'.json';
        Storage::put('backups/'.$name, json_encode($payload, JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE));

        return Storage::download('backups/'.$name);
    }

    public function restore(Request $request)
    {
        $data = $request->validate(['backup' => ['required', 'file', 'mimes:json,txt', 'max:51200']]);
        $payload = json_decode(file_get_contents($data['backup']->getRealPath()), true);

        if (! is_array($payload) || ! isset($payload['tables']) || ! is_array($payload['tables'])) {
            return back()->withErrors(['backup' => __('Invalid backup file.')]);
        }

        $allowedTables = $this->tables();
        $unknownTables = array_diff(array_keys($payload['tables']), $allowedTables);
        if ($unknownTables) {
            return back()->withErrors(['backup' => __('Backup contains tables that do not belong to this project.')]);
        }

        DB::transaction(function () use ($payload, $allowedTables) {
            Schema::disableForeignKeyConstraints();
            try {
                foreach (array_reverse($allowedTables) as $table) {
                    DB::table($table)->truncate();
                }
                foreach ($allowedTables as $table) {
                    foreach (array_chunk($payload['tables'][$table] ?? [], 250) as $rows) {
                        if ($rows) {
                            DB::table($table)->insert($rows);
                        }
                    }
                }
            } finally {
                Schema::enableForeignKeyConstraints();
            }
        });

        return back()->with('success', __('Backup restored successfully.'));
    }

    private function tables(): array
    {
        try {
            $tables = collect(DB::select("SHOW FULL TABLES WHERE Table_type = 'BASE TABLE'"))
                ->map(fn ($row) => array_values((array) $row)[0]);
        } catch (\Throwable) {
            $tables = collect(Schema::getTableListing());
        }

        return $tables
            ->map(fn ($table) => str_contains($table, '.') ? str($table)->after('.')->toString() : $table)
            ->reject(fn ($table) => in_array($table, ['migrations', 'jobs', 'job_batches', 'failed_jobs', 'cache', 'cache_locks', 'sessions'], true))
            ->values()
            ->all();
    }
}
