<?php

declare(strict_types=1);

namespace OCA\Mitgliederverwaltung\Controller;

use OCA\Mitgliederverwaltung\Middleware\InputSanitizer;
use OCA\Mitgliederverwaltung\Service\DuplicateMemberException;
use OCA\Mitgliederverwaltung\Service\ValidationException;
use OCP\AppFramework\Db\DoesNotExistException;
use OCP\AppFramework\Http;
use OCP\AppFramework\Http\JSONResponse;

/**
 * Shared helpers for all API controllers.
 *
 * Eliminates the identical getRequestData() and error-handling boilerplate
 * that was duplicated across every controller.
 */
trait ApiControllerTrait {

    /**
     * Parse the JSON request body and sanitize all string values.
     *
     * Applies InputSanitizer to all string fields as defense-in-depth
     * against stored XSS. Non-string values (int, bool, array) pass
     * through unchanged.
     *
     * @return array<string, mixed>
     */
    protected function getRequestData(): array {
        $input = file_get_contents('php://input');
        if ($input === false || $input === '') {
            return [];
        }
        $data = json_decode($input, true);
        if (!is_array($data)) {
            return [];
        }
        return $this->sanitizeRequestData($data);
    }

    /**
     * Recursively sanitize all string values in request data.
     *
     * @param array<string, mixed> $data Raw request data
     * @return array<string, mixed> Sanitized data
     */
    private function sanitizeRequestData(array $data): array {
        $sanitized = [];
        foreach ($data as $key => $value) {
            if (is_string($value)) {
                $sanitized[$key] = InputSanitizer::sanitizeString($value);
            } elseif (is_array($value)) {
                $sanitized[$key] = $this->sanitizeRequestData($value);
            } else {
                $sanitized[$key] = $value;
            }
        }
        return $sanitized;
    }

    /**
     * Run a controller action with standard exception handling.
     *
     * Maps service-layer exceptions to appropriate HTTP status codes:
     * - ValidationException        -> 400
     * - DuplicateMemberException   -> 409
     * - DoesNotExistException      -> 404
     * - Any other Exception        -> 500 (logged)
     *
     * @param callable(): JSONResponse $action The controller action to execute
     * @param string $context Human-readable context for error logging
     * @return JSONResponse
     */
    protected function handleAction(callable $action, string $context): JSONResponse {
        try {
            return $action();
        } catch (ValidationException $e) {
            return new JSONResponse(
                ['error' => $e->getMessage()],
                Http::STATUS_BAD_REQUEST
            );
        } catch (DuplicateMemberException $e) {
            return new JSONResponse(
                ['error' => $e->getMessage()],
                Http::STATUS_CONFLICT
            );
        } catch (DoesNotExistException $e) {
            return new JSONResponse(
                ['error' => $context . ' nicht gefunden'],
                Http::STATUS_NOT_FOUND
            );
        } catch (\Exception $e) {
            $this->logger->error("Failed: $context", [
                'exception' => $e,
                'app' => 'mitgliederverwaltung',
            ]);
            return new JSONResponse(
                ['error' => 'Internal server error'],
                Http::STATUS_INTERNAL_SERVER_ERROR
            );
        }
    }
}
