package LATMOS::Accounts::Bases::Sql::User;

use 5.010000;
use strict;
use warnings;

use LATMOS::Accounts::Utils;
use LATMOS::Accounts::Log;
use base qw(LATMOS::Accounts::Bases::Sql::objects);

our $VERSION = (q$Rev$ =~ /^Rev: (\d+) /)[0];

=head1 NAME

LATMOS::Ad - Perl extension for blah blah blah

=head1 SYNOPSIS

  use LATMOS::Accounts::Bases;
  my $base = LATMOS::Accounts::Bases->new('sql');
  ...

=head1 DESCRIPTION

Account base access over standard unix file format.

=head1 FUNCTIONS

=cut

=head2 new(%options)

Create a new LATMOS::Ad object for windows AD $domain.

domain / server: either the Ad domain or directly the server

ldap_args is an optionnal list of arguments to pass to L<Net::LDAP>.

=cut

sub object_table { 'user' }

sub key_field { 'name' }

sub has_extended_attributes { 1 }

sub _delayed_fields {
    my ($self)= @_;
    return qw(memberOf manager directReports);
}

sub _office_address_fields { qw(telephoneNumber physicalDeliveryOfficeName site) }

sub _inline_fields {
    my ($class, $for, $base) = @_;
    return (
        $class->SUPER::_inline_fields($for, $base),
        uidNumber => 'uidnumber',
        gidNumber => 'gidnumber',
    );
}

sub _managed_fields {
    my ($self, $for, $base) = @_;
    return (
        memberOf        => 'memberOf',
        forward => 'forward',
        (map { $_ => $_ } $self->_address_fields),
        (map { $_ => $_ } $self->_office_address_fields),
        (($for !~ /w/) ? (
        uid => 'name',
        cn => 'name',
        gecos        => 'gecos',
        displayName  => 'displayName',
        sAMAccountName  => 'sAMAccountName',
        accountExpires => 'accountExpires',
        shadowExpire => 'shadowExpire',
        directReports => 'directReports',
        managedObjects => 'managedObjects',
        department => 'department',
        otheraddress => 'otheraddress',
        mainaddress => 'mainaddress',
        aliases => 'aliases',
        revaliases => 'revaliases',
        ) : ()),
    )
}

sub get_field {
    my ($self, $field) = @_;
    if ($field eq 'gecos') {
        return to_ascii(
            join(' ', grep { $_ } ($self->_get_c_field('givenName'), ($self->_get_c_field('sn'))))
        ) || to_ascii($self->_get_c_field('description'));
    } elsif ($field eq 'displayName') {
        return join(' ', grep { $_ } ($self->_get_c_field('givenName'), ($self->_get_c_field('sn'))))
            || $self->id;
    } elsif ($field eq 'sAMAccountName') {
        return $self->id;
    } elsif ($field eq 'memberOf') {
        my $sth = $self->db->prepare_cached(
            q{
            select name from "group" join
            group_attributes_users on group_attributes_users.okey = "group".ikey
            where value = ? and attr = ?
            }
        );
        $sth->execute($self->id, 'memberUID');
        my @res;
        while (my $res = $sth->fetchrow_hashref) {
            push(@res, $res->{name});
        }
        return \@res;
    } elsif ($field eq 'directReports') {
        my $sth = $self->db->prepare_cached(
            q{
            select name from "user" join
            user_attributes on user_attributes.okey = "user".ikey
            where value = ? and attr = ?
            }
        );
        $sth->execute($self->id, 'manager');
        my @res;
        while (my $res = $sth->fetchrow_hashref) {
            push(@res, $res->{name});
        }
        return \@res;
    } elsif ($field eq 'department') {
        my $sth = $self->db->prepare_cached(
            q{
            select name from "group" join
            group_attributes on "group".ikey = group_attributes.okey
            where okey in (select okey from group_attributes_users where value = ?)
            and attr = ? and value = ?
            }
        );
        $sth->execute($self->id, 'sutype', 'dpmt');
        my @res;
        while (my $res = $sth->fetchrow_hashref) {
            push(@res, $res->{name});
        }
        return join(',', @res);
    } elsif ($field eq 'managedObjects') {
        my $sth = $self->db->prepare_cached(
            q{
            select name from "group" join
            group_attributes on group_attributes.okey = "group".ikey
            where value = ? and attr = ?
            }
        );
        $sth->execute($self->id, 'managedBy');
        my @res;
        while (my $res = $sth->fetchrow_hashref) {
            push(@res, $res->{name});
        }
        return \@res;
    } elsif ($field eq 'accountExpires') {
        my $sth = $self->db->prepare_cached(
            sprintf(
                q{select extract(epoch from expire) + 11644474161 as expire
                from %s where %s = ?},
                $self->db->quote_identifier($self->object_table),
                $self->db->quote_identifier($self->key_field),
            )
        );
        $sth->execute($self->id);
        my $res = $sth->fetchrow_hashref;
        $sth->finish;
        return $res->{expire} ? sprintf("%.f", $res->{expire} * 1E7) : '9223372036854775807';
    } elsif ($field eq 'shadowExpire') {
        my $sth = $self->db->prepare_cached(
            sprintf(
                q{select justify_hours(expire - '1/1/1970'::timestamp) as expire
                from %s where %s = ?},
                $self->db->quote_identifier($self->object_table),
                $self->db->quote_identifier($self->key_field),
            )
        );
        $sth->execute($self->id);
        my $res = $sth->fetchrow_hashref;
        $sth->finish;
        return -1 unless($res->{expire});
        $res->{expire} =~ /(\d+) days\s*(\w)?/;
        return $1 + ($2 ? 1 : 0);
    } elsif ($field eq 'otheraddress') {
        my $sth = $self->db->prepare_cached(q{
            select name from address where "user" = ?
        });
        $sth->execute($self->id);
        my @values;
        while (my $res = $sth->fetchrow_hashref) {
            push(@values, $res->{name});
        }
        return \@values;
    } elsif ($field eq 'mainaddress') {
        my $sth = $self->db->prepare_cached(q{
            select name from address join address_attributes on ikey = okey
            where "user" = ? and attr = 'isMainAddress'
            });
        $sth->execute($self->id);
        my $res = $sth->fetchrow_hashref;
        $sth->finish;
        return $res->{name};
    } elsif (grep { $field eq $_ } __PACKAGE__->_address_fields(), $self->_office_address_fields) {
        if (my $fmainaddress = $self->get_c_field('mainaddress')) {
            my $address = $self->base->get_object('address', $fmainaddress);
            if ($address) {
                return $address->get_c_field($field);
            } else { # can't happend
                return;
            }
        } else {
            return $self->SUPER::get_field($field);
        }
    } elsif ($field eq 'aliases') {
        my $sth = $self->db->prepare(q{
            select name from aliases where forward = ?
        } . ($self->base->{wexported} ? '' : 'and exported = true'));
        $sth->execute($self->id);
        my @values;
        while (my $res = $sth->fetchrow_hashref) {
            push(@values, $res->{name});
        }
        return \@values;
    } elsif ($field eq 'forward') {
        my $sth = $self->db->prepare(q{
            select forward from aliases where name = ?
        } . ($self->base->{wexported} ? '' : ' and exported = true'));
        $sth->execute($self->id);
        my $res = $sth->fetchrow_hashref;
        $sth->finish;
        return $res->{forward}
    } elsif ($field eq 'revaliases') {
        my $sth = $self->db->prepare(q{
            select "as" from revaliases where name = ?
        } . ($self->base->{wexported} ? '' : ' and exported = true'));
        $sth->execute($self->id);
        my $res = $sth->fetchrow_hashref;
        $sth->finish;
        return $res->{as}
    } else {
        return $self->SUPER::get_field($field);
    }
}

sub set_fields {
    my ($self, %data) = @_;
    my %fdata;
    my $res = 0;
    foreach my $attr (keys %data) {
        $attr =~ /^memberOf$/ and do {
            my %member;
            foreach (@{ $self->get_field('memberOf') }) {
                $member{$_}{c} = 1;
            }
            foreach (ref $data{$attr} ? @{ $data{$attr} || []} : $data{$attr}) {
                $member{$_}{n} = 1;
            }

            foreach (keys %member) {
                $member{$_}{c} && $member{$_}{n} and next; # no change !
                my $group = $self->base->get_object('group', $_) or next;
                if ($member{$_}{n}) {
                    my $sth = $self->db->prepare_cached(
                        q{insert into group_attributes_users (value, attr, okey) values (?,?,?)}
                    );
                    $sth->execute($self->id, 'memberUID', $group->_get_ikey);
                    $res++;
                } elsif ($member{$_}{c}) {
                    my $sth = $self->db->prepare_cached(
                        q{delete from group_attributes_users where value = ? and attr = ? and okey = ?}
                    );
                    $sth->execute($self->id, 'memberUID', $group->_get_ikey);
                    $res++;
                } # else {} # can't happend
            }
            next;
        };
        $attr =~ /^forward$/ and do {
            if ($data{$attr}) {
                if (my $f = $self->base->get_object('aliases', $self->id)) {
                    $res += $f->set_c_fields(forward => $data{$attr});
                } else {
                    $self->base->create_c_object('aliases', $self->id,
                        forward => $data{$attr}) and $res++;
                }
            } else {
                $self->base->delete_object('aliases', $self->id);
                $res++;
            }
            next;
        };
        grep { $attr eq $_ } (__PACKAGE__->_office_address_fields, __PACKAGE__->_address_fields()) and do {
            my $fmainaddress = $self->_get_c_field('mainaddress');
            if (!$fmainaddress) {
                $fmainaddress = $self->id . '-' . join('', map { ('a'..'z')[rand(26)] }
                (0..4));
                $self->base->create_c_object(
                    'address', $fmainaddress,
                    user => $self->id,
                    isMainAddress => 1, ) or do {
                    $self->base->log(LA_ERR,
                        "Cannot create main address for user %s", $self->id);
                    return;
                };
            }
            if (my $address = $self->base->get_object('address', $fmainaddress)) {
                $res += $address->set_c_fields($attr => $data{$attr});
            }
            next;
        };
        $fdata{$attr} = $data{$attr} || undef;
    }
    if (keys %fdata) {
        return $self->SUPER::set_fields(%fdata) + $res;
    } else { return $res; }
}


1;

__END__

=head1 SEE ALSO

=head1 AUTHOR

Olivier Thauvin, E<lt>olivier.thauvin@latmos.ipsl.frE<gt>

=head1 COPYRIGHT AND LICENSE

Copyright (C) 2008, 2009 CNRS SA/CETP/LATMOS

This library is free software; you can redistribute it and/or modify
it under the same terms as Perl itself, either Perl version 5.10.0 or,
at your option, any later version of Perl 5 you may have available.


=cut
