<?php

namespace App\Filament\Central\Resources;

use App\Filament\Central\Resources\TestInstanceResource\Pages\EditTestInstance;
use App\Filament\Central\Resources\TestInstanceResource\Pages\ListTestInstances;
use App\Models\Central\Tenant;
use App\Services\Tenancy\TestInstanceService;
use Filament\Actions\Action;
use Filament\Actions\EditAction;
use Filament\Forms\Components\Select;
use Filament\Forms\Components\TextInput;
use Filament\Notifications\Notification;
use Filament\Resources\Resource;
use Filament\Schemas\Components\Section;
use Filament\Schemas\Schema;
use Filament\Support\Icons\Heroicon;
use Filament\Tables\Columns\TextColumn;
use Filament\Tables\Table;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Str;
use Illuminate\Validation\Rule;

/**
 * Master Control → Test Instances. Lists every test environment separately
 * from the live tenants so the two can never be confused. Test instances are
 * full {@see Tenant} rows (`environment = test`) with an explicit parent link;
 * they share the tenant application and are isolated by their own tenant_id.
 */
class TestInstanceResource extends Resource
{
    protected static ?string $model = Tenant::class;

    protected static string|\BackedEnum|null $navigationIcon = Heroicon::OutlinedBeaker;

    protected static ?string $navigationLabel = 'Test Instances';

    protected static ?string $modelLabel = 'test instance';

    protected static ?string $recordTitleAttribute = 'name';

    protected static ?int $navigationSort = 2;

    public static function getEloquentQuery(): Builder
    {
        return parent::getEloquentQuery()->testInstances();
    }

    public static function form(Schema $schema): Schema
    {
        return $schema->components([
            Section::make('Test Instance')
                ->description('An isolated copy of a live tenant. Changes made inside this environment never touch the live tenant.')
                ->schema([
                    TextInput::make('name')
                        ->label('Test Instance Name')
                        ->required()
                        ->maxLength(255)
                        ->helperText(fn (?Tenant $record): string => $record?->parentTenant?->name
                            ? sprintf('Copy of "%s" (%s).', $record->parentTenant->name, $record->parentTenant->slug)
                            : 'No parent tenant linked.'),
                    TextInput::make('slug')
                        ->label(config('tenancy.routing') === 'path' ? 'Test Workspace' : 'Test Subdomain')
                        ->required()
                        ->maxLength(63)
                        ->alphaDash()
                        ->rules([
                            fn (?Tenant $record) => Rule::unique('tenants', 'slug')->ignore($record?->id),
                            Rule::notIn(config('tenancy.reserved_slugs', [])),
                        ])
                        ->suffix(config('tenancy.routing') === 'path' ? '/app' : '.'.config('tenancy.app_domain'))
                        ->dehydrateStateUsing(fn (?string $state): string => Str::lower((string) $state)),
                    Select::make('status')
                        ->options(collect(Tenant::STATUSES)->mapWithKeys(
                            fn (string $status): array => [$status => Str::headline($status)],
                        )->all())
                        ->required(),
                ])->columns(2),
        ]);
    }

    public static function table(Table $table): Table
    {
        return $table
            ->columns([
                TextColumn::make('name')
                    ->label('Test Instance')
                    ->searchable()
                    ->sortable()
                    ->icon(fn (Tenant $record): ?string => $record->isSuspended() ? Heroicon::OutlinedPauseCircle->value : null),

                TextColumn::make('parentTenant.name')
                    ->label('Associated Tenant')
                    ->searchable()
                    ->sortable()
                    ->description(fn (Tenant $record): ?string => $record->parentTenant?->slug),

                TextColumn::make('parentTenant.slug')
                    ->label('Tenant / Subcategory')
                    ->badge()
                    ->color('gray')
                    ->toggleable(isToggledHiddenByDefault: false),

                TextColumn::make('slug')
                    ->label('Test Domain')
                    ->badge()
                    ->color('warning')
                    ->description(fn (Tenant $record): string => static::domainFor($record->slug)),

                TextColumn::make('status')
                    ->badge()
                    ->color(fn (?string $state): string => match ($state) {
                        Tenant::STATUS_ACTIVE => 'success',
                        Tenant::STATUS_TRIAL => 'info',
                        Tenant::STATUS_SUSPENDED => 'danger',
                        default => 'gray',
                    }),

                TextColumn::make('created_at')
                    ->label('Created')
                    ->date()
                    ->sortable()
                    ->toggleable(),

                TextColumn::make('last_synced_at')
                    ->label('Last Synced From Live')
                    ->dateTime('M j, Y g:i A')
                    ->placeholder('Never synced')
                    ->tooltip(fn (Tenant $record): ?string => $record->last_synced_at?->toRfc2822String())
                    ->sortable(),

                TextColumn::make('lastSyncAdmin.name')
                    ->label('Synced By')
                    ->placeholder('—')
                    ->toggleable(isToggledHiddenByDefault: true),
            ])
            ->defaultSort('created_at', 'desc')
            ->recordActions([
                EditAction::make()->label('Edit Configuration'),

                Action::make('open')
                    ->label('Open Test Instance')
                    ->icon(Heroicon::OutlinedArrowTopRightOnSquare)
                    ->url(fn (Tenant $record): string => TenantResource::tenantUrl($record))
                    ->openUrlInNewTab(),

                Action::make('impersonate')
                    ->label('Impersonate')
                    ->icon(Heroicon::OutlinedUserCircle)
                    ->requiresConfirmation()
                    ->modalDescription('Opens a support session inside this test instance as its administrator. The hand-off is written to their audit trail.')
                    ->action(fn (Tenant $record) => TenantResource::startImpersonation($record)),

                Action::make('syncFromLive')
                    ->label('Sync from Live')
                    ->icon(Heroicon::OutlinedArrowPath)
                    ->color('primary')
                    ->requiresConfirmation()
                    ->modalHeading('Sync Test Instance?')
                    ->modalIcon(Heroicon::OutlinedArrowPath)
                    ->modalDescription('This will refresh the test instance using the current live tenant data. Existing test-instance data may be overwritten. This action cannot be automatically undone.')
                    ->modalSubmitActionLabel('Sync from Live')
                    ->action(function (Tenant $record): void {
                        try {
                            $result = app(TestInstanceService::class)->syncFromLive(
                                $record,
                                Auth::guard('central')->id(),
                            );
                        } catch (\Throwable $e) {
                            Notification::make()
                                ->title('Sync failed')
                                ->body($e->getMessage())
                                ->danger()
                                ->persistent()
                                ->send();

                            return;
                        }

                        TenantResource::audit($record, 'test_instance.synced', static::auditContext($result));

                        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'],
                                $record->parentTenant?->name ?? 'the live tenant',
                                $result['seconds'],
                            ))
                            ->success()
                            ->persistent()
                            ->send();
                    }),

                Action::make('syncProductsFromLive')
                    ->label('Sync Products')
                    ->icon(Heroicon::OutlinedShoppingBag)
                    ->color('primary')
                    ->requiresConfirmation()
                    ->modalHeading('Sync Products?')
                    ->modalIcon(Heroicon::OutlinedShoppingBag)
                    ->modalDescription('This will refresh only the product catalog (categories, subcategories, products) in the test instance from the current live tenant data. No other test data is touched. This action cannot be automatically undone.')
                    ->modalSubmitActionLabel('Sync Products')
                    ->action(function (Tenant $record): void {
                        try {
                            $result = app(TestInstanceService::class)->syncProductsFromLive(
                                $record,
                                Auth::guard('central')->id(),
                            );
                        } catch (\Throwable $e) {
                            Notification::make()
                                ->title('Sync failed')
                                ->body($e->getMessage())
                                ->danger()
                                ->persistent()
                                ->send();

                            return;
                        }

                        TenantResource::audit($record, 'test_instance.products_synced', static::catalogAuditContext($result));

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

                Action::make('disable')
                    ->label('Disable')
                    ->color('danger')
                    ->requiresConfirmation()
                    ->modalDescription('The test instance will stop serving traffic until re-enabled. Its data is kept.')
                    ->visible(fn (Tenant $record): bool => ! $record->isSuspended())
                    ->action(function (Tenant $record): void {
                        $record->update(['status' => Tenant::STATUS_SUSPENDED]);
                        TenantResource::audit($record, 'test_instance.disabled');
                        Notification::make()->title("{$record->name} disabled.")->success()->send();
                    }),

                Action::make('resume')
                    ->label('Enable')
                    ->color('success')
                    ->requiresConfirmation()
                    ->visible(fn (Tenant $record): bool => $record->isSuspended())
                    ->action(function (Tenant $record): void {
                        $record->update(['status' => Tenant::STATUS_ACTIVE]);
                        TenantResource::audit($record, 'test_instance.enabled');
                        Notification::make()->title("{$record->name} enabled.")->success()->send();
                    }),

                Action::make('delete')
                    ->label('Delete')
                    ->icon(Heroicon::OutlinedTrash)
                    ->color('danger')
                    ->requiresConfirmation()
                    ->modalHeading('Delete test instance?')
                    ->modalIcon(Heroicon::OutlinedExclamationTriangle)
                    ->modalIconColor('danger')
                    ->modalDescription('This permanently deletes the test instance and every row it owns (products, invoices, customers, users, settings, anything created inside it). The live tenant is never touched. This cannot be undone.')
                    ->modalSubmitActionLabel('Delete test instance')
                    ->action(function (Tenant $record): void {
                        $live = $record->parentTenant;
                        $name = $record->name;

                        app(TestInstanceService::class)->destroy($record);

                        if ($live) {
                            TenantResource::audit($live, 'tenant.test_instance_deleted', ['test_name' => $name]);
                        }

                        Notification::make()->title("{$name} deleted.")->success()->send();
                    }),
            ]);
    }

    /**
     * Display form of a tenant URL for exactly this slug — a host label in
     * subdomain mode, the "/<slug>/app" path on the single host in path mode.
     */
    public static function domainFor(string $slug): string
    {
        return config('tenancy.routing') === 'path'
            ? url('/'.$slug.'/app')
            : $slug.'.'.config('tenancy.app_domain');
    }

    /**
     * @param  array<string, int|float>  $result
     * @return array<string, mixed>
     */
    private static function auditContext(array $result): array
    {
        return [
            'products' => $result['products'],
            'customers' => $result['customers'],
            'invoices' => $result['invoices'],
            'branches' => $result['branches'],
            'users' => $result['users'],
            'total_rows' => $result['total_rows'],
            'seconds' => $result['seconds'],
        ];
    }

    /**
     * @param  array<string, int|float>  $result
     * @return array<string, mixed>
     */
    private static function catalogAuditContext(array $result): array
    {
        return [
            'products' => $result['products'],
            'added' => $result['added'],
            'updated' => $result['updated'],
            'total_rows' => $result['total_rows'],
            'seconds' => $result['seconds'],
        ];
    }

    public static function getRelations(): array
    {
        return [];
    }

    public static function getPages(): array
    {
        return [
            'index' => ListTestInstances::route('/'),
            'edit' => EditTestInstance::route('/{record}/edit'),
        ];
    }
}
