<?php

namespace App\Http\Controllers\Admin;

use App\Http\Controllers\Controller;
use App\Models\CatalogProduct;
use App\Services\Catalog\CatalogExport;
use App\Services\Catalog\CatalogPresence;
use App\Services\Catalog\CatalogSync;
use App\Services\Catalog\CatalogWriter;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Cache;

/**
 * Artikelkatalog: das Sortiment des Lieferanten neben unserem eigenen Stamm
 * (Vorgabe 23.09.2026).
 *
 * Die Seite legt selbst nichts an und aendert nichts in einer Wawi. Sie zeigt,
 * was der Lieferant fuehrt, was davon bei uns bekannt ist, und erzeugt zwei
 * CSV-Dateien - Anlage und Anreicherung -, die ueber die Ameise importiert
 * werden. Der Import bleibt ein bewusster Schritt von Hand; eine Seite, die
 * 2.000 Artikel auf Knopfdruck in die Wawi schreibt, will hier niemand.
 *
 * Das Anreicherungspotenzial kostet einen vollen Abzug des Artikelstamms aus
 * der Offline-Wawi. Deshalb liegt es 15 Minuten im Zwischenspeicher - ohne das
 * wuerde jeder Seitenaufruf 10.000 Zeilen ziehen.
 */
class CatalogController extends Controller
{
    public function index(Request $request, CatalogExport $export, CatalogPresence $vorhanden)
    {
        $quelle = CatalogSync::QUELLE_EXTRADE;
        $filter = [
            'suche'      => trim((string) $request->query('suche', '')),
            'hersteller' => trim((string) $request->query('hersteller', '')),
            'art'        => trim((string) $request->query('art', '')),    // Artikelart des Lieferanten
            'eol'        => (string) $request->query('eol', 'ohne'),      // ohne | nur | alle
            'bestand'    => (string) $request->query('bestand', 'alle'),  // alle | mit | ohne
            'seit'       => (string) $request->query('seit', 'alle'),     // alle | 1 | 7 | 30 (Tage)
            'kuenftig'   => (string) $request->query('kuenftig', ''),     // '' | ja
            'fehlt'      => (string) $request->query('fehlt', 'alle'),    // alle | online | offline | beide
        ];

        // Artikel, die erst noch erscheinen: das DATUM ist das verlaessliche
        // Merkmal, nicht das Kennzeichen des Lieferanten (von 169 kuenftigen
        // Artikeln tragen nur 33 sein "coming_soon").
        // Wo ein Artikel schon steht, sagt die jeweilige Wawi - nicht unsere
        // Merkspalten. Siehe CatalogPresence.
        $karte = $vorhanden->karte();

        $abfrage = CatalogProduct::where('source', $quelle);

        match ($filter['eol']) {
            'nur'   => $abfrage->where('eol', true),
            'alle'  => null,
            default => $abfrage->where('eol', false),
        };

        match ($filter['bestand']) {
            'mit'   => $abfrage->where('stock', '>', 0),
            'ohne'  => $abfrage->where(fn ($q) => $q->whereNull('stock')->orWhere('stock', '<=', 0)),
            default => null,
        };

        // "Neu im Katalog": erstmals gesehen. Der Zeitpunkt der Anlage der
        // Katalogzeile ist genau das - beim Abgleich werden vorhandene Zeilen
        // nur aktualisiert, created_at bleibt der Tag des ersten Auftauchens.
        if (in_array($filter['seit'], ['1', '7', '30'], true)) {
            $abfrage->where('catalog_products.created_at', '>=', now()->subDays((int) $filter['seit'])->startOfDay());
        }

        if ($filter['hersteller'] !== '') {
            $abfrage->where('manufacturer', $filter['hersteller']);
        }

        if ($filter['art'] !== '') {
            $abfrage->where('sub_category', $filter['art']);
        }

        if ($filter['suche'] !== '') {
            $s = '%' . $filter['suche'] . '%';
            $abfrage->where(function ($q) use ($s) {
                $q->where('name', 'like', $s)
                  ->orWhere('ean', 'like', $s)
                  ->orWhere('source_number', 'like', $s)
                  ->orWhere('sku', 'like', $s);
            });
        }

        if ($filter['kuenftig'] === 'ja') {
            $abfrage->whereNotNull('available_from')->where('available_from', '>', now()->toDateString());
        }

        if (in_array($filter['fehlt'], ['online', 'offline', 'beide'], true)) {
            // Fehlt = Nummer steht nicht im Artikelstamm der Instanz. Welche
            // Zeilen das sind, steht fertig im Zwischenspeicher (siehe
            // CatalogPresence) - hier wird nur noch eingegrenzt.
            $ids = match ($filter['fehlt']) {
                'online'  => $karte['fehlt_online'],
                'offline' => $karte['fehlt_offline'],
                default   => array_values(array_intersect($karte['fehlt_online'], $karte['fehlt_offline'])),
            };

            $abfrage->whereIn('id', $ids ?: [0]);
        }

        $treffer = $abfrage->orderBy('manufacturer')->orderBy('name')
            ->paginate(50)->withQueryString();

        $basis = CatalogProduct::where('source', $quelle);

        $kennzahlen = [
            // "offen" heisst seit dem 24.09.2026 nicht mehr "jemand muss
            // entscheiden", sondern schlicht: fehlt noch in der Wawi.
            'offen'    => (clone $basis)->neu()->nichtAbgelehnt()
                ->when((bool) config('extrade.automatik.ohne_eol', true), fn ($q) => $q->where('eol', false))
                ->count(),
            'fehlt_online'  => $karte['ok_online'] ? count($karte['fehlt_online']) : null,
            'fehlt_offline' => $karte['ok_offline'] ? count($karte['fehlt_offline']) : null,
            'aufnehmen' => (clone $basis)->where('decision', 'aufnehmen')->count(),
            'abgelehnt' => (clone $basis)->where('decision', 'ablehnen')->count(),
            'neu_heute' => (clone $basis)->where('catalog_products.created_at', '>=', now()->subDay())->count(),
            'gesamt'   => (clone $basis)->count(),
            'bekannt'  => (clone $basis)->bekannt()->count(),
            'neu'      => (clone $basis)->neu()->count(),
            'eol'      => (clone $basis)->where('eol', true)->count(),
            'bestand'  => (clone $basis)->where('stock', '>', 0)->count(),
            'neu_lieferbar' => (clone $basis)->neu()->where('eol', false)->where('stock', '>', 0)->count(),
            'per_ean'  => (clone $basis)->where('matched_by', 'ean')->count(),
            'per_nr'   => (clone $basis)->where('matched_by', 'liefnr')->count(),
            'manuell'  => (clone $basis)->where('matched_by', 'manuell')->count(),
            'stand'    => (clone $basis)->max('fetched_at'),
        ];

        $potenzial = Cache::remember('katalog_potenzial_' . $quelle, now()->addMinutes(15),
            fn () => $export->potenzial($quelle));

        // Schnellzugriffe: die Fragen, die man tatsaechlich an den Katalog hat -
        // mit der Zahl dahinter, damit vor dem Klick klar ist, ob sich das
        // Hinsehen lohnt.
        $schnell = [
            ['text' => 'fehlt online',    'zahl' => $kennzahlen['fehlt_online'],  'ziel' => ['fehlt' => 'online', 'eol' => 'ohne']],
            ['text' => 'fehlt offline',   'zahl' => $kennzahlen['fehlt_offline'], 'ziel' => ['fehlt' => 'offline', 'eol' => 'ohne']],
            ['text' => 'neu diese Woche', 'zahl' => (clone $basis)->where('catalog_products.created_at', '>=', now()->subDays(7))->count(),
             'ziel' => ['seit' => '7']],
            ['text' => 'nicht lieferbar', 'zahl' => (clone $basis)->where('eol', false)
                ->where(fn ($q) => $q->whereNull('stock')->orWhere('stock', '<=', 0))->count(),
             'ziel' => ['bestand' => 'ohne', 'eol' => 'ohne']],
            ['text' => 'erscheint demnächst', 'zahl' => (clone $basis)->whereNotNull('available_from')
                ->where('available_from', '>', now()->toDateString())->count(), 'ziel' => ['kuenftig' => 'ja']],
            ['text' => 'Auslaufartikel',  'zahl' => $kennzahlen['eol'], 'ziel' => ['eol' => 'nur']],
        ];

        // Rechtelage je Zielinstanz: ohne items.write kann die Seite zwar
        // rechnen, aber nichts schreiben - das gehoert sichtbar nach oben und
        // nicht in eine Fehlermeldung nach dem Knopfdruck.
        $rechte = [];

        foreach ((array) config('extrade.ziele', []) as $schluessel => $z) {
            try {
                $api = \App\Services\JtlApiService::verbindung($schluessel);
                $rechte[$schluessel] = ['name' => $z['name'], 'darf' => $api->darfArtikelSchreiben(),
                                        'scopes' => $api->grantedScopes()];
            } catch (\Throwable $e) {
                $rechte[$schluessel] = ['name' => $z['name'], 'darf' => false, 'scopes' => []];
            }
        }

        return view('admin.catalog.index', [
            'vorhanden'    => $karte,
            'pruefer'      => $vorhanden,
            'rechte'       => $rechte,
            'automatik'    => Cache::get('katalog_automatik_stand'),
            'ziele'        => (array) config('extrade.ziele', []),
            'treffer'      => $treffer,
            'filter'       => $filter,
            'kennzahlen'   => $kennzahlen,
            'potenzial'    => $potenzial,
            'schnell'      => $schnell,
            'hersteller'   => $this->werte($quelle, 'manufacturer'),
            'arten'        => $this->werte($quelle, 'sub_category'),
        ]);
    }

    /**
     * Zuordnung von Hand setzen oder loesen.
     *
     * Von Hand gesetzte Zuordnungen ueberschreibt der naechste Lauf nicht -
     * siehe CatalogSync::ordneZu(). Eine leere Artikelnummer loest die
     * Zuordnung wieder, auch die automatische.
     */
    public function zuordnen(Request $request, CatalogProduct $katalogartikel)
    {
        $sku = trim((string) $request->input('sku', ''));

        if ($sku === '') {
            $katalogartikel->update(['sku' => null, 'matched_by' => null, 'matched_at' => null]);

            return back()->with('success', 'Zuordnung gelöst.');
        }

        $katalogartikel->update(['sku' => $sku, 'matched_by' => 'manuell', 'matched_at' => now()]);
        Cache::forget('katalog_potenzial_' . CatalogSync::QUELLE_EXTRADE);

        return back()->with('success', $katalogartikel->name . ' ist jetzt ' . $sku . ' zugeordnet.');
    }

    /**
     * Vorschau: was der naechste Lauf tun wuerde - fuer BEIDE Warenwirtschaften
     * in einer Liste (Vorgabe 24.09.2026).
     *
     * Vorher gab es eine Auswahl "Ziel: online / offline" und damit zweimal
     * dieselbe Seite mit fast denselben Zeilen. Jetzt steht jede Zeile einmal
     * da, und die Abzeichen sagen, wo sie fehlt. Das ist auch naeher an der
     * Wirklichkeit: die Artikelnummer ist dieselbe, egal in welcher Instanz
     * der Artikel entsteht.
     *
     * Rein lesend - es wird nichts geschrieben und keine Nummer verbraucht.
     */
    public function vorschau(Request $request, CatalogWriter $writer, CatalogPresence $vorhanden)
    {
        $was = in_array($request->query('was'), ['anreichern', 'bilder'], true)
            ? (string) $request->query('was')
            : 'anlegen';

        $grenze = min(500, max(10, (int) $request->query('grenze', 200)));
        $ziele  = (array) config('extrade.ziele', []);

        $zeilen    = [];
        $meldungen = [];
        $summen    = [];

        foreach (array_keys($ziele) as $ziel) {
            $e = match ($was) {
                'anreichern' => $writer->anreichern($ziel, true, $grenze),
                'bilder'     => $writer->bilder($ziel, true, $grenze),
                default      => $writer->anlegen(
                    CatalogProduct::whereIn('id', $vorhanden->fehlendeIds($ziel) ?: [0])
                        ->orderBy('manufacturer')->orderBy('name')->cursor(),
                    $ziel, true, $grenze,
                ),
            };

            $summen[$ziel] = count($e['vorschau']);

            foreach ($e['meldungen'] as $m) {
                $meldungen[$ziel][] = $m;
            }

            foreach ($e['vorschau'] as $v) {
                // Schluessel ist die Artikel- bzw. Lieferantennummer: dieselbe
                // Zeile aus beiden Laeufen gehoert zusammen.
                $schluessel = (string) ($v['liefnr'] ?? $v['sku'] ?? $v['nummer'] ?? '');

                if (! isset($zeilen[$schluessel])) {
                    $zeilen[$schluessel] = $v + ['ziele' => []];
                }

                $zeilen[$schluessel]['ziele'][$ziel] = $was === 'anreichern'
                    ? implode(', ', $v['felder'] ?? [])
                    : ($was === 'bilder' ? ($v['anzahl'] ?? 0) : true);
            }
        }

        return view('admin.catalog.vorschau', [
            'was'       => $was,
            'grenze'    => $grenze,
            'ziele'     => $ziele,
            'zeilen'    => array_values($zeilen),
            'summen'    => $summen,
            'meldungen' => $meldungen,
        ]);
    }

    /** CSV: fehlende Felder bei Artikeln, die wir bereits fuehren. */
    public function anreicherung(CatalogExport $export)
    {
        $datei = $export->anreicherung();

        if (! $datei['ok']) {
            return back()->with('error', $datei['message']);
        }

        return response($datei['inhalt'], 200, [
            'Content-Type'        => 'text/csv; charset=UTF-8',
            'Content-Disposition' => 'attachment; filename="' . $datei['dateiname'] . '"',
        ]);
    }

    /** CSV: Artikel, die wir noch nicht fuehren. */
    public function anlage(Request $request, CatalogExport $export)
    {
        $datei = $export->anlage(
            CatalogSync::QUELLE_EXTRADE,
            $request->boolean('mit_eol'),
            ! $request->has('ohne_bestand'),
        );

        if (! $datei['ok']) {
            return back()->with('error', $datei['message']);
        }

        return response($datei['inhalt'], 200, [
            'Content-Type'        => 'text/csv; charset=UTF-8',
            'Content-Disposition' => 'attachment; filename="' . $datei['dateiname'] . '"',
        ]);
    }

    /** @return array<int,string> */
    private function werte(string $quelle, string $spalte): array
    {
        return CatalogProduct::where('source', $quelle)
            ->whereNotNull($spalte)
            ->distinct()
            ->orderBy($spalte)
            ->pluck($spalte)
            ->all();
    }
}
