<?php

declare(strict_types=1);

namespace Ekdos\Relay;

use Ekdos\N8n\CachePolicy;
use Ekdos\Support\Json;
use Psr\Http\Message\ResponseInterface as Response;
use Psr\Http\Message\ServerRequestInterface as Request;

/**
 * Rechnungen eines Kunden, die Gesamtuebersicht und die Zuordnung offener Belege.
 */
final readonly class CustomerInvoicesController extends RelayController
{
    /** Die Rechnungsablage. Nur darunter darf ein PDF ausgeliefert werden. */
    private const string PDF_ROOT = '/files/EK-DOS/40 Rechnungen/';

    public function index(Request $request, Response $response): Response
    {
        $mode = $this->query($request, 'mode');
        $wantsAll = $mode === 'all';
        $secret = $this->invoiceSyncSecret();

        if ($wantsAll && $secret === '') {
            return Json::error($response, 'Die geschützte Rechnungsübersicht ist noch nicht eingerichtet.', 503);
        }

        $url = $wantsAll ? $this->n8nUrl->allInvoices() : $this->n8nUrl->invoices([
            'kunde' => $this->query($request, 'kunde'),
            'aliases' => $this->query($request, 'aliases'),
            'mode' => $mode,
            'liegenschaft' => $this->query($request, 'liegenschaft'),
        ]);

        $reply = $this->cachedGet(CachePolicy::Invoices, $url, $wantsAll ? ['x-ekdos-invoice-sync' => $secret] : []);

        if (!$reply->ok()) {
            return $this->upstreamError($response, $reply, 'Rechnungen konnten nicht geladen werden.');
        }

        $decoded = $reply->decoded();

        return Json::write($response, [
            'invoices' => is_array($decoded['invoices'] ?? null) ? array_values($decoded['invoices']) : [],
            'properties' => is_array($decoded['properties'] ?? null) ? array_values($decoded['properties']) : [],
            'stale' => $reply->stale ?: null,
        ]);
    }

    /** Einen offenen Beleg einem Kunden zuordnen. */
    public function assign(Request $request, Response $response): Response
    {
        $body = $this->body($request);
        $id = self::text($body, 'id');
        $customer = self::text($body, 'kunde');
        $secret = $this->invoiceSyncSecret();

        if ($id === '' || $customer === '') {
            return Json::error($response, 'Rechnung und Kunde müssen ausgewählt sein.', 400);
        }

        if ($secret === '') {
            return Json::error($response, 'Die geschützte Rechnungszuordnung ist noch nicht eingerichtet.', 503);
        }

        $reply = $this->n8n->post(
            $this->n8nUrl->assignInvoice(),
            ['id' => $id, 'kunde' => $customer, 'kundennummer' => self::text($body, 'kundennummer')],
            ['x-ekdos-invoice-sync' => $secret],
        );

        if (!$reply->ok()) {
            return $this->upstreamError($response, $reply, 'Rechnung konnte nicht zugeordnet werden.');
        }

        $this->cache->invalidate(CachePolicy::Invoices[0]);

        return Json::passthrough($response, $reply->body);
    }

    /**
     * Rechnungs-PDF.
     *
     * Der Pfad kommt aus der Oberflaeche, deshalb wird er streng geprueft: er muss
     * unterhalb der Rechnungsablage liegen, auf .pdf enden und darf kein ".."
     * enthalten. Ohne diese Pruefung waere die Route ein Dateibrowser fuer die NAS.
     */
    public function pdf(Request $request, Response $response): Response
    {
        $path = $this->query($request, 'path');

        if (!str_starts_with($path, self::PDF_ROOT) || !str_ends_with(strtolower($path), '.pdf') || str_contains($path, '..')) {
            return Json::error($response, 'Ungültiger Rechnungspfad.', 400);
        }

        $reply = $this->n8n->get($this->n8nUrl->invoicePdf($path));

        if (!$reply->ok() || $reply->body === '') {
            return $this->upstreamError($response, $reply, 'Die Rechnung ist nicht verfügbar.');
        }

        return $this->pdfResponse($response, $reply->body, 'Rechnung.pdf');
    }

    /** Stoesst den Abgleich der Rechnungsablage in n8n an. */
    public function sync(Request $request, Response $response): Response
    {
        $secret = $this->invoiceSyncSecret();

        if ($secret === '') {
            return Json::error($response, 'Der geschützte Rechnungsabgleich ist noch nicht eingerichtet.', 503);
        }

        $reply = $this->n8n->post($this->n8nUrl->invoiceSync(), null, ['x-ekdos-invoice-sync' => $secret]);

        if (!$reply->ok()) {
            return $this->upstreamError($response, $reply, 'Der Rechnungsabgleich konnte nicht durchgeführt werden.');
        }

        // Nach einem Abgleich stimmt keine der Rechnungslisten mehr.
        $this->cache->invalidate(
            CachePolicy::Invoices[0],
            CachePolicy::InvoicesCreate[0],
            CachePolicy::InvoicesReview[0],
            CachePolicy::InvoicesSend[0],
        );

        return $response->withStatus(204);
    }
}
