#!/usr/bin/perl

# Copyright 2025 Koha Development Team
#
# This file is part of Koha
#
# Koha is free software; you can redistribute it and/or modify it
# under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 3 of the License, or
# (at your option) any later version.
#
# Koha is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with Koha; if not, see <https://www.gnu.org/licenses>.

use Modern::Perl;

use Test::More tests => 4;
use Test::MockModule;
use Test::NoWarnings;

use JSON         qw(encode_json);
use MIME::Base64 qw(encode_base64url);

use Koha::Auth::Client::OAuth;
use Koha::Database;

use t::lib::TestBuilder;

my $schema  = Koha::Database->new->schema;
my $builder = t::lib::TestBuilder->new;

subtest '_get_data_and_patron() with id_token tests' => sub {
    plan tests => 4;

    $schema->storage->txn_begin;

    my $client = Koha::Auth::Client::OAuth->new;

    # Create test patron
    my $patron = $builder->build_object(
        {
            class => 'Koha::Patrons',
            value => { email => 'test@example.com' }
        }
    );

    # Create provider with email matchpoint
    my $provider = $builder->build_object(
        {
            class => 'Koha::Auth::Identity::Providers',
            value => {
                matchpoint => 'email',
                mapping    => encode_json(
                    {
                        email     => 'mail',
                        firstname => 'given_name',
                        surname   => 'family_name'
                    }
                )
            }
        }
    );

    # Create JWT token with user data
    my $claims = {
        mail        => 'test@example.com',
        given_name  => 'John',
        family_name => 'Doe'
    };

    my $id_token = 'header.' . encode_base64url( encode_json($claims) ) . '.signature';

    my $data   = { id_token => $id_token };
    my $config = {};

    # Test the method
    my ( $mapped_data, $found_patron ) = $client->_get_data_and_patron(
        {
            provider => $provider,
            data     => $data,
            config   => $config
        }
    );

    # Verify results
    is( $mapped_data->{email},     'test@example.com', 'Email mapped correctly' );
    is( $mapped_data->{firstname}, 'John',             'First name mapped correctly' );
    is( $mapped_data->{surname},   'Doe',              'Surname mapped correctly' );
    is( $found_patron->id,         $patron->id,        'Patron found by email matchpoint' );

    $schema->storage->txn_rollback;
};

subtest '_get_data_and_patron() with userinfo_url tests' => sub {
    plan tests => 5;

    $schema->storage->txn_begin;

    my $client = Koha::Auth::Client::OAuth->new;

    # Create test patron
    my $patron = $builder->build_object(
        {
            class => 'Koha::Patrons',
            value => { email => 'userinfo@example.com' }
        }
    );

    # Create provider with email matchpoint
    my $provider = $builder->build_object(
        {
            class => 'Koha::Auth::Identity::Providers',
            value => {
                matchpoint => 'email',
                mapping    => encode_json(
                    {
                        email     => 'email',
                        firstname => 'first_name'
                    }
                )
            }
        }
    );

    # Mock UserAgent for userinfo endpoint
    my $ua_mock      = Test::MockModule->new('Mojo::UserAgent');
    my $tx_mock      = Test::MockModule->new('Mojo::Transaction::HTTP');
    my $res_mock     = Test::MockModule->new('Mojo::Message::Response');
    my $headers_mock = Test::MockModule->new('Mojo::Headers');

    $headers_mock->mock( 'content_type', sub { 'application/json' } );
    $res_mock->mock( 'code', sub { '200' } );
    $res_mock->mock(
        'json',
        sub {
            return {
                email      => 'userinfo@example.com',
                first_name => 'Jane'
            };
        }
    );
    $res_mock->mock(
        'headers',
        sub {
            my $headers = {};
            bless $headers, 'Mojo::Headers';
            return $headers;
        }
    );

    $tx_mock->mock(
        'res',
        sub {
            my $res = {};
            bless $res, 'Mojo::Message::Response';
            return $res;
        }
    );

    $ua_mock->mock(
        'get',
        sub {
            my $tx = {};
            bless $tx, 'Mojo::Transaction::HTTP';
            return $tx;
        }
    );

    my $data   = { access_token => 'test_token' };
    my $config = { userinfo_url => 'https://provider.com/userinfo' };

    # Test the method
    my ( $mapped_data, $found_patron ) = $client->_get_data_and_patron(
        {
            provider => $provider,
            data     => $data,
            config   => $config
        }
    );

    # Verify results
    is( $mapped_data->{email},     'userinfo@example.com', 'Email mapped from userinfo' );
    is( $mapped_data->{firstname}, 'Jane',                 'First name mapped from userinfo' );
    is( $found_patron->id,         $patron->id,            'Patron found by email from userinfo' );

    # Test with both id_token and userinfo (patron should be found from id_token first)
    my $id_token_claims = { email => 'token@example.com' };
    my $id_token        = 'header.' . encode_base64url( encode_json($id_token_claims) ) . '.signature';

    my $token_patron = $builder->build_object(
        {
            class => 'Koha::Patrons',
            value => { email => 'token@example.com' }
        }
    );

    $data->{id_token} = $id_token;

    ( $mapped_data, $found_patron ) = $client->_get_data_and_patron(
        {
            provider => $provider,
            data     => $data,
            config   => $config
        }
    );

    is( $found_patron->id,     $token_patron->id,      'Patron found from id_token takes precedence' );
    is( $mapped_data->{email}, 'userinfo@example.com', 'But userinfo data still merged' );

    $schema->storage->txn_rollback;
};

subtest '_get_data_and_patron() no patron found tests' => sub {
    plan tests => 2;

    $schema->storage->txn_begin;

    my $client = Koha::Auth::Client::OAuth->new;

    # Create provider
    my $provider = $builder->build_object(
        {
            class => 'Koha::Auth::Identity::Providers',
            value => {
                matchpoint => 'email',
                mapping    => encode_json( { email => 'mail' } )
            }
        }
    );

    # Create token for non-existent user
    my $claims   = { mail => 'nonexistent@example.com' };
    my $id_token = 'header.' . encode_base64url( encode_json($claims) ) . '.signature';

    my $data   = { id_token => $id_token };
    my $config = {};

    # Test the method
    my ( $mapped_data, $found_patron ) = $client->_get_data_and_patron(
        {
            provider => $provider,
            data     => $data,
            config   => $config
        }
    );

    # Verify results
    is( $mapped_data->{email}, 'nonexistent@example.com', 'Email mapped correctly' );
    is( $found_patron,         undef,                     'No patron found for non-existent email' );

    $schema->storage->txn_rollback;
};
