<?php

namespace App\Filament\Resources;

use App\Filament\Exports\InvoiceOutValidationExporter;
use App\Filament\Resources\InvoiceOutValidationResource\Pages;
use App\Models\InvoiceOutValidation;
use App\Models\Plant;
use Carbon\Carbon;
use DB;
use Filament\Facades\Filament;
use Filament\Forms;
use Filament\Forms\Components\FileUpload;
use Filament\Forms\Components\Section;
use Filament\Forms\Form;
use Filament\Notifications\Notification;
use Filament\Resources\Resource;
use Filament\Tables;
use Filament\Tables\Actions\ExportAction;
use Filament\Tables\Table;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Database\Eloquent\SoftDeletingScope;
use Maatwebsite\Excel\Facades\Excel;
use PhpOffice\PhpSpreadsheet\Shared\Date as ExcelDate;
use Storage;

class InvoiceOutValidationResource extends Resource
{
    protected static ?string $model = InvoiceOutValidation::class;

    protected static ?string $navigationIcon = 'heroicon-o-rectangle-stack';

    protected static ?string $navigationGroup = 'Invoice Management';

    public static function form(Form $form): Form
    {
        return $form
            ->schema([
                Section::make('')
                    ->schema([
                        Forms\Components\Select::make('plant_id')
                            ->label('Plant')
                            ->relationship('plant', 'name')
                            ->required(),
                        Forms\Components\TextInput::make('qr_code')
                            ->label('QR Code'),
                        Forms\Components\DateTimePicker::make('scanned_at')
                            ->label('Scanned At'),
                        Forms\Components\TextInput::make('scanned_by')
                            ->label('Scanned By'),
                        Forms\Components\Hidden::make('created_by')
                            ->label('Created By')
                            ->default(Filament::auth()->user()?->name),
                        Forms\Components\Hidden::make('updated_by')
                            ->label('Updated By')
                            ->default(Filament::auth()->user()?->name),
                    ])
                    ->columns(4),
            ]);
    }

    public static function table(Table $table): Table
    {
        return $table
            ->columns([
                Tables\Columns\TextColumn::make('No.')
                    ->label('No.')
                    ->alignCenter()
                    ->getStateUsing(function ($record, $livewire, $column, $rowLoop) {
                        $paginator = $livewire->getTableRecords();
                        $perPage = method_exists($paginator, 'perPage') ? $paginator->perPage() : 10;
                        $currentPage = method_exists($paginator, 'currentPage') ? $paginator->currentPage() : 1;

                        return ($currentPage - 1) * $perPage + $rowLoop->iteration;
                    }),
                Tables\Columns\TextColumn::make('plant.code')
                    ->label('Plant')
                    ->alignCenter()
                    ->searchable()
                    ->sortable(),
                Tables\Columns\TextColumn::make('qr_code')
                    ->label('QR Code')
                    ->alignCenter()
                    ->searchable()
                    ->sortable(),
                Tables\Columns\TextColumn::make('scanned_at')
                    ->label('Scanned At')
                    ->searchable()
                    ->alignCenter()
                    ->dateTime()
                    ->sortable(),
                Tables\Columns\TextColumn::make('scanned_by')
                    ->label('Scanned By')
                    ->searchable()
                    ->alignCenter()
                    ->sortable(),
                Tables\Columns\TextColumn::make('created_at')
                    ->label('Created At')
                    ->dateTime()
                    ->searchable()
                    ->alignCenter()
                    ->sortable(),
                // ->toggleable(isToggledHiddenByDefault: true),
                Tables\Columns\TextColumn::make('created_by')
                    ->label('Created By')
                    ->searchable()
                    ->alignCenter()
                    ->sortable(),
                Tables\Columns\TextColumn::make('updated_at')
                    ->label('Updated At')
                    ->dateTime()
                    ->searchable()
                    ->alignCenter()
                    ->sortable()
                    ->toggleable(isToggledHiddenByDefault: true),
                Tables\Columns\TextColumn::make('updated_by')
                    ->label('Updated By')
                    ->searchable()
                    ->alignCenter()
                    ->sortable()
                    ->toggleable(isToggledHiddenByDefault: true),
                Tables\Columns\TextColumn::make('deleted_at')
                    ->dateTime()
                    ->alignCenter()
                    ->sortable()
                    ->toggleable(isToggledHiddenByDefault: true),
            ])
            ->filters([
                Tables\Filters\TrashedFilter::make(),
            ])
            ->actions([
                Tables\Actions\ViewAction::make(),
                Tables\Actions\EditAction::make(),
            ])
            ->bulkActions([
                Tables\Actions\BulkActionGroup::make([
                    Tables\Actions\DeleteBulkAction::make(),
                    Tables\Actions\ForceDeleteBulkAction::make(),
                    Tables\Actions\RestoreBulkAction::make(),
                ]),
            ])
            ->headerActions([
                Tables\Actions\Action::make('Import Invoice Out Data')
                    ->label('Import Invoice Out Data')
                    ->form([
                        FileUpload::make('invoice_data_file')
                            ->label('Invoice Out Data')
                            // ->required()
                            ->preserveFilenames()
                            ->storeFiles(false)
                            ->reactive()
                            ->required()
                            ->disk('local')
                            // ->visible(fn (Get $get) => !empty($get('plant_id')))
                            ->directory('uploads/temp'),
                    ])
                    ->action(function (array $data) {
                        $uploadedFile = $data['invoice_data_file'];

                        $disk = Storage::disk('local');

                        $user = Filament::auth()->user();

                        $operatorName = $user->name;

                        // Get original filename
                        $originalName = $uploadedFile->getClientOriginalName(); // e.g. 3RA0018732.xlsx

                        $originalNameOnly = pathinfo($originalName, PATHINFO_FILENAME);

                        // Store manually using storeAs to keep original name
                        $path = $uploadedFile->storeAs('uploads/temp', $originalName, 'local'); // returns relative path

                        $fullPath = Storage::disk('local')->path($path);

                        if ($fullPath && file_exists($fullPath)) {
                            $rows = Excel::toArray(null, $fullPath)[0];

                            if ((count($rows) - 1) <= 0) {
                                Notification::make()
                                    ->title('Records Not Found')
                                    ->body("Import the valid 'Invoice Data' file to proceed..!")
                                    ->danger()
                                    ->send();

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

                                return;
                            }

                            $invalidPlantCode = [];
                            $invalidPlaCoFound = [];
                            $invalidqrCode = [];
                            $invalidScannedAt = [];
                            $invalidScannedBy = [];
                            $invalidUser = [];
                            $userNotFound = [];
                            $seenPlantQr = [];
                            $duplicateQrExcel = [];
                            // $duplicateQrDb = [];

                            foreach ($rows as $index => $row) {
                                if ($index == 0) {
                                    continue;
                                }

                                $qrCode = trim($row[1]);
                                $plantCode = trim($row[2]);
                                $scannedAt = trim($row[3]);
                                $scannedby = trim($row[4]);

                                if (empty($plantCode)) {
                                    $invalidPlantCode[] = "Row {$index}";
                                }
                                if (empty($qrCode)) {
                                    $invalidqrCode[] = "Row {$index}";
                                }
                                if (empty($scannedAt)) {
                                    $invalidScannedAt[] = "Row {$index}";
                                }
                                if (empty($scannedby)) {
                                    $invalidScannedBy[] = "Row {$index}";
                                }

                                if (strlen($plantCode) < 4) {
                                    $invalidPlantCode[] = $plantCode;
                                } elseif (! Plant::where('code', $plantCode)->first()) {
                                    $invalidPlaCoFound[] = $plantCode;
                                }

                                $plant = Plant::where('code', $plantCode)->first();

                                $plantId = $plant->id;

                                $uniqueKey = $plantCode.'_'.$qrCode;

                                if (in_array($uniqueKey, $seenPlantQr)) {
                                    $duplicateQrExcel[] = "Duplicate in file at Row {$index}: Document Number '{$qrCode}' already exists for Plant '{$plant->name}'";
                                }

                                $seenPlantQr[] = $uniqueKey;

                                // $existsInDb = InvoiceOutValidation::where('plant_id', $plantId)
                                //     ->where('qr_code', $qrCode)
                                //     ->first();

                                // if ($existsInDb) {
                                //     $duplicateQrDb[] = "Document Numbers '{$qrCode}' already exists in DB for Plant '{$plant->name}'";
                                // }
                            }

                            if (! empty($invalidqrCode) || ! empty($invalidScannedAt) || ! empty($invalidScannedBy) || ! empty($invalidUser)) {
                                $errorMsg = '';

                                if (! empty($invalidqrCode)) {
                                    $errorMsg .= 'Missing Qr code in rows: '.implode(', ', $invalidqrCode).'<br>';
                                }
                                if (! empty($invalidScannedAt)) {
                                    $errorMsg .= 'Missing Scanned At in rows: '.implode(', ', $invalidScannedAt).'<br>';
                                }
                                if (! empty($invalidScannedBy)) {
                                    $errorMsg .= 'Missing Scanned By in rows: '.implode(', ', $invalidScannedBy).'<br>';
                                }

                                Notification::make()
                                    ->title('Missing Mandatory Fields')
                                    ->body($errorMsg)
                                    ->danger()
                                    ->send();

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

                                return;
                            }

                            if (! empty($invalidPlantCode)) {
                                $invalidPlantCode = array_unique($invalidPlantCode);
                                Notification::make()
                                    ->title('Invalid Plant Codes')
                                    ->body('The following plant codes should contain minimum 4 digits:<br>'.implode(', ', $invalidPlantCode))
                                    ->danger()
                                    ->send();
                                if ($disk->exists($path)) {
                                    $disk->delete($path);
                                }

                                return;
                            }

                            if (! empty($invalidPlaCoFound)) {
                                $invalidPlaCoFound = array_unique($invalidPlaCoFound);
                                Notification::make()
                                    ->title('Invalid Plant Codes')
                                    ->body('The following plant codes not found in plants:<br>'.implode(', ', $invalidPlaCoFound))
                                    ->danger()
                                    ->send();
                                if ($disk->exists($path)) {
                                    $disk->delete($path);
                                }

                                return;
                            }

                            if (! empty($userNotFound)) {
                                $userNotFound = array_unique($userNotFound);
                                Notification::make()
                                    ->title('Invalid User')
                                    ->body('The following user not found:<br>'.implode(', ', $userNotFound))
                                    ->danger()
                                    ->send();
                                if ($disk->exists($path)) {
                                    $disk->delete($path);
                                }

                                return;
                            }

                            if (! empty($duplicateQrExcel)) {
                                $duplicateGroupedByPlantQr = [];

                                foreach ($duplicateQrExcel as $message) {// "/Document Numbers '([^']+)' already exists for Plant Code (\S+)/"
                                    if (preg_match("/Document Number '([^']+)' already exists for Plant '([^']+)'/", $message, $matches)) {
                                        $qrCode = $matches[1];
                                        $plantCode = $matches[2];
                                        $duplicateGroupedByPlantQr[$plantCode][] = $qrCode;
                                    }
                                }

                                $errorMsg = 'Duplicate Document Number found in Uploaded File :<br>';

                                foreach ($duplicateGroupedByPlantQr as $plantCode => $qrCodes) {
                                    $uniqueQrCodes = array_unique($qrCodes);
                                    $count = count($uniqueQrCodes);

                                    if ($count > 10) {
                                        $errorMsg .= "Duplicate Document Numbers for Plant <b>{$plantCode}</b> : {$count} Document Numbers already exist in uploaded file<br>";
                                    } else {
                                        $errorMsg .= "Duplicate Document Numbers for Plant <b>{$plantCode}</b> : '"
                                            .implode(', ', $uniqueQrCodes)
                                            ."' already exist in uploaded file<br>";
                                    }
                                }

                                Notification::make()
                                    // ->title('Duplicate Document Number in Uploaded File')
                                    ->body($errorMsg)
                                    ->danger()
                                    ->send();

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

                                return;
                            }

                            // if (!empty($duplicateQrDb)) {
                            //     $duplicateGroupedByPlantDb = [];

                            //     foreach ($duplicateQrDb as $message) {
                            //         if (preg_match("/Document Numbers '([^']+)' already exists in DB for Plant '([^']+)'/", $message, $matches)) {
                            //             $qrCode = $matches[1];
                            //             $plantCode = $matches[2];
                            //             $duplicateGroupedByPlantDb[$plantCode][] = $qrCode;
                            //         }
                            //     }

                            //     $errorMsg = 'Duplicate Document Numbers found in Database:<br>';

                            //     foreach ($duplicateGroupedByPlantDb as $plantCode => $qrCodes) {
                            //         $uniqueQrCodes = array_unique($qrCodes);
                            //         $count = count($uniqueQrCodes);

                            //         if ($count > 10) {
                            //             $errorMsg .= "Duplicate Document Numbers for Plant <b>{$plantCode}</b>: {$count} Document Numbers already exist in DB<br>";
                            //         } else {
                            //             $errorMsg .= "Duplicate Document Numbers for Plant <b>{$plantCode}</b>: "
                            //                 . implode(', ', $uniqueQrCodes)
                            //                 . " already exist in DB<br>";
                            //         }
                            //     }

                            //     Notification::make()
                            //        // ->title('Duplicate Document Numbers in Database')
                            //         ->body($errorMsg)
                            //         ->danger()
                            //         ->send();

                            //     if ($disk->exists($path)) {
                            //         $disk->delete($path);
                            //     }

                            //     return;
                            // }

                            $successCount = 0;
                            $failedRecords = [];

                            DB::beginTransaction();

                            try {
                                foreach ($rows as $index => $row) {
                                    if ($index == 0) {
                                        continue;
                                    }

                                    $rowNumber = $index + 1;

                                    try {
                                        $qrcode = trim($row[1]);
                                        $plantCode = trim($row[2]);
                                        $scannedAt = trim($row[3]);
                                        $scannedBy = trim($row[4]);

                                        if (empty($qrcode)) {
                                            throw new \Exception("Row '{$rowNumber}' Missing QR Code");
                                        }

                                        $plant = Plant::where('code', $plantCode)->first();
                                        if (! $plant) {
                                            throw new \Exception("Invalid plant code : '{$plantCode}'");
                                        }

                                        $formattedDate = null;
                                        if (! empty($scannedAt)) {
                                            try {
                                                // $formattedDate = Carbon::createFromFormat('d-m-Y H:i:s', $scannedAt)
                                                //     ->format('Y-m-d H:i:s');
                                                if (is_numeric($scannedAt)) {
                                                    $formattedDate = ExcelDate::excelToDateTimeObject($scannedAt)
                                                        ->format('Y-m-d H:i:s');
                                                } else {
                                                    // Or handle as manual string date (d-m-Y H:i:s)
                                                    $formattedDate = Carbon::createFromFormat('d-m-Y H:i:s', $scannedAt)
                                                        ->format('Y-m-d H:i:s');
                                                }
                                            } catch (\Exception $e) {
                                                throw new \Exception("Invalid date format : '{$scannedAt}'");
                                            }
                                        }

                                        $record = InvoiceOutValidation::where('plant_id', $plant->id)
                                            ->where('qr_code', $qrcode)
                                            ->first();

                                        $curStat = $record ? 'Updation' : 'Insertion';

                                        if ($record) {
                                            $record->update([
                                                'scanned_at' => $formattedDate,
                                                'scanned_by' => $scannedBy,
                                                'updated_by' => $operatorName,
                                            ]);
                                            $inserted = $record;
                                        } else {
                                            // Record does not exist, create with 'created_by'
                                            $inserted = InvoiceOutValidation::create([
                                                'plant_id' => $plant->id,
                                                'qr_code' => $qrcode,
                                                'scanned_at' => $formattedDate,
                                                'scanned_by' => $scannedBy,
                                                'created_by' => $operatorName,
                                            ]);
                                        }
                                        // $inserted = InvoiceOutValidation::create([
                                        //     'plant_id'   => $plant->id,
                                        //     'qr_code'    => $qrcode,
                                        //     'scanned_at' => $formattedDate,
                                        //     'scanned_by' => $scannedBy,
                                        //     'created_by' => $operatorName
                                        // ]);

                                        if (! $inserted) {
                                            throw new \Exception("{$curStat} failed for QR : {$qrcode}");
                                        }

                                        $successCount++;
                                    } catch (\Exception $e) {
                                        $failedRecords[] = [
                                            'row' => $rowNumber,
                                            'qrcode' => $qrcode ?? null,
                                            'error' => $e->getMessage(),
                                        ];
                                    }
                                }

                                DB::commit();

                                if (count($failedRecords) > 0) {
                                    $failedSummary = collect($failedRecords)
                                        ->map(fn ($f) => "Row {$f['row']} ({$f['qrcode']}) : {$f['error']}")
                                        ->take(5) // limit preview to first 5 errors
                                        ->implode("\n");

                                    Notification::make()
                                        ->title('Partial Import Warning')
                                        ->body("'{$successCount}' records inserted. ".count($failedRecords)." failed.\n\n{$failedSummary}")
                                        ->warning()
                                        ->send();
                                } else {
                                    Notification::make()
                                        ->title('Import Success')
                                        ->body("Successfully imported '{$successCount}' records.")
                                        ->success()
                                        ->send();
                                }
                            } catch (\Exception $e) {
                                DB::rollBack();
                                Notification::make()
                                    ->title('Import Failed')
                                    ->body("No records were inserted. Error : {$e->getMessage()}")
                                    ->danger()
                                    ->send();
                            }
                        }
                    })
                    ->visible(function () {
                        return Filament::auth()->user()->can('view import invoice out validation');
                    }),
                ExportAction::make()
                    ->label('Export Invoice Out Data')
                    ->color('warning')
                    ->exporter(InvoiceOutValidationExporter::class)
                    ->visible(function () {
                        return Filament::auth()->user()->can('view export invoice out validation');
                    }),
            ]);
    }

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

    public static function getPages(): array
    {
        return [
            'index' => Pages\ListInvoiceOutValidations::route('/'),
            'create' => Pages\CreateInvoiceOutValidation::route('/create'),
            'view' => Pages\ViewInvoiceOutValidation::route('/{record}'),
            'edit' => Pages\EditInvoiceOutValidation::route('/{record}/edit'),
        ];
    }

    public static function getEloquentQuery(): Builder
    {
        return parent::getEloquentQuery()
            ->withoutGlobalScopes([
                SoftDeletingScope::class,
            ]);
    }
}
