#!/usr/bin/perl
#
# Author:  Petter Reinholdtsen
# Date:    2005-12-17
# License: GNU Public License v2
#
# Check NUUG member registry for consistency, and report the errors.

use strict;
use warnings;

BEGIN {
    # Add script location to include path
    my @p = split(m%/%, $0); pop @p; push @INC, join("/", @p);
}

use Getopt::Std;
use medlemsliste;
use Date::Parse;

my $usenixlimit = 4 * 30; # days

my %opts;
usage() unless getopts('wiWIf:v', \%opts);

my %members;
my %mcount;
my %names;
my $retval = 0;
load_memberslist($opts{f} || "medlemsliste.csv", \&process_member);
check_memberslist();
exit $retval;

sub usage {
    print <<EOF;
Usage: $0 [-f <filename>]
  -f filename  load database from filename
  -v           list info on the members with issues
  -w           show warning messages
  -i           show information messages
EOF
    exit 1;
}

sub error {
    print "error: ",@_,"\n";
    $retval = 1;
}
sub warning {
    if ($opts{'w'}) {
        print "warning: ",@_,"\n";
        $retval = 1;
    }
}
sub info {
    print "info: ",@_,"\n" if $opts{'i'};
}

sub process_member {
    my $memberinfo = shift;

    my $customerid = $memberinfo->{CustomerNo};
    if (exists $members{$customerid}) {
            error("Duplikat-ID $customerid");
    }
    $members{$customerid} = $memberinfo;
    $names{$memberinfo->{Name}} = $memberinfo;

    if ("FIRMAMEDLEM" eq $memberinfo->{ZUsrMedlemstatus}) {
        if (!exists $mcount{$memberinfo->{CustomerNo}}) {
            $mcount{$memberinfo->{CustomerNo}} = 0;
        }
    } elsif ("Medlem under bedrift" eq $memberinfo->{ZUsrMedlemstatus}) {
        $mcount{$memberinfo->{ZUsrArbeidsgiverNo}}++;
    }
}

sub show_memberinfo {
    my $memberinfo = shift;
    return unless $opts{v};
    for my $key (sort keys %{$memberinfo}) {
        printf "  %-22s %s\n", "$key:", $memberinfo->{$key} ? $memberinfo->{$key} : "";
    }
    print "\n";
}

sub longago {
    my $date = shift;
    my $period = (time() - str2time($date));
    $date =~ s/ .+$//;
    return 1 if ($period > ($usenixlimit*24*60*60));
    return 0;
}

sub is_usenix_missing {
# obsolete after 2020-12-31
    my $memberinfo = shift;
    my $customerid = $memberinfo->{CustomerNo};
    my $date = $memberinfo->{ZUsrInnmeldtDato};

    return 0;  # the following will never execute

    return 0 if ($memberinfo->{ZUsrMedlemstatus} =~ m/uten USENIX/);
    return 0 unless $memberinfo->{ZBetaltTil};

    $date =~ s/ .+$//;
    my $bdate = sprintf(", betalt til %s", $memberinfo->{ZBetaltTil});

    # Only warn if it is more than half a year since the member was registered
    if (!$memberinfo->{USENIX} and longago($date)) {
        warning "Medlem $customerid uten ".
            "USENIX-nummer (innmeldt $date$bdate).";
        return 1 if $opts{'w'};
    } elsif (!$memberinfo->{USENIX}) {
        info "Medlem $customerid uten ".
            "USENIX-nummer (innmeldt $date$bdate).";
        return 1 if $opts{'i'};
    }
    return 0;
}

sub check_memberslist {
    for my $customerid (sort keys %members) {
        my $memberinfo = $members{$customerid};

        # Sjekk kun medlemmer
        next unless $memberinfo->{ZUsrMedlemstatus};
        my $show_id;
        my $show_nextid;
        if ($memberinfo->{ZUsrUtmeldtDato} ne "1970-01-01 00:00:00") {
            error("Medlem $customerid har utmeldtdato ",
                  $memberinfo->{ZUsrUtmeldtDato});
            $show_id = $memberinfo;
        }

        my $innmeldtdato = $memberinfo->{ZUsrInnmeldtDato};
        error("Medlem $customerid har defekt/manglende ZUsrInnmeldtDato ($innmeldtdato).")
            unless (str2time($innmeldtdato));

        if (!$memberinfo->{PostCode}) {
            error "Medlem $customerid mangler postnummer.";
            $show_id = $memberinfo if $opts{'i'};
        }

        if (!$memberinfo->{Address1} && !$memberinfo->{Address2} &&
            !$memberinfo->{Address3}) {
            error "Medlem $customerid har for få adressefelt (gir problemer hos sendregning.no).";
            $show_id = $memberinfo;
        } elsif (!$memberinfo->{Address1} && !$memberinfo->{Address2} &&
            !$memberinfo->{Address3} && !$memberinfo->{PostCode} &&
            !$memberinfo->{PostOffice}) {
            error "Medlem $customerid mangler adresse.";
            $show_id = $memberinfo;
        }

        # sendregning.no limits
        my %limits =
            (
             'Name'       => 42,
             'Address1'   => 41,
             'Address2'   => 41,
             'PostOffice' => 36,
             'PostCode'   => 8,
             'CustomerNo' => 32,
             'Country'    => 42,
             'ZUsrFakturaReferanse' => 30,
             'EmailAddress' => 64,
            );
        for my $key (keys %limits) {
            my $limit = $limits{$key};
            if (exists $memberinfo->{$key} &&
                $limit < length($memberinfo->{$key})) {
                error "Medlem $customerid har for mange tegn i $key (>$limit).";
                $show_id = $memberinfo;
            }
        }

        # Need to ignore Address1 if it is the same as the company name
        my @a = ($memberinfo->{Address1},
                 $memberinfo->{Address2},
                 $memberinfo->{Address3});
        $a[0] = ""
            if ($memberinfo->{ZUsrMedlemstatus} eq "Medlem under bedrift" &&
                $a[0] eq $members{$memberinfo->{ZUsrArbeidsgiverNo}}->{Name});
        if ($a[0] && $a[1] && $a[2]) {
            error "Medlem $customerid har for mange adressefelt.";
            $show_id = $memberinfo;
        }

        my $currentperiod = current_membership_period();

        if ( !$memberinfo->{ZUsrFakturertPeriode} ) {
            warning "Medlem $customerid mangler FakturertPeriode.";
            $show_id = $memberinfo if $opts{'w'};
        }

        if ( $memberinfo->{ZUsrMedlemstatus} ne "FIRMAMEDLEM" ) {
            if ( (!defined $memberinfo->{ZBetaltTil} ||
                  (defined $memberinfo->{ZBetaltTil} &&
                   !$memberinfo->{ZBetaltTil})) &&
                 longago($memberinfo->{ZUsrInnmeldtDato})) {
                warning "Medlem $customerid har ikke betalt kontingenten på $usenixlimit dager.";
                $show_id = $memberinfo if $opts{'w'};
            } elsif ( !defined $memberinfo->{ZBetaltTil} ||
                      (defined $memberinfo->{ZBetaltTil} &&
                       $memberinfo->{ZBetaltTil} ne $currentperiod) ) {
                info "Medlem $customerid har ikke betalt den siste kontingenten.";
                $show_id = $memberinfo if $opts{'i'};
            }
        }

        if ($memberinfo->{Kontaktperson} &&
            ! exists $names{$memberinfo->{Kontaktperson}}) {
            warning "Firma $customerid har kontaktperson " . $memberinfo->{Kontaktperson} . " som mangler i registeret.";
        }
        if ($memberinfo->{EmailAddress} &&
            # $memberinfo->{EmailAddress} !~ m/^[a-zA-Z0-9._%+-]+@[a-z0-9._%-]+\.[a-z]{2,4}$/) {  # must accept longer topdomains 2015-11-06 halden
            $memberinfo->{EmailAddress} !~ m/^[a-zA-Z0-9._%+-]+@[a-z0-9._%-]+\.[a-z]{2,10}$/) {
            warning("Medlem $customerid har ugyldig epostadresse '".
                    $memberinfo->{EmailAddress}."'.");
            $show_id = $memberinfo if $opts{'w'};
        }

        if ("FIRMAMEDLEM" eq $memberinfo->{ZUsrMedlemstatus}) {
            if ($memberinfo->{ZUsrArbeidsgiverNo} or
                $memberinfo->{ZUsrArbeidsgiver}) {
                error "Firma $customerid har referanse til firmamedlem.";
                $show_id = $memberinfo;
            }
            unless ($memberinfo->{Kontaktperson}) {
                info "Firma $customerid mangler kontaktperson.";
                $show_id = $memberinfo if $opts{'i'};
            }
            my $count = $mcount{$memberinfo->{CustomerNo}};
            if (0 == $count) {
                error "Firmamedlem $customerid har ingen personmedlemmer.";
                $show_id = $memberinfo;
            }
        } elsif ("Medlem under bedrift" eq $memberinfo->{ZUsrMedlemstatus}) {
            my $companyid = $memberinfo->{ZUsrArbeidsgiverNo};
            if (! defined $companyid) {
                error "Medlem $customerid er firmamedlem uten referanse til firma";
                $show_id = $memberinfo;
            } elsif (! exists $members{$companyid}) {

                error "Medlem $customerid har referanse til ikke-eksisterende firma $companyid";
                $show_id = $memberinfo;
            } elsif ("FIRMAMEDLEM" ne $members{$companyid}->{ZUsrMedlemstatus}) {
                error "Medlem $customerid har referanse til ikke-firma $companyid";
                $show_id = $memberinfo;
                $show_nextid = $members{$companyid};
            }

            $show_id = $memberinfo if is_usenix_missing($memberinfo);
            if (!$memberinfo->{EmailAddress})
            {
                info "Medlem $customerid mangler epostadresse.";
                $show_id = $memberinfo if $opts{'i'};
            }

            if ( $members{$companyid}->{Name} ne
                 $memberinfo->{ZUsrArbeidsgiver} ) {
                error "Medlem $customerid har inkonsistent navn på firma (firma: '$members{$companyid}->{Name}' != member: '$memberinfo->{ZUsrArbeidsgiver}')";
                $show_id = $memberinfo;
                $show_nextid = $members{$companyid};
            }
        } else {
            # Personlige medlemskapstyper
            if (($memberinfo->{ZUsrArbeidsgiverNo} or
                $memberinfo->{ZUsrArbeidsgiver}) and
                "Æresmedlem" ne $memberinfo->{ZUsrMedlemstatus})
            {
                # unntatt æresmedlemmer som enten kan være personlig
                # eller del av bedrift.
                error "Personlig medlem $customerid har referanse til firmamedlem.";
                $show_id = $memberinfo;
            }
            $show_id = $memberinfo if is_usenix_missing($memberinfo);
            if (!$memberinfo->{EmailAddress})
            {
                info "Medlem $customerid mangler epostadresse.";
                $show_id = $memberinfo if $opts{'i'};
            }
        }
        show_memberinfo($show_id) if $show_id;
        show_memberinfo($show_nextid) if $show_nextid;
    }
}
