<?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;

/**
 * Stundennachweise.
 *
 * Ein schwerer Report, den n8n aus mehreren Quellen zusammensetzt. Er wird
 * deshalb am laengsten zwischengespeichert (5 Minuten frisch, 1 Stunde Rueckfall).
 */
final readonly class HoursController extends RelayController
{
    public function index(Request $request, Response $response): Response
    {
        $reply = $this->cachedGet(CachePolicy::Hours, $this->n8nUrl->hours());

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

        $decoded = $reply->decoded();
        // n8n liefert den Report je nach Workflow-Zweig als Objekt oder als Liste mit einem Element.
        $source = array_is_list($decoded) ? (is_array($decoded[0] ?? null) ? $decoded[0] : []) : $decoded;

        $employees = [];

        foreach ((is_array($source['employees'] ?? null) ? $source['employees'] : []) as $row) {
            if (is_array($row) && ($employee = self::mapEmployee($row)) !== null) {
                $employees[] = $employee;
            }
        }

        $payload = [
            'month' => self::text($source, 'month'),
            'year' => self::number($source, 'year'),
            'generatedAt' => self::text($source, 'generatedAt') !== '' ? self::text($source, 'generatedAt') : gmdate('c'),
            'employees' => $employees,
            'statusPriority' => self::text($source, 'statusPriority') !== '' ? self::text($source, 'statusPriority') : null,
        ];

        if ($reply->stale) {
            $payload['stale'] = true;
        }

        return Json::write($response, $payload);
    }

    /** @return array<string, mixed>|null */
    private static function mapEmployee(array $row): ?array
    {
        $name = self::text($row, 'name');

        if ($name === '') {
            return null;
        }

        $entries = [];

        foreach ((is_array($row['entries'] ?? null) ? $row['entries'] : []) as $entry) {
            if (is_array($entry) && !array_is_list($entry)) {
                $entries[] = self::mapEntry($entry);
            }
        }

        return [
            'name' => $name,
            'totalHours' => self::number($row, 'totalHours'),
            'entries' => $entries,
            'workDaysYear' => self::number($row, 'workDaysYear'),
        ];
    }

    /** @return array<string, mixed> */
    private static function mapEntry(array $entry): array
    {
        $date = self::text($entry, 'date');
        $label = self::text($entry, 'label');
        $status = self::text($entry, 'status');

        return [
            'date' => $date,
            'label' => $label !== '' ? $label : $date,
            'status' => $status !== '' ? $status : 'Betrieb',
            'customer' => self::optional($entry, 'customer'),
            'property' => self::optional($entry, 'property'),
            'report' => self::optional($entry, 'report'),
            'hours' => self::number($entry, 'hours'),
        ];
    }

    private static function number(array $source, string $key): float
    {
        $value = $source[$key] ?? null;

        return is_numeric($value) ? (float) $value : 0.0;
    }

    private static function optional(array $source, string $key): ?string
    {
        $value = self::text($source, $key);

        return $value !== '' ? $value : null;
    }
}
