<?php

namespace App\Filament\Exports;

use App\Models\Pet;
use Filament\Actions\Action;
use Filament\Actions\Exports\ExportColumn;
use Filament\Actions\Exports\Exporter;
use Filament\Actions\Exports\Models\Export;
use Filament\Notifications\Notification;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Support\Facades\Log;
use Illuminate\Support\Facades\Storage;
use Illuminate\Support\Number;
use ZipArchive;

class PetExporter extends Exporter
{
    protected static ?string $model = Pet::class;

    public static function getColumns(): array
    {
        return [
            ExportColumn::make('id')
                ->label('Pet ID'),
            ExportColumn::make('name')
                ->label('Name'),
            ExportColumn::make('slug')
                ->label('Slug'),
            ExportColumn::make('species.name')
                ->label('Species')
                ->formatStateUsing(fn ($state) => $state ?? 'N/A'),
            ExportColumn::make('breed.name')
                ->label('Breed')
                ->formatStateUsing(fn ($state) => $state ?? 'N/A'),
            ExportColumn::make('date_of_birth')
                ->label('Date of Birth')
                ->formatStateUsing(fn ($state) => $state?->format('Y-m-d') ?? 'N/A'),
            ExportColumn::make('age_in_years')
                ->label('Age')
                ->formatStateUsing(fn ($state) => $state ?? 'Unknown'),
            ExportColumn::make('gender')
                ->label('Gender'),
            ExportColumn::make('size')
                ->label('Size')
                ->formatStateUsing(fn ($state) => $state ?? 'N/A'),
            ExportColumn::make('color')
                ->label('Color')
                ->formatStateUsing(fn ($state) => $state ?? 'N/A'),
            ExportColumn::make('description')
                ->label('Description')
                ->formatStateUsing(fn ($state) => $state ?? 'N/A'),
            ExportColumn::make('medical_notes')
                ->label('Medical Notes')
                ->formatStateUsing(fn ($state) => $state ?? 'N/A'),
            ExportColumn::make('vaccination_status')
                ->label('Vaccinated')
                ->formatStateUsing(fn ($state) => $state ? 'Yes' : 'No'),
            ExportColumn::make('special_needs')
                ->label('Special Needs')
                ->formatStateUsing(fn ($state) => $state ? 'Yes' : 'No'),
            ExportColumn::make('intake_date')
                ->label('Intake Date')
                ->formatStateUsing(fn ($state) => $state?->format('Y-m-d') ?? 'N/A'),
            ExportColumn::make('status')
                ->label('Status'),
            ExportColumn::make('adoption_date')
                ->label('Adoption Date')
                ->formatStateUsing(fn ($state) => $state?->format('Y-m-d H:i:s') ?? 'N/A'),
            ExportColumn::make('created_at')
                ->label('Created At')
                ->formatStateUsing(fn ($state) => $state?->format('Y-m-d H:i:s')),
            ExportColumn::make('updated_at')
                ->label('Updated At')
                ->formatStateUsing(fn ($state) => $state?->format('Y-m-d H:i:s')),
            ExportColumn::make('image_count')
                ->label('Number of Images')
                ->state(fn (Pet $record): int => is_array($record->images) ? count($record->images) : 0),
        ];
    }

    public static function modifyQuery(Builder $query): Builder
    {
        return $query->with([
            'species',
            'breed',
        ]);
    }

    public static function getCompletedNotificationBody(Export $export): string
    {
        $body = 'Your pet export has completed and '.Number::format($export->successful_rows).' '.str('row')->plural($export->successful_rows).' exported.';

        if ($failedRowsCount = $export->getFailedRowsCount()) {
            $body .= ' '.Number::format($failedRowsCount).' '.str('row')->plural($failedRowsCount).' failed to export.';
        }

        // Create the pet images zip after export completes and send a separate notification
        try {
            Log::info('PetExporter: Attempting to create image zip', ['export_id' => $export->getKey()]);
            static::sendImageZipNotification($export);
            Log::info('PetExporter: Image zip notification sent successfully');
        } catch (\Exception $e) {
            Log::error('PetExporter: Failed to create/send image zip', [
                'export_id' => $export->getKey(),
                'error' => $e->getMessage(),
                'trace' => $e->getTraceAsString(),
            ]);
        }

        return $body;
    }

    protected static function sendImageZipNotification(Export $export): void
    {
        $zipFileName = static::createImageZipForExport($export);

        if (! $zipFileName) {
            return;
        }

        // Send a separate notification for the image zip
        $export->user->notify(
            Notification::make()
                ->title('Pet images ready')
                ->body('A zip file containing all pet images is ready for download.')
                ->icon('heroicon-o-photo')
                ->iconColor('success')
                ->actions([
                    Action::make('download_images')
                        ->label('Download Images Zip')
                        ->url(route('filament.admin.exports.download-images', [
                            'export' => $export->getKey(),
                            'fileName' => $zipFileName,
                        ]))
                        ->openUrlInNewTab()
                        ->markAsRead(),
                ])
                ->persistent()
                ->toDatabase()
        );
    }

    /**
     * @return list<int>
     */
    protected static function getExportedPetIdsFromCsv(Export $export): array
    {
        try {
            // Filament stores export CSVs in filament_exports/{id}/ directory
            $exportDirectory = "filament_exports/{$export->getKey()}";
            $csvFiles = Storage::disk($export->file_disk)->files($exportDirectory);

            // Filter for numbered CSV files (e.g., 0000000000000001.csv)
            $csvFiles = array_filter($csvFiles, function ($file) {
                return preg_match('/\/\d+\.csv$/', $file);
            });

            if (empty($csvFiles)) {
                Log::warning('PetExporter: No CSV files found in export directory', ['directory' => $exportDirectory]);

                return [];
            }

            $petIds = [];

            // Read all CSV chunk files
            foreach ($csvFiles as $csvFile) {
                $csvPath = Storage::disk($export->file_disk)->path($csvFile);

                if (! file_exists($csvPath)) {
                    continue;
                }

                $handle = fopen($csvPath, 'r');

                if ($handle === false) {
                    continue;
                }

                // Read each row and extract the pet ID (first column)
                while (($row = fgetcsv($handle)) !== false) {
                    if (! empty($row[0]) && is_numeric($row[0])) {
                        $petIds[] = (int) $row[0];
                    }
                }

                fclose($handle);
            }

            return array_unique($petIds);
        } catch (\Exception $e) {
            Log::error('PetExporter: Failed to read pet IDs from CSV', [
                'error' => $e->getMessage(),
            ]);

            return [];
        }
    }

    protected static function createImageZipForExport(Export $export): ?string
    {
        Log::info('PetExporter: Starting createImageZipForExport', ['export_id' => $export->getKey()]);

        // Read the exported pet IDs from the CSV file since static properties don't persist across queued jobs
        $exportedPetIds = static::getExportedPetIdsFromCsv($export);

        Log::info('PetExporter: Exported pet IDs', ['ids' => $exportedPetIds, 'count' => count($exportedPetIds)]);

        if (empty($exportedPetIds)) {
            Log::info('PetExporter: No exported pet IDs found, returning null');

            return null;
        }

        // Get only the pets that were exported and have images
        $pets = Pet::whereIn('id', $exportedPetIds)
            ->whereNotNull('images')
            ->get();

        Log::info('PetExporter: Found exported pets with images', ['count' => $pets->count()]);

        if ($pets->isEmpty()) {
            Log::info('PetExporter: No pets found, returning null');

            return null;
        }

        $hasImages = false;

        // Check if any pets have images
        foreach ($pets as $pet) {
            if (! empty($pet->images)) {
                $hasImages = true;
                break;
            }
        }

        Log::info('PetExporter: Checked if pets have images', ['hasImages' => $hasImages]);

        if (! $hasImages) {
            Log::info('PetExporter: No images found in pets, returning null');

            return null;
        }

        // Create a temporary zip file
        $zipFileName = 'pet-images-'.now()->format('Y-m-d-His').'.zip';
        $zipPath = storage_path('app/temp/'.$zipFileName);

        // Ensure temp directory exists
        if (! file_exists(storage_path('app/temp'))) {
            mkdir(storage_path('app/temp'), 0755, true);
        }

        $zip = new ZipArchive;

        if ($zip->open($zipPath, ZipArchive::CREATE | ZipArchive::OVERWRITE) !== true) {
            return null;
        }

        foreach ($pets as $pet) {
            if (empty($pet->images)) {
                continue;
            }

            // Create a folder for each pet using their slug or ID
            $petFolder = $pet->slug ?? 'pet-'.$pet->id;

            foreach ($pet->images as $index => $imagePath) {
                // Get the actual file path
                $fullPath = Storage::disk('public')->path($imagePath);

                if (file_exists($fullPath)) {
                    // Get file extension
                    $extension = pathinfo($imagePath, PATHINFO_EXTENSION);
                    $fileName = ($index + 1).'-'.$pet->slug.'.'.$extension;

                    // Add file to zip in pet's subfolder
                    $zip->addFile($fullPath, $petFolder.'/'.$fileName);
                }
            }
        }

        $zip->close();

        // Store the zip file in the export's directory
        $exportDirectory = "filament_exports/{$export->getKey()}";
        $finalPath = "{$exportDirectory}/{$zipFileName}";
        Storage::disk($export->file_disk)->put($finalPath, file_get_contents($zipPath));

        // Clean up temp file
        unlink($zipPath);

        return $zipFileName;
    }
}
