<?php

namespace App\Http\Controllers;

use App\Models\Central\Tenant;
use App\Services\AuditLog;
use App\Services\CurrentContext;
use App\Services\Tenancy\TestInstanceService;
use Filament\Notifications\Notification;
use Illuminate\Http\RedirectResponse;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\Session;

/**
 * Tenant-side "Sync from Live" endpoint, wired to the topbar button that the
 * tenant panel renders for test instances only.
 *
 * Hard guard: this controller serves NOTHING unless the request is bound to a
 * test-instance tenant (via IdentifyTenant) — a live tenant hitting the route
 * gets a plain 404, as does any host that isn't a tenant at all. So the route
 * can never refresh — let alone wipe — a live tenant's data.
 *
 * Session reality after the sync: every row the test instance owned is deleted
 * and re-cloned from the live tenant with fresh ids, so the acting user's id no
 * longer exists. The session is therefore settled explicitly:
 *
 *   - same-email match on the fresh data → log in as that replacement (keeps
 *     the tester signed in; the wipe is invisible to them apart from the
 *     reload);
 *   - no match (e.g. the account was never in the live tenant) → log out.
 *
 * Both branches run BEFORE the audit is recorded, so the recorded actor_id is
 * either the fresh replacement's id or null — never the wiped id that the
 * guard still held. The guard can not log in as a new user for us: it can only
 * invalidate the session, and the user.login listener (AuthEventsServiceProvider)
 * resolves its actor from the guard, which would hand it the wiped id while it
 * is still set.
 */
class TestInstanceSyncController
{
    public function sync(Request $request, TestInstanceService $service): RedirectResponse
    {
        $tenant = app(CurrentContext::class)->tenant();

        if (! $tenant instanceof Tenant || ! $tenant->isTestInstance()) {
            abort(404);
        }

        $live = $tenant->parentTenant;

        $result = $service->syncFromLive($tenant);

        $replacement = null;

        $email = $request->user()?->email;

        Auth::guard('web')->logout();
        $request->session()->regenerate();

        if ($email !== null && $email !== '') {
            $replacement = $tenant->users()->where('email', $email)->first();
        }

        if ($replacement !== null) {
            Auth::guard('web')->login($replacement);
        }

        if ($live !== null) {
            AuditLog::record('test_instance.synced', $live, $result, $replacement?->id, null);
        }

        Notification::make()
            ->title('Test instance synced')
            ->body(sprintf(
                'Copied %d products, %d customers, %d invoices, %d branches and %d users (%d rows) from %s in %ss.',
                $result['products'],
                $result['customers'],
                $result['invoices'],
                $result['branches'],
                $result['users'],
                $result['total_rows'],
                $live?->name ?? 'the live tenant',
                $result['seconds'],
            ))
            ->success()
            ->send();

        if ($replacement === null) {
            return redirect()->route('filament.app.auth.login');
        }

        return back();
    }

    /**
     * Tenant-side "Sync Products from Live": copies ONLY the product catalog
     * (categories, subcategories, products) from the live parent. Same hard
     * test-only guard as {@see self::sync()}. No session settlement is needed —
     * this merge never deletes users, so the acting user's row survives intact.
     */
    public function syncProducts(Request $request, TestInstanceService $service): RedirectResponse
    {
        $tenant = app(CurrentContext::class)->tenant();

        if (! $tenant instanceof Tenant || ! $tenant->isTestInstance()) {
            abort(404);
        }

        $live = $tenant->parentTenant;

        $result = $service->syncProductsFromLive($tenant, $request->user()?->id);

        if ($live !== null) {
            AuditLog::record('test_instance.products_synced', $live, $result, $request->user()?->id, null);
        }

        Notification::make()
            ->title('Products synced')
            ->body(sprintf(
                'Copied %d products (%d added, %d updated) from %s in %ss.',
                $result['products'],
                $result['added'],
                $result['updated'],
                $live?->name ?? 'the live tenant',
                $result['seconds'],
            ))
            ->success()
            ->send();

        return back();
    }
}
