package Mojolicious::Plugin::Fondation::User;
$Mojolicious::Plugin::Fondation::User::VERSION = '0.03';
# ABSTRACT: User management plugin for Fondation

use Mojo::Base 'Mojolicious::Plugin', -signatures;
use Mojo::JSON qw(true);

sub fondation_meta {
    return {
        dependencies => [
            'Fondation::Model::DBIx::Async',
            'Fondation::MigrationDBIx',
            ],
        defaults => {
            title  => 'User Management',
            models => {
                user => {
                    source  => 'User',
                    backend => undef,  # must be set in app config
                },
            },
        },
    };
}

sub register ($self, $app, $conf) {
    # API routes generated by Fondation::OpenAPI.
    # HTML routes provided by Fondation::User::UI.

    return $self;
}

1;

__END__

=pod

=encoding UTF-8

=head1 NAME

Mojolicious::Plugin::Fondation::User - User management plugin for Fondation

=head1 VERSION

version 0.03

=head1 SYNOPSIS

  # myapp.conf
  {
      'Fondation' => {
          dependencies => [
              { 'Fondation::Model::DBIx::Async' => {
                  backends => [ main => {
                      dsn          => 'dbi:SQLite:dbname=data/app.db',
                      schema_class => 'MySchema',
                  }],
              }},
              'Fondation::User',
          ],
      },
  }

=head1 DESCRIPTION

L<Mojolicious::Plugin::Fondation::User> provides the C<users> database table
(schema, Result, and ResultSet classes) and a REST controller for CRUD
operations.

Routes are auto-generated by L<Fondation::OpenAPI>. HTML UI is provided by
L<Fondation::User::UI::Bootstrap>.

=head2 Schema

The C<users> table has these columns:

  id          INTEGER PRIMARY KEY AUTOINCREMENT
  username    VARCHAR(100) NOT NULL
  email       VARCHAR(255) NOT NULL
  password    VARCHAR(255) NOT NULL  (hashed with Argon2)
  created_at  DATETIME NOT NULL
  updated_at  DATETIME NOT NULL
  active      INTEGER DEFAULT 1

Passwords are automatically hashed with L<Crypt::Passphrase> (Argon2) via
C<insert()> and C<update()> hooks in the Result class. The hashing runs in
the DBIx::Class::Async worker process, keeping the event loop free.

=head2 Controller

REST endpoints (routes generated by OpenAPI):

  GET    /api/user        list all users
  POST   /api/user        create a user
  GET    /api/user/:id    read a user
  PUT    /api/user/:id    update a user
  DELETE /api/user/:id    delete a user

The C<password> field is never returned in API responses.

=head2 ResultSet extensions

  $c->model('user')->active;           # users with active = 1
  $c->model('user')->created_today;    # users created today
  $c->model('user')->latest;           # latest 10 users

=head3 with() — many_to_many prefetch

  # Include groups in list/read responses (requires Fondation::Group)
  GET /api/User?with=groups

  # Or programmatically:
  $c->model('user')->with('groups')->TO_JSON->then(sub ($data) {
      $self->render(openapi => $data);
  });

C<with('groups')> triggers a single-query prefetch via the
C<user_group> pivot table.  Each user row includes a C<groups>
arrayref with the full group objects — no extra DB round-trips.
The automatic C<TO_JSON> serialization (inherited from
L<Mojolicious::Plugin::Fondation::Schema::Result::Base>) includes
the groups when the data is available.

Without C<with()>, m2m relationships are silently excluded.

=head2 Translations

Translations for API notifications are shipped in C<share/translations/>.
English and French are included.

=head1 SEE ALSO

=over

=item *

L<Mojolicious::Plugin::Fondation::Model::DBIx::Async> — database backend

=item *

L<Mojolicious::Plugin::Fondation::OpenAPI> — OpenAPI spec and route generation

=item *

L<Mojolicious::Plugin::Fondation::User::UI::Bootstrap> — HTML UI

=back

=head1 AUTHOR

Daniel Brosseau <dab@cpan.org>

=head1 COPYRIGHT AND LICENSE

This software is copyright (c) 2026 by Daniel Brosseau.

This is free software; you can redistribute it and/or modify it under
the same terms as the Perl 5 programming language system itself.

=cut
