<?php

declare(strict_types=1);

namespace App\Filament\Resources;

use App\Enums\JobStatus;
use App\Filament\Resources\JobResource\Pages;
use App\Models\Flow;
use App\Models\Job;
use Filament\Forms\Components\DatePicker;
use Filament\Forms\Components\FileUpload;
use Filament\Forms\Components\Group;
use Filament\Forms\Components\Grid;
use Filament\Forms\Components\Hidden;
use Filament\Forms\Components\KeyValue;
use Filament\Forms\Components\Placeholder;
use Filament\Forms\Components\Section;
use Filament\Forms\Components\Select;
use Filament\Forms\Components\TextInput;
use Filament\Forms\Components\Toggle;
use Filament\Forms\Form;
use Filament\Resources\Resource;
use Filament\Tables;
use Filament\Tables\Columns\IconColumn;
use Filament\Tables\Columns\TextColumn;
use Filament\Tables\Filters\TernaryFilter;
use Filament\Tables\Table;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Support\Carbon;
use Illuminate\Support\Facades\Schema;

class JobResource extends Resource
{
    protected static ?string $model = Job::class;

    protected static ?string $navigationIcon  = 'heroicon-o-clipboard-document-check';
    protected static ?string $navigationLabel = 'Jobs';
    protected static ?string $recordTitleAttribute = 'booking_reference';

    /** Build time options like 8:00am, 8:15am … */
    protected static function timeOptions(int $startHour = 7, int $endHour = 20, int $stepMinutes = 15): array
    {
        $opts = [];
        for ($h = $startHour; $h <= $endHour; $h++) {
            for ($m = 0; $m < 60; $m += $stepMinutes) {
                $value = sprintf('%02d:%02d:00', $h, $m);
                $label = Carbon::createFromTime($h, $m)->format('g:ia');
                $opts[$value] = $label;
            }
        }
        return $opts;
    }

    public static function form(Form $form): Form
    {
        // helper: combine a date + time safely
        $combine = function ($date, $time, ?string $tz = null): ?Carbon {
            if (blank($date)) return null;
            $c = $date instanceof Carbon ? $date->copy() : Carbon::parse($date, $tz);
            if (filled($time)) $c->setTimeFromTimeString((string) $time);
            return $c;
        };

        return $form->schema([
            Section::make('Flow & Status')
                ->schema([
                    // Lighter search-based select (no ->preload())
                    Select::make('flow_id')
                        ->label('Flow')
                        ->helperText('Pick the template of rules this Job should follow. The Hold value will prefill from this Flow.')
                        ->searchable()
                        ->getSearchResultsUsing(fn (string $query) =>
                            Flow::query()
                                ->where('name', 'like', "%{$query}%")
                                ->limit(25)
                                ->pluck('name', 'id')
                                ->toArray()
                        )
                        ->getOptionLabelUsing(fn ($value): ?string =>
                            $value ? Flow::query()->whereKey($value)->value('name') : null
                        )
                        ->reactive()
                        ->afterStateUpdated(function ($state, \Filament\Forms\Set $set): void {
                            $cents = (int) (Flow::find($state)?->hold_amount_cents ?? 0);
                            $set('hold_from_flow_display', number_format($cents / 100, 2, '.', ''));
                        })
                        ->required(),

                    Select::make('status')
                        ->label('Status')
                        ->helperText('Lifecycle of this Job (e.g. pending → active → captured/released).')
                        ->options(collect(JobStatus::cases())->mapWithKeys(
                            fn ($c) => [$c->value => $c->value]
                        )->all())
                        ->required(),

                    TextInput::make('external_reference')
                        ->label('Booking reference')
                        ->maxLength(120),
                ])
                ->columns(2),

            Section::make('Agreement')
                ->description('Upload the signed rental agreement (PDF).')
                ->schema([
                    FileUpload::make('agreement_path')
                        ->label('Rental agreement (PDF)')
                        // Use 'public' unless you have 'agreements' disk configured
                        ->disk(config('filesystems.disks.agreements') ? 'agreements' : 'public')
                        ->directory('job-agreements')
                        ->acceptedFileTypes(['application/pdf'])
                        ->maxSize(10 * 1024)
                        ->preserveFilenames()
                        ->openable()
                        ->downloadable(),
                ]),

            Section::make('Customer')
                ->schema([
                    TextInput::make('customer_name')->label('Customer name'),
                    TextInput::make('customer_email')->label('Email')->email(),
                    TextInput::make('customer_phone')->label('Phone'),
                ])
                ->columns(2),

            Section::make('Billing address')
                ->description('Stored as structured JSON on this Job.')
                ->schema([
                    Group::make()
                        ->statePath('billing_address')
                        ->schema([
                            Grid::make(2)->schema([
                                TextInput::make('line1')->label('Address line 1'),
                                TextInput::make('line2')->label('Address line 2'),
                            ]),
                            Grid::make(3)->schema([
                                TextInput::make('city')->label('City'),
                                TextInput::make('region')->label('Region/State'),
                                TextInput::make('postcode')->label('Postcode'),
                            ]),
                            TextInput::make('country')->label('Country')->default('New Zealand'),
                        ])
                        ->columnSpanFull(),
                ]),

            // ===== Dates & Times (no minDate, so past jobs edit cleanly) =====
            Section::make('Dates')
                ->schema([
                    Grid::make(2)->schema([
                        DatePicker::make('start_date')
                            ->label('Start date')
                            ->native(false)
                            ->required()
                            ->reactive()
                            ->afterStateHydrated(function (DatePicker $component, ?Job $record) {
                                if ($record?->start_at) {
                                    $component->state($record->start_at->timezone(config('app.timezone'))->toDateString());
                                }
                            })
                            ->afterStateUpdated(function (\Filament\Forms\Get $get, \Filament\Forms\Set $set, $state) use ($combine) {
                                $start = $combine($state, $get('start_time'), config('app.timezone'));
                                $end   = $combine($get('end_date'), $get('end_time'), config('app.timezone'));
                                if ($start && (! $end || $end->lte($start))) {
                                    $new = $start->copy()->addHour();
                                    $set('end_date', $new->toDateString());
                                    $set('end_time', $new->format('H:i:00'));
                                }
                            }),

                        Select::make('start_time')
                            ->label('Start time')
                            ->options(self::timeOptions(7, 20, 15))
                            ->searchable()
                            ->required()
                            ->reactive()
                            ->afterStateHydrated(function (Select $component, ?Job $record) {
                                if ($record?->start_at) {
                                    $component->state($record->start_at->timezone(config('app.timezone'))->format('H:i:00'));
                                }
                            })
                            ->afterStateUpdated(function (\Filament\Forms\Get $get, \Filament\Forms\Set $set, $state) use ($combine) {
                                $start = $combine($get('start_date'), $state, config('app.timezone'));
                                $end   = $combine($get('end_date'),   $get('end_time'), config('app.timezone'));
                                if ($start && (! $end || $end->lte($start))) {
                                    $new = $start->copy()->addHour();
                                    $set('end_date', $new->toDateString());
                                    $set('end_time', $new->format('H:i:00'));
                                }
                            }),
                    ]),

                    Grid::make(2)->schema([
                        DatePicker::make('end_date')
                            ->label('End date')
                            ->native(false)
                            ->required()
                            ->reactive()
                            ->afterStateHydrated(function (DatePicker $component, ?Job $record) {
                                if ($record?->end_at) {
                                    $component->state($record->end_at->timezone(config('app.timezone'))->toDateString());
                                }
                            }),

                        Select::make('end_time')
                            ->label('End time')
                            ->options(self::timeOptions(7, 20, 15))
                            ->searchable()
                            ->required()
                            ->reactive()
                            ->afterStateHydrated(function (Select $component, ?Job $record) {
                                if ($record?->end_at) {
                                    $component->state($record->end_at->timezone(config('app.timezone'))->format('H:i:00'));
                                }
                            }),
                    ]),

                    Hidden::make('start_at')
                        ->dehydrated(fn (\Filament\Forms\Get $get) => filled($get('start_date')) && filled($get('start_time')))
                        ->default(fn (?Job $record) => $record?->start_at?->timezone(config('app.timezone'))?->toDateTimeString())
                        ->dehydrateStateUsing(function (\Filament\Forms\Get $get) use ($combine) {
                            $dt = $combine($get('start_date'), $get('start_time'), config('app.timezone'));
                            return $dt?->toDateTimeString();
                        }),

                    Hidden::make('end_at')
                        ->dehydrated(fn (\Filament\Forms\Get $get) => filled($get('end_date')) && filled($get('end_time')))
                        ->default(fn (?Job $record) => $record?->end_at?->timezone(config('app.timezone'))?->toDateTimeString())
                        ->dehydrateStateUsing(function (\Filament\Forms\Get $get) use ($combine) {
                            $dt = $combine($get('end_date'), $get('end_time'), config('app.timezone'));
                            return $dt?->toDateTimeString();
                        }),

                    Placeholder::make('duration_preview')
                        ->label('Duration')
                        ->content(function (\Filament\Forms\Get $get) use ($combine) {
                            $start = $combine($get('start_date'), $get('start_time'), config('app.timezone'));
                            $end   = $combine($get('end_date'),   $get('end_time'),   config('app.timezone'));
                            if (!($start && $end) || $end->lte($start)) return '—';
                            $mins  = (int) round($start->diffInMinutes($end));
                            $days  = intdiv($mins, 1440);
                            $hours = intdiv($mins % 1440, 60);
                            $rem   = $mins % 60;
                            $parts = [];
                            if ($days)  $parts[] = $days.' day'.($days === 1 ? '' : 's');
                            if ($hours) $parts[] = $hours.' hr'.($hours === 1 ? '' : 's');
                            if ($rem)   $parts[] = $rem.' min'.($rem === 1 ? '' : 's');
                            return $parts ? implode(', ', $parts) : '0 mins';
                        }),
                ])
                ->columns(2),

            Section::make('Financials')
                ->schema([
                    TextInput::make('charge_amount_cents')
                        ->label('Charge (NZD)')
                        ->prefix('NZ$')
                        ->numeric()
                        ->required()
                        ->formatStateUsing(fn ($state) => $state !== null ? number_format(((int) $state) / 100, 2, '.', '') : null)
                        ->dehydrateStateUsing(function ($state) {
                            if ($state === null || $state === '') return null;
                            return (int) round(((float) $state) * 100);
                        }),

                    TextInput::make('hold_from_flow_display')
                        ->label('Hold (NZD) — from Flow')
                        ->disabled()
                        ->dehydrated(false)
                        ->afterStateHydrated(function (TextInput $component, $state, ?Job $record): void {
                            $cents = (int) ($record?->hold_amount_cents_from_flow
                                ?? $record?->flow?->hold_amount_cents
                                ?? 0);
                            $component->state(number_format($cents / 100, 2, '.', ''));
                        }),

                    // Guards so dropped cache columns don't crash the page
                    Placeholder::make('paid_amount')
                        ->label('Paid to date')
                        ->content(function (?Job $record) {
                            if (!$record) return '—';
                            $paid = method_exists($record, 'getPaidAmountCentsAttribute')
                                ? (int) ($record->paid_amount_cents ?? 0)
                                : (int) ($record->paid_amount_cents ?? 0);
                            return 'NZ$ ' . number_format($paid / 100, 2);
                        }),

                    Placeholder::make('remaining_amount')
                        ->label('Remaining')
                        ->content(function (?Job $record) {
                            if (!$record) return '—';
                            // Prefer model accessor if you have one; otherwise compute safely
                            $charge = (int) ($record->charge_amount_cents ?? 0);
                            $paid   = (int) ($record->paid_amount_cents   ?? 0);
                            $rem    = max($charge - $paid, 0);
                            return 'NZ$ ' . number_format($rem / 100, 2);
                        }),
                ])
                ->columns(2),

            Section::make('Payment (Admin only)')
                ->description('Managed by the system during authorise/capture and webhooks.')
                ->schema([
                    TextInput::make('psp')->label('Provider')->default('stripe')->disabled()->dehydrated(false),
                    TextInput::make('psp_authorization_id')->label('Authorization ID')->disabled()->dehydrated(false),
                    TextInput::make('psp_payment_method_id')->label('Payment Method ID')->disabled()->dehydrated(false),
                    TextInput::make('psp_customer_id')->label('Customer ID')->disabled()->dehydrated(false),
                ])
                ->collapsed(),

            Section::make('Metadata (Admin only)')
                ->schema([
                    KeyValue::make('meta')->disabled()->dehydrated(false),
                    KeyValue::make('comms_log')->disabled()->dehydrated(false),
                ])
                ->collapsed(),

            Section::make('Deposit received (optional)')
                ->description('If a customer paid a deposit outside the system, log it here.')
                ->schema([
                    Toggle::make('deposit_received_enabled')
                        ->label('Log a deposit now')
                        ->default(false)
                        ->reactive(),
                    TextInput::make('deposit_amount')
                        ->label('Deposit amount (NZD)')
                        ->numeric()
                        ->minValue(0)
                        ->visible(fn (\Filament\Forms\Get $get) => (bool) $get('deposit_received_enabled')),
                    Select::make('deposit_method')
                        ->label('Method')
                        ->options([
                            'cash' => 'Cash',
                            'eftpos' => 'EFTPOS',
                            'stripe' => 'Stripe (manual)',
                            'bank' => 'Bank transfer',
                            'other' => 'Other',
                        ])
                        ->visible(fn (\Filament\Forms\Get $get) => (bool) $get('deposit_received_enabled')),
                    TextInput::make('deposit_notes')
                        ->label('Reason / notes')
                        ->visible(fn (\Filament\Forms\Get $get) => (bool) $get('deposit_received_enabled')),
                ])
                ->collapsed()
                ->columns(3),
        ]);
    }

    public static function table(Table $table): Table
    {
        return $table
            ->columns([
                IconColumn::make('is_paid_in_full')
                    ->label('Payment')
                    ->tooltip(fn (Job $record) => $record->is_paid_in_full ? 'Paid in full' : 'Balance owing')
                    ->boolean()
                    ->trueIcon('heroicon-m-check-circle')
                    ->falseIcon('heroicon-m-exclamation-triangle')
                    ->trueColor('success')
                    ->falseColor('warning'),

                TextColumn::make('booking_reference')
                    ->label('Job Reference')
                    ->searchable()
                    ->sortable(),

                TextColumn::make('status')
                    ->label('Status')
                    ->badge()
                    ->sortable(),

                TextColumn::make('customer_name')
                    ->label('Customer')
                    ->searchable(),

                TextColumn::make('charge_amount_cents')
                    ->label('Charge (NZD)')
                    ->alignRight()
                    ->sortable()
                    ->formatStateUsing(fn ($state) => number_format(($state ?? 0) / 100, 2)),

                TextColumn::make('paid_display')
                    ->label('Paid (NZD)')
                    ->alignRight()
                    ->getStateUsing(fn (Job $record) => $record->paidAmountFormatted()),

                TextColumn::make('owing_display')
                    ->label('Owing (NZD)')
                    ->alignRight()
                    ->getStateUsing(fn (Job $record) => $record->owingAmountFormatted()),

                TextColumn::make('updated_at')->since()->label('Updated')->sortable(),
            ])
            ->filters([
                Tables\Filters\SelectFilter::make('status')
                    ->label('Status')
                    ->options(collect(JobStatus::cases())->mapWithKeys(
                        fn ($c) => [$c->value => $c->value]
                    )->all()),
                TernaryFilter::make('is_paid_in_full')
                    ->label('Paid status')
                    ->boolean()
                    ->queries(
                        true: function (Builder $q) {
                            if (Schema::hasColumn('jobs', 'remaining_amount_cents')) {
                                return $q->where('remaining_amount_cents', 0)
                                         ->where('charge_amount_cents', '>', 0);
                            }
                            return $q->whereRaw('
                                (COALESCE(charge_amount_cents,0) - COALESCE(
                                   (SELECT COALESCE(SUM(amount_cents),0)
                                      FROM payments
                                      WHERE payments.job_id = jobs.id
                                        AND status = "succeeded"),
                                   0
                                )) = 0
                                AND COALESCE(charge_amount_cents,0) > 0
                            ');
                        },
                        false: function (Builder $q) {
                            if (Schema::hasColumn('jobs', 'remaining_amount_cents')) {
                                return $q->where('remaining_amount_cents', '>', 0);
                            }
                            return $q->whereRaw('
                                (COALESCE(charge_amount_cents,0) - COALESCE(
                                   (SELECT COALESCE(SUM(amount_cents),0)
                                      FROM payments
                                      WHERE payments.job_id = jobs.id
                                        AND status = "succeeded"),
                                   0
                                )) > 0
                            ');
                        },
                        blank: fn (Builder $q) => $q,
                    ),
            ])
            ->recordClasses(fn (Job $record) => $record->is_paid_in_full
                ? 'bg-emerald-950/10 hover:bg-emerald-950/20'
                : 'bg-amber-950/5 hover:bg-amber-950/15'
            )
            ->actions([
                Tables\Actions\ViewAction::make(),
                Tables\Actions\EditAction::make(),
            ])
            ->bulkActions([
                Tables\Actions\DeleteBulkAction::make(),
            ])
            ->defaultSort('id', 'desc');
    }

    public static function getRelations(): array
    {
        // TEMP: disable heavy relations while we stabilise the edit page
        return [
            // JobResource\RelationManagers\EventsRelationManager::class,
        ];
    }

    public static function getPages(): array
    {
        return [
            'index'  => Pages\ListJobs::route('/'),
            'create' => Pages\CreateJob::route('/create'),
            'edit'   => Pages\EditJob::route('/{record}/edit'),
            'view'   => Pages\ViewJob::route('/{record}'),
        ];
    }
}
