<?php

namespace App\Filament\Pages;

use Carbon\Carbon;
use App\Models\Customer;
use Filament\Pages\Page;
use Filament\Schemas\Schema;
use App\Exports\CustomerStatementExport;
use Filament\Forms\Components\Select;
use Filament\Forms\Contracts\HasForms;
use Filament\Schemas\Components\Section;
use Filament\Forms\Components\DatePicker;
use Filament\Forms\Concerns\InteractsWithForms;
use Filament\Notifications\Notification;
use App\Jobs\SendStatementEmailJob;
use App\Jobs\SendStatementFileJob;
use App\Jobs\SendWhatsAppStatementJob;
use App\Jobs\SendWhatsAppGatewayStatementJob;
use App\Services\StatementService;
use Maatwebsite\Excel\Facades\Excel;
use Mpdf\Mpdf;
use Illuminate\Support\Facades\Log;
use Illuminate\Support\Facades\RateLimiter;
use BezhanSalleh\FilamentShield\Traits\HasPageShield;
use Filament\Support\Icons\Heroicon;

class CustomerStatementReport extends Page implements HasForms
{
    use InteractsWithForms;
    use HasPageShield;

    protected string $view = 'filament.pages.customer-statement-report';

    public ?array $data = [];
    public array $statement_data = [];

    public int $current_page = 1;
    public int $per_page = 50;

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

    protected static string|\UnitEnum|null $navigationGroup = 'customers';
    protected static ?int $navigationSort = 6;

    public function getTitle(): string
    {
        return __('filament/admin/reports.customer_statement');
    }

    public static function getNavigationLabel(): string
    {
        return __('filament/admin/reports.customer_statement');
    }

    public static function getNavigationGroup(): ?string
    {
        return __('filament.navigation_groups.customers');
    }

    public function mount(): void
    {
        $customerId = request()->query('customer_id');

        $this->form->fill([
            'customer_id' => $customerId,
            'from_date' => now()->startOfMonth()->toDateString(),
            'to_date' => now()->endOfDay()->toDateString(),
            'per_page' => $this->per_page,
        ]);

        if ($customerId) {
            $this->generateStatement();
        }
    }

    public function form(Schema $schema): Schema
    {
        return $schema
            ->statePath('data')
            ->components([
                Section::make(__('filament/admin/reports.filters'))
                    ->columns(3)
                    ->schema([
                        Select::make('customer_id')
                            ->label(__('filament/admin/reports.customer'))
                            ->options(Customer::pluck('name', 'id'))
                            ->searchable()
                            ->preload()
                            ->required(),

                        DatePicker::make('from_date')
                            ->label(__('filament/admin/reports.from_date'))
                            ->native(false),

                        DatePicker::make('to_date')
                            ->label(__('filament/admin/reports.to_date'))
                            ->native(false),
                    ]),


            ]);
    }

    public function generateStatement(): void
    {
        if ($this->hitRateLimit('view')) {
            return;
        }

        $formData = $this->form->getState();

        $fromDate = !empty($formData['from_date']) ? Carbon::parse($formData['from_date'])->startOfDay() : null;
        $toDate = !empty($formData['to_date']) ? Carbon::parse($formData['to_date'])->endOfDay() : Carbon::now()->endOfDay();

        if ($fromDate && $toDate->lt($fromDate)) {
            Notification::make()
                ->title('تاريخ النهاية يجب أن يكون بعد أو يساوي تاريخ البداية')
                ->warning()
                ->send();
            $this->statement_data = [];
            return;
        }

        $customer_id = $formData['customer_id'] ?? null;

        if (!$customer_id) {
            $this->statement_data = [];
            return;
        }

        $service = app(StatementService::class);

        $statement = $service->getCustomerStatement(
            customerId: $customer_id,
            farmId: $formData['farm_id'] ?? null,
            fromDate: $formData['from_date'] ?? null,
            toDate: $formData['to_date'] ?? null,
        );

        if (!$statement) {
            $this->statement_data = [];
            return;
        }

        $this->statement_data = $statement;

        if (empty($this->data['email']) && !empty($statement['customer']->email)) {
            $this->data['email'] = $statement['customer']->email;
        }

        $this->per_page = (int) ($this->data['per_page'] ?? $this->per_page);
        $this->current_page = 1;
    }

    public function exportExcel()
    {
        if ($this->hitRateLimit('export_excel')) {
            return;
        }

        if (empty($this->statement_data)) {
            return;
        }

        if (count($this->statement_data['details']) > 500) {
            $this->queueExport('xlsx');
            return;
        }

        $customer = $this->statement_data['customer'];
        $fileName = 'customer_statement_' . $customer->id . '_' . now()->format('Y-m-d') . '.xlsx';

        return Excel::download(
            new CustomerStatementExport(
                $this->statement_data,
                $customer,
                $this->statement_data['from_date'],
                $this->statement_data['to_date']
            ),
            $fileName
        );
    }

    public function exportPdf()
    {
        if ($this->hitRateLimit('export_pdf')) {
            return;
        }

        if (empty($this->statement_data)) {
            return;
        }

        if (count($this->statement_data['details']) > 500) {
            $this->queueExport('pdf');
            return;
        }

        [$fileName, $mpdf] = $this->buildPdf();

        return response()->streamDownload(function () use ($mpdf) {
            echo $mpdf->Output('', 'S');
        }, $fileName);
    }

    public function sendEmail(): void
    {
        if ($this->hitRateLimit('send_email')) {
            return;
        }

        if (empty($this->statement_data)) {
            return;
        }

        $email = $this->resolveEmail();

        if (!$email) {
            Notification::make()
                ->title(__('filament/admin/reports.email_required'))
                ->danger()
                ->send();
            return;
        }

        SendStatementEmailJob::dispatch(
            type: 'customer',
            entityId: $this->statement_data['customer']->id,
            farmId: $this->data['farm_id'] ?? null,
            fromDate: isset($this->data['from_date']) ? Carbon::parse($this->data['from_date'])->toDateString() : null,
            toDate: isset($this->data['to_date']) ? Carbon::parse($this->data['to_date'])->toDateString() : null,
            email: $email
        );

        Log::info('customer_statement.email_queued', [
            'user_id' => auth()->id(),
            'customer_id' => $this->statement_data['customer']->id,
            'email' => $email,
        ]);

        Notification::make()
            ->title(__('filament/admin/reports.email_queued'))
            ->success()
            ->send();
    }

    public function sendWhatsApp(): void
    {
        if ($this->hitRateLimit('send_whatsapp')) {
            return;
        }

        if (empty($this->statement_data)) {
            return;
        }

        $customer = $this->statement_data['customer'];

        if (empty($customer->phone)) {
            Notification::make()
                ->title(__('filament/admin/reports.phone_required'))
                ->danger()
                ->send();
            return;
        }

        SendWhatsAppStatementJob::dispatch(
            type: 'customer',
            entityId: $customer->id,
            farmId: $this->data['farm_id'] ?? null,
            fromDate: isset($this->data['from_date']) ? Carbon::parse($this->data['from_date'])->toDateString() : null,
            toDate: isset($this->data['to_date']) ? Carbon::parse($this->data['to_date'])->toDateString() : null,
            customMessage: $this->data['whatsapp_message'] ?? null
        );

        Log::info('customer_statement.whatsapp_queued', [
            'user_id' => auth()->id(),
            'customer_id' => $customer->id,
            'phone' => $customer->phone,
        ]);

        Notification::make()
            ->title(__('filament/admin/reports.whatsapp_queued'))
            ->success()
            ->send();
    }

    public function sendWhatsAppGateway(): void
    {
        if ($this->hitRateLimit('send_whatsapp_gateway')) {
            return;
        }

        if (empty($this->statement_data)) {
            return;
        }

        $customer = $this->statement_data['customer'];

        if (empty($customer->phone)) {
            Notification::make()
                ->title(__('filament/admin/reports.phone_required'))
                ->danger()
                ->send();
            return;
        }

        SendWhatsAppGatewayStatementJob::dispatch(
            type: 'customer',
            entityId: $customer->id,
            farmId: $this->data['farm_id'] ?? null,
            fromDate: isset($this->data['from_date']) ? Carbon::parse($this->data['from_date'])->toDateString() : null,
            toDate: isset($this->data['to_date']) ? Carbon::parse($this->data['to_date'])->toDateString() : null,
            customMessage: $this->data['whatsapp_message'] ?? null
        );

        Log::info('customer_statement.whatsapp_gateway_queued', [
            'user_id' => auth()->id(),
            'customer_id' => $customer->id,
            'phone' => $customer->phone,
        ]);

        Notification::make()
            ->title(__('filament/admin/reports.whatsapp_gateway_queued'))
            ->success()
            ->send();
    }

    public function scheduleEmail(): void
    {
        if ($this->hitRateLimit('schedule_email')) {
            return;
        }

        if (empty($this->statement_data)) {
            return;
        }

        $email = $this->resolveEmail();
        $scheduledAt = isset($this->data['scheduled_at']) ? Carbon::parse($this->data['scheduled_at']) : null;

        if (!$email || !$scheduledAt) {
            Notification::make()
                ->title(__('filament/admin/reports.schedule_required'))
                ->danger()
                ->send();
            return;
        }

        SendStatementEmailJob::dispatch(
            type: 'customer',
            entityId: $this->statement_data['customer']->id,
            farmId: $this->data['farm_id'] ?? null,
            fromDate: isset($this->data['from_date']) ? Carbon::parse($this->data['from_date'])->toDateString() : null,
            toDate: isset($this->data['to_date']) ? Carbon::parse($this->data['to_date'])->toDateString() : null,
            email: $email
        )->delay($scheduledAt);

        Log::info('customer_statement.email_scheduled', [
            'user_id' => auth()->id(),
            'customer_id' => $this->statement_data['customer']->id,
            'email' => $email,
            'scheduled_at' => $scheduledAt->toDateTimeString(),
        ]);

        Notification::make()
            ->title(__('filament/admin/reports.email_scheduled'))
            ->success()
            ->send();
    }

    protected function resolveEmail(): ?string
    {
        if (!empty($this->data['email'])) {
            return $this->data['email'];
        }

        return $this->statement_data['customer']->email
            ?? auth()->user()?->email
            ?? null;
    }

    protected function buildPdf(): array
    {
        $html = view('reports.pdf.customer-statement', [
            'data' => $this->statement_data,
        ])->render();

        $mpdf = new Mpdf([
            'mode' => 'utf-8',
            'format' => 'A4',
            'orientation' => 'L',
            'margin_left' => 10,
            'margin_right' => 10,
            'margin_top' => 10,
            'margin_bottom' => 10,
            'default_font' => 'dejavusans'
        ]);

        $mpdf->autoScriptToLang = true;
        $mpdf->autoLangToFont = true;
        $mpdf->SetDirectionality('rtl');

        $mpdf->WriteHTML($html);

        $customer = $this->statement_data['customer'];
        $fileName = 'customer_statement_' . $customer->id . '_' . now()->format('Y-m-d') . '.pdf';

        Log::info('customer_statement.pdf_generated', [
            'user_id' => auth()->id(),
            'customer_id' => $customer->id,
        ]);

        return [$fileName, $mpdf];
    }

    protected function queueExport(string $format): void
    {
        $email = $this->resolveEmail();

        SendStatementFileJob::dispatch(
            type: 'customer',
            entityId: $this->statement_data['customer']->id,
            farmId: $this->data['farm_id'] ?? null,
            fromDate: $this->data['from_date'] ?? null,
            toDate: $this->data['to_date'] ?? null,
            format: $format,
            email: $email ?? (auth()->user()?->email ?? '')
        );

        Notification::make()
            ->title(__('filament/admin/reports.email_queued'))
            ->body(__('filament/admin/reports.export_in_queue') ?? 'سيتم إرسال الملف على البريد.')
            ->success()
            ->send();
    }

    protected function hitRateLimit(string $action, int $max = 5, int $decaySeconds = 60): bool
    {
        $key = auth()->id() . ':customer_statement:' . $action;

        if (RateLimiter::tooManyAttempts($key, $max)) {
            Notification::make()
                ->title(__('filament/admin/reports.rate_limited'))
                ->body(__('filament/admin/reports.rate_limited_body'))
                ->danger()
                ->send();
            return true;
        }

        RateLimiter::hit($key, $decaySeconds);
        return false;
    }

    public function paginatedDetails(): array
    {
        $details = $this->statement_data['details'] ?? [];
        $perPage = max(1, (int) ($this->data['per_page'] ?? $this->per_page));
        $totalPages = max(1, (int) ceil(count($details) / $perPage));
        $page = min(max(1, $this->current_page), $totalPages);
        $this->current_page = $page;

        $offset = ($page - 1) * $perPage;
        return array_slice($details, $offset, $perPage);
    }

    public function nextPage(): void
    {
        $this->current_page++;
    }

    public function prevPage(): void
    {
        $this->current_page = max(1, $this->current_page - 1);
    }

    public function gotoPage(int $page): void
    {
        $this->current_page = max(1, $page);
    }

    public function totalPages(): int
    {
        $details = $this->statement_data['details'] ?? [];
        $perPage = max(1, (int) ($this->data['per_page'] ?? $this->per_page));
        return max(1, (int) ceil(count($details) / $perPage));
    }
}
