# Plugin mit funktioneller Erweiterung von Mojolicious::Plugin::OpenAPI.
#
# SYNOPSIS
# ========
#
# Dies ist als Erweiterung bzw. Ersatz zu Mojolicious::Plugin::OpenAPI->valid_input gedacht.
# Wichtig ist hier, dass man den Rückgabetyp (return_type) definieren kann:
# - empty_array: JSON mit leerem Array
# - empty_hash: JSON mit leerem Hash
# - mlands_msg: JSON mit mlands-Struktur - in msg ist die Fehlermeldung
# - mlands: JSON mit mlands-Struktur - in msg steht 'Fehlerhafte Anfrage'
# - alternativ kommt es in der Struktur von JSON::Validator zurück
#
# mit dem Parameter 'validate_return' wird die Rückgabe vor dem Senden zum Nutzer validiert
#
# Die Fehlermeldung wird als Info-Meldung im Log des Programms abgelegt
#
#
# Beispiel
#
# sub rfc_function {
#    my $self = shift;
#    $self->openapi()->validate_params({'validate_return' => '1'}) || return;
# }
#
# $self->plugin('steffen::MojoPlug::OpenAPI' => { verbose => 1 });
#
# Beispiel 1 - liefert JSON mit einem leeren Array zurück
#
# sub rfc_function {
#    my $self = shift;
#    $self->openapi()->validate_params({'return_type' => 'empty_array'}) || return;
# }
#
# Beispiel 2 - liefert JSON mit einen leeren Hash zurück
#
# sub rfc_function {
#    my $self = shift;
#    $self->openapi()->validate_params({'return_type' => 'empty_hash'}) || return;
# }
#
# Beispiel 3 - liefert JSON mit der mlands Struktur zurück
#
# sub rfc_function {
#    my $self = shift;
#    $self->openapi()->validate_params({'return_type' => 'mlands'}) || return;
# }
#
# Beispiel 4 - liefert JSON mit der mlands Struktur in msg steht die Fehlermeldung zurück
#
# sub rfc_function {
#    my $self = shift;
#    $self->openapi()->validate_params({'return_type' => 'mlands_msg'}) || return;
# }
#
#
# SEE ALSO
# ========
# L<https://metacpan.org/pod/Mojolicious::Plugin::OpenAPI>
# AUTHOR
# ======
# Tilo Brueckner tilo.brueckner@mlands.com

package steffen::MojoPlug::OpenAPI;

use strict;
use warnings;
use utf8;

use Mojo::Base 'Mojolicious::Plugin';

our $VERSION = '__VERSION__';

sub register {
    my ($c, $app, $config) = @_;

    $app->helper('openapi.validate_params' => \&_validate_params);

    if ($config && %{$config} && $config->{'verbose'}) {
        $app->log()->info(sprintf('REGISTERED: %s V%s', __PACKAGE__, $VERSION));
    }

    return;
}

sub _validate_params {
    my ($c, $params) = @_;

    if (!$params || ref $params ne 'HASH') {
        $c->app()->log()->warn('fehlerhafte oder leere Parameter bei Funktionsaufruf');
        $params = {};
    }

    $params->{'return_type'} //= q||;

    my $type = $params->{'validate_return'} ? 'openapi' : 'json';

    # es wird nur ausgewertet, wenn wir auchwirklich mit OpenAPI auf dem Pfad arbeiten
    if ($c->stash()->{'openapi.path'}) {
        my @errors = $c->openapi()->validate();
        if (@errors) {
            # Die übergebenen Parameter für die Fehleranzeige
            my $params = $c->req()->json() || $c->req()->body_params()->to_hash();
            my $msg    = q||;
            for my $error (@errors) {
                if ($error->details()->[1] eq 'type') {
                    $msg .= 'Expected ' . $error->details()->[0] . ' - got ' . $error->details()->[2] . ' in path ' . $error->path() . "\n";
                }
                elsif ($error->details()->[1] eq 'required') {
                    $msg .= 'Missing property: ' . $error->path() . "\n";
                }
                else {
                    my @keys = split /\//, $error->path();
                    $msg .= 'Value "' . $params->{$keys[-1]} . '" does not match reqirement (' . $error->details()->[1] . '): ' . $error->path() . "\n";
                }
            }

            # Ausgabe im Log
            $c->app()->log()->warn($msg);

            # Rückgabe an den Aufrufer
            if ($params->{'return_type'} eq 'empty_array') {
                return !$c->render($type => [], 'status' => 400);
            }
            elsif ($params->{'return_type'} eq 'empty_hash') {
                return !$c->render($type => {}, 'status' => 400);
            }
            elsif ($params->{'return_type'} eq 'mlands_msg') {
                return !$c->render($type => {'rc' => 1, 'msg' => $msg}, 'status' => 400);
            }
            elsif ($params->{'return_type'} eq 'mlands') {
                return !$c->render($type => {'rc' => 1, 'msg' => 'Fehlerhafte Anfrage'}, 'status' => 400);
            }
            else {
                return !$c->render($type => \@errors, 'status' => 400);
            }
        }
    }

    return 1;
}

1;
