<?php

namespace App\Livewire;

use App\Services\FinanceRepository;
use App\Services\PreferencesStore;
use App\Support\CategoryCatalog;
use App\Support\CsvStatementParser;
use App\Support\FinanceListOrdering;
use Illuminate\Validation\ValidationException;
use Livewire\Attributes\Computed;
use Livewire\Attributes\Layout;
use Livewire\Component;
use Livewire\Features\SupportFileUploads\TemporaryUploadedFile;
use Livewire\WithFileUploads;

#[Layout('layouts.app')]
class ImportPage extends Component
{
    use WithFileUploads;

    /** @var 'idle'|'parsing'|'preview'|'imported' */
    public string $stage = 'idle';

    /** @var array{name: string, format: string, size: int}|null */
    public ?array $fileMeta = null;

    /** @var list<array<string, mixed>> */
    public array $rows = [];

    public ?TemporaryUploadedFile $upload = null;

    /** @return array<string, true> */
    protected function existingFingerprints(): array
    {
        $prefs = PreferencesStore::all();
        $rowsForFp = [];
        foreach (app(FinanceRepository::class)->transactions() as $t) {
            $rowsForFp[] = [
                'date' => (string) ($t['date'] ?? ''),
                'amount' => (float) ($t['amount'] ?? 0),
                'name' => (string) ($t['name'] ?? ''),
            ];
        }
        foreach ($prefs['importedTransactions'] ?? [] as $t) {
            if (! is_array($t)) {
                continue;
            }
            $rowsForFp[] = [
                'date' => (string) ($t['date'] ?? ''),
                'amount' => (float) ($t['amount'] ?? 0),
                'name' => (string) ($t['name'] ?? ''),
            ];
        }

        return CsvStatementParser::buildFingerprintsFromRows($rowsForFp);
    }

    public function updatedUpload(?TemporaryUploadedFile $upload): void
    {
        if (! $upload) {
            return;
        }

        $this->resetValidation();
        try {
            $this->validate([
                'upload' => 'required|file|max:10240',
            ]);
        } catch (ValidationException) {
            $this->upload = null;
            session()->flash('toast', (string) __('budgets.import_page.toast_unsupported_file_10mb'));

            return;
        }

        $this->runParse($upload);
        $this->upload = null;
    }

    protected function runParse(TemporaryUploadedFile $file): void
    {
        $name = $file->getClientOriginalName();
        $format = $this->detectFormat($name, (string) $file->getMimeType());
        if ($format === 'unknown') {
            session()->flash('toast', (string) __('budgets.import_page.toast_unsupported_file'));

            return;
        }

        $this->fileMeta = [
            'name' => $name,
            'format' => $format,
            'size' => (int) $file->getSize(),
        ];
        $fingerprints = $this->existingFingerprints();
        if ($format !== 'csv') {
            session()->flash('toast', (string) __('budgets.import_page.toast_unsupported_file'));
            $this->stage = 'idle';
            $this->fileMeta = null;

            return;
        }

        $text = $file->get();
        $parsed = CsvStatementParser::parseCsv($text, $fingerprints);
        if ($parsed === []) {
            session()->flash('toast', (string) __('budgets.import_page.toast_no_transactions_import'));
            $this->stage = 'idle';
            $this->fileMeta = null;

            return;
        }

        $this->rows = FinanceListOrdering::importRowsNewestFirst($parsed);
        $this->stage = 'preview';
    }

    private function detectFormat(string $filename, string $mime): string
    {
        $lower = mb_strtolower($filename);
        if (str_ends_with($lower, '.csv') || $mime === 'text/csv' || $mime === 'application/csv' || $mime === 'text/plain') {
            return 'csv';
        }
        return 'unknown';
    }

    public function toggleRow(string $id): void
    {
        $this->rows = array_map(function (array $r) use ($id) {
            if (($r['id'] ?? '') === $id) {
                $r['selected'] = ! (bool) ($r['selected'] ?? false);
            }

            return $r;
        }, $this->rows);
    }

    public function updateRowCategory(string $id, string $categoryId): void
    {
        $this->rows = array_map(function (array $r) use ($id, $categoryId) {
            if (($r['id'] ?? '') === $id) {
                $r['categoryId'] = $categoryId;
                $r['confidence'] = 100;
            }

            return $r;
        }, $this->rows);
    }

    public function removeRow(string $id): void
    {
        $this->rows = array_values(array_filter($this->rows, fn (array $r) => ($r['id'] ?? '') !== $id));
    }

    public function resetSelection(): void
    {
        $this->rows = array_map(function (array $r) {
            $r['selected'] = empty($r['duplicate']);

            return $r;
        }, $this->rows);
    }

    public function resetFlow(): void
    {
        $this->stage = 'idle';
        $this->rows = [];
        $this->fileMeta = null;
        $this->upload = null;
    }

    public function importSelected(): void
    {
        $toImport = array_values(array_filter($this->rows, fn (array $r) => ! empty($r['selected'])));
        if ($toImport === []) {
            session()->flash('toast', (string) __('budgets.import_page.toast_nothing_selected'));

            return;
        }

        $importedAt = (new \DateTimeImmutable('now'))->format(\DateTimeInterface::ATOM);
        $source = $this->fileMeta['format'] ?? 'csv';
        $newTransactions = [];
        $importSeq = (int) round(microtime(true) * 1000);
        foreach ($toImport as $i => $r) {
            $newTransactions[] = [
                'id' => (string) ($r['id'] ?? uniqid('imp-', true)),
                'name' => (string) ($r['name'] ?? ''),
                'amount' => (float) ($r['amount'] ?? 0),
                'date' => (string) ($r['date'] ?? ''),
                'type' => (string) ($r['type'] ?? 'expense'),
                'categoryId' => (string) ($r['categoryId'] ?? 'other'),
                'isRecurring' => false,
                '_createdTs' => $importSeq + $i,
            ];
        }

        PreferencesStore::update(function (array $p) use ($toImport, $importedAt, $source): array {
            $extra = [];
            foreach ($toImport as $r) {
                $extra[] = [
                    'id' => (string) ($r['id'] ?? uniqid('imp-', true)),
                    'name' => (string) ($r['name'] ?? ''),
                    'amount' => (float) ($r['amount'] ?? 0),
                    'date' => (string) ($r['date'] ?? ''),
                    'type' => (string) ($r['type'] ?? 'expense'),
                    'categoryId' => (string) ($r['categoryId'] ?? 'other'),
                    'source' => $source,
                    'importedAt' => $importedAt,
                ];
            }
            $p['importedTransactions'] = array_merge($p['importedTransactions'] ?? [], $extra);

            return $p;
        });

        $finance = app(FinanceRepository::class);
        $existing = $finance->transactions();
        $finance->setTransactions(array_merge($newTransactions, $existing));

        session()->flash('toast', (string) __('budgets.import_page.toast_imported_count_activity', ['count' => count($toImport)]));
        $this->stage = 'imported';
    }

    /**
     * @return array{count: int, duplicates: int, expense: float, income: float}
     */
    #[Computed]
    public function stats(): array
    {
        $selected = array_filter($this->rows, fn (array $r) => ! empty($r['selected']));
        $duplicates = count(array_filter($this->rows, fn (array $r) => ! empty($r['duplicate'])));
        $expense = 0.0;
        $income = 0.0;
        foreach ($selected as $r) {
            $amt = (float) ($r['amount'] ?? 0);
            if ($amt < 0) {
                $expense += abs($amt);
            } elseif ($amt > 0) {
                $income += $amt;
            }
        }

        return [
            'count' => count($selected),
            'duplicates' => $duplicates,
            'expense' => $expense,
            'income' => $income,
        ];
    }

    public function render()
    {
        return view('livewire.import-page', [
            'categories' => CategoryCatalog::all(),
        ])->title((string) __('budgets.import_page.document_title'));
    }
}
