<?php

declare(strict_types=1);

namespace App\Filament\Pages;

use App\Enums\ImportType;
use App\Enums\NavigationGroup;
use App\Jobs\ProcessImportJob;
use App\Services\Import\ImportService;
use BackedEnum;
use Filament\Forms\Components\FileUpload;
use Filament\Forms\Components\Placeholder;
use Filament\Forms\Components\Select;
use Filament\Notifications\Notification;
use Filament\Pages\Page;
use Filament\Schemas\Components\Actions;
use Filament\Schemas\Components\EmbeddedSchema;
use Filament\Schemas\Components\Form;
use Filament\Schemas\Components\Section;
use Filament\Schemas\Components\Wizard;
use Filament\Schemas\Components\Wizard\Step;
use Filament\Schemas\Schema;
use Filament\Support\Enums\Alignment;
use Filament\Support\Icons\Heroicon;
use Illuminate\Contracts\Support\Htmlable;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\Storage;
use Illuminate\Support\HtmlString;
use UnitEnum;

class ImportWizard extends Page
{
    protected static string|BackedEnum|null $navigationIcon = Heroicon::OutlinedArrowUpTray;

    protected static string|UnitEnum|null $navigationGroup = NavigationGroup::System;

    protected static ?int $navigationSort = 2;

    protected static ?string $slug = 'import-wizard';

    /**
     * @var array<string, mixed>|null
     */
    public ?array $data = [];

    /** @var array<int, string> */
    public array $headers = [];

    public ?string $storedFilePath = null;

    /** @var array<string, mixed>|null */
    public ?array $importResult = null;

    public static function getNavigationLabel(): string
    {
        return __('hr.resources.import_wizard');
    }

    public function getTitle(): string|Htmlable
    {
        return __('hr.import.title');
    }

    public function mount(): void
    {
        $this->form->fill([
            'import_type' => ImportType::Employees->value,
            'column_mapping' => [],
        ]);

        $this->refreshImportResult();
    }

    public function defaultForm(Schema $schema): Schema
    {
        return $schema
            ->statePath('data')
            ->columns(1);
    }

    public function form(Schema $schema): Schema
    {
        return $schema->components([
            Wizard::make([
                Step::make(__('hr.import.upload'))
                    ->description(__('hr.import.upload_description'))
                    ->schema([
                        Select::make('import_type')
                            ->label(__('hr.import.import_type'))
                            ->options(ImportType::class)
                            ->required()
                            ->live()
                            ->afterStateUpdated(fn () => $this->resetColumnMapping()),
                        FileUpload::make('file')
                            ->label(__('hr.import.excel_file'))
                            ->disk(config('hr.file_uploads.disk'))
                            ->directory(config('hr.file_uploads.directory') . '/imports')
                            ->acceptedFileTypes([
                                'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
                                'application/vnd.ms-excel',
                                'text/csv',
                            ])
                            ->required()
                            ->afterStateUpdated(fn () => $this->loadHeaders()),
                    ]),
                Step::make(__('hr.import.map_columns'))
                    ->description(__('hr.import.map_columns_description'))
                    ->schema([
                        Section::make(__('hr.import.column_mapping'))
                            ->schema(fn (): array => $this->getColumnMappingFields()),
                    ]),
                Step::make(__('hr.import.preview_step'))
                    ->description(__('hr.import.preview_description'))
                    ->schema([
                        Placeholder::make('preview_table')
                            ->label(__('hr.import.preview'))
                            ->content(fn (): HtmlString => new HtmlString($this->getPreviewHtml())),
                    ]),
                Step::make(__('hr.import.import_step'))
                    ->description(__('hr.import.import_description'))
                    ->schema([
                        Placeholder::make('import_results')
                            ->label(__('hr.import.import_results'))
                            ->content(fn (): HtmlString => new HtmlString($this->getResultsHtml())),
                    ]),
            ])
                ->alpineSubmitHandler('$wire.runImport()')
                ->submitAction(new HtmlString('<button type="submit" class="fi-btn fi-btn-primary">' . e(__('hr.import.start_import')) . '</button>'))
                ->skippable(false),
        ]);
    }

    public function content(Schema $schema): Schema
    {
        return $schema->components([
            Form::make([EmbeddedSchema::make('form')])
                ->id('import-wizard-form')
                ->livewireSubmitHandler('runImport')
                ->footer([
                    Actions::make([])
                        ->alignment(Alignment::Start)
                        ->key('import-wizard-actions'),
                ]),
        ]);
    }

    public function runImport(): void
    {
        $this->form->validate();

        $filePath = $this->resolveStoredFilePath();

        if ($filePath === null) {
            Notification::make()
                ->title(__('hr.import.upload_required_title'))
                ->body(__('hr.import.upload_required_body'))
                ->danger()
                ->send();

            return;
        }

        $importType = ImportType::from($this->data['import_type']);
        $columnMapping = $this->data['column_mapping'] ?? [];

        ProcessImportJob::dispatch(
            $importType,
            $filePath,
            $columnMapping,
            Auth::id(),
        );

        Notification::make()
            ->title(__('hr.import.import_queued_title'))
            ->body(__('hr.import.import_queued_body'))
            ->success()
            ->send();
    }

    public function refreshImportResult(): void
    {
        $result = ProcessImportJob::getCachedResult(Auth::id());
        $this->importResult = $result?->toArray();
    }

    public function loadHeaders(): void
    {
        $path = $this->resolveStoredFilePath();

        if ($path === null) {
            $this->headers = [];

            return;
        }

        $this->headers = app(ImportService::class)->readHeaders($path);
        $this->resetColumnMapping();
    }

    protected function resetColumnMapping(): void
    {
        $this->data['column_mapping'] = [];
    }

    /**
     * @return array<int, Select|Placeholder>
     */
    protected function getColumnMappingFields(): array
    {
        if ($this->headers === []) {
            return [
                Placeholder::make('mapping_hint')
                    ->content(__('hr.import.mapping_hint')),
            ];
        }

        $importType = ImportType::from($this->data['import_type'] ?? ImportType::Employees->value);
        $headerOptions = array_combine($this->headers, $this->headers);

        return collect($importType->fields())
            ->map(function (string $label, string $field) use ($headerOptions): Select {
                return Select::make("column_mapping.{$field}")
                    ->label($label)
                    ->options($headerOptions)
                    ->searchable()
                    ->nullable();
            })
            ->values()
            ->all();
    }

    protected function getPreviewHtml(): string
    {
        $path = $this->resolveStoredFilePath();

        if ($path === null) {
            return '<p class="text-sm text-gray-500">' . e(__('hr.import.upload_to_preview')) . '</p>';
        }

        $importType = ImportType::from($this->data['import_type'] ?? ImportType::Employees->value);
        $importService = app(ImportService::class);
        $rows = $importService->readRows($path, 10);
        $mappedRows = $importService->mapRows($rows, $this->data['column_mapping'] ?? []);
        $validation = $importService->validatePreview($importType, $mappedRows);

        if ($mappedRows === []) {
            return '<p class="text-sm text-gray-500">' . e(__('hr.import.no_rows')) . '</p>';
        }

        $fields = array_keys($importType->fields());
        $html = '<div class="overflow-x-auto"><table class="fi-ta-table w-full text-sm"><thead><tr>';
        $html .= '<th class="px-3 py-2 text-left">' . e(__('hr.import.row')) . '</th>';

        foreach ($fields as $field) {
            $html .= '<th class="px-3 py-2 text-left">' . e($importType->fields()[$field]) . '</th>';
        }

        $html .= '<th class="px-3 py-2 text-left">' . e(__('hr.import.validation')) . '</th></tr></thead><tbody>';

        foreach ($mappedRows as $index => $row) {
            $errors = $validation[$index]['errors'] ?? [];
            $html .= '<tr class="border-t border-gray-200 dark:border-gray-700">';
            $html .= '<td class="px-3 py-2">' . ($index + 2) . '</td>';

            foreach ($fields as $field) {
                $html .= '<td class="px-3 py-2">' . e((string) ($row[$field] ?? '—')) . '</td>';
            }

            $html .= '<td class="px-3 py-2">';

            if ($errors === []) {
                $html .= '<span class="text-success-600">' . e(__('hr.import.valid')) . '</span>';
            } else {
                $html .= '<span class="text-danger-600">' . e(implode(' ', $errors)) . '</span>';
            }

            $html .= '</td></tr>';
        }

        $html .= '</tbody></table></div>';

        return $html;
    }

    protected function getResultsHtml(): string
    {
        $this->refreshImportResult();

        if ($this->importResult === null) {
            return '<p class="text-sm text-gray-500">' . e(__('hr.import.submit_to_see_results')) . '</p>';
        }

        $html = '<div class="space-y-2 text-sm">';
        $html .= '<p><strong>' . e(__('hr.import.total_rows')) . ':</strong> ' . $this->importResult['total'] . '</p>';
        $html .= '<p><strong>' . e(__('hr.import.imported')) . ':</strong> ' . $this->importResult['imported'] . '</p>';
        $html .= '<p><strong>' . e(__('hr.import.skipped_duplicates')) . ':</strong> ' . $this->importResult['skipped'] . '</p>';
        $html .= '<p><strong>' . e(__('hr.import.failed')) . ':</strong> ' . $this->importResult['failed'] . '</p>';

        if (($this->importResult['errors'] ?? []) !== []) {
            $html .= '<div class="mt-3"><strong>' . e(__('hr.import.errors')) . ':</strong><ul class="list-disc ps-5">';

            foreach ($this->importResult['errors'] as $error) {
                $html .= '<li>' . e($error) . '</li>';
            }

            $html .= '</ul></div>';
        }

        $html .= '</div>';

        return $html;
    }

    protected function resolveStoredFilePath(): ?string
    {
        $file = $this->data['file'] ?? null;

        if ($file === null || $file === []) {
            return null;
        }

        $relativePath = is_array($file) ? ($file[0] ?? null) : $file;

        if ($relativePath === null) {
            return null;
        }

        $disk = Storage::disk(config('hr.file_uploads.disk'));
        $this->storedFilePath = $disk->path($relativePath);

        return $this->storedFilePath;
    }
}
