#!/usr/bin/env perl

# Copyright 2018 BibLibre
#
# 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 <http://www.gnu.org/licenses>.

use Modern::Perl;

use Date::Format qw( time2str );
use File::Basename qw( basename );
use FindBin qw( $Bin );
use Getopt::Long qw( GetOptions );
use Locale::PO;
use YAML::XS;
use utf8;

use Koha::Database;

my $help;
my $po_file;
my $dump_pot;
my $force;
GetOptions(
    'help' => \$help,
    'po-file=s' => \$po_file,
    'dump-pot' => \$dump_pot,
    'force' => \$force,
) or die 'Error in command line arguments';

if ($help) {
    my $basename = basename($0);
    say <<"EOT";
Usage:
    $basename [--po-file FILE] [--force]
    $basename --dump-pot
    $basename --help

This script adds new fields and subfields for biblio and authority, new
authority types and new authorised values, for UNIMARC IFLA update

Options:
    --help
        Display this help

    --po-file FILE
        PO file containing translations

    --dump-pot
        Print a POT file containing all translatable strings and exit

    --force
        Force updating existing data
EOT

    exit 0;
}

my $defaults = YAML::XS::LoadFile("$Bin/data/defaults.yml");
my $authorised_values = YAML::XS::LoadFile("$Bin/data/authorised_values.yml");
my $authtypes = YAML::XS::LoadFile("$Bin/data/authtypes.yml");
my @authtags;
my @authsubfields;
for my $authfw (qw(default CLASS CO EXP FAM GENRE_FORM NP NTEXP NTWORK PA PERS PUB SAUTTIT SNC SNG TM TU WORK)) {
    my $file = YAML::XS::LoadFile("$Bin/data/auth/$authfw.yml");
    push @authtags, @{ $file->{authtags} };
    push @authsubfields, @{ $file->{authsubfields} };
}
my $biblio = YAML::XS::LoadFile("$Bin/data/biblio/default.yml");
my @tags = @{ $biblio->{tags} };
my @subfields = @{ $biblio->{subfields} };

my $translations = {};
if ($dump_pot) {
    $translations->{''} = Locale::PO->new(
        -msgid => '',
        -msgstr => "Project-Id-Version: Koha\n" .
            "POT-Creation-Date: " . time2str('%Y-%m-%d %R%z', time) . "\n" .
            "MIME-Version: 1.0\n" .
            "Content-Type: text/plain; charset=UTF-8\n" .
            "Content-Transfer-Encoding: 8bit\n",
    );
    while (my ($category, $values) = each %$authorised_values) {
        foreach my $authorised_value (@$values) {
            $translations->{$authorised_value->{lib}} = Locale::PO->new(
                -msgid => $authorised_value->{lib},
                -msgstr => '',
            );
        }
    }
    for my $tag (@tags) {
        $translations->{$tag->{liblibrarian}} = Locale::PO->new(
            -msgid => $tag->{liblibrarian},
            -msgstr => '',
        );
    }
    for my $subfield (@subfields) {
        $translations->{$subfield->{liblibrarian}} = Locale::PO->new(
            -msgid => $subfield->{liblibrarian},
            -msgstr => '',
        );
    }
    for my $authtype (@$authtypes) {
        $translations->{$authtype->{authtypetext}} = Locale::PO->new(
            -msgid => $authtype->{authtypetext},
            -msgstr => '',
        );
    }
    for my $authtag (@authtags) {
        $translations->{$authtag->{liblibrarian}} = Locale::PO->new(
            -msgid => $authtag->{liblibrarian},
            -msgstr => '',
        );
    }
    for my $authsubfield (@authsubfields) {
        $translations->{$authsubfield->{liblibrarian}} = Locale::PO->new(
            -msgid => $authsubfield->{liblibrarian},
            -msgstr => '',
        );;
    }

    Locale::PO->save_file_fromhash("$Bin/language/template.pot", $translations, 'utf8');

    exit 0;
}

if ($po_file) {
    $translations = Locale::PO->load_file_ashash($po_file, 'utf8');
}

sub t {
    my ($string) = @_;

    my $quoted_string = Locale::PO->quote($string);
    unless (exists $translations->{$quoted_string} and $translations->{$quoted_string}) {
        return $string;
    }

    return Locale::PO->dequote($translations->{$quoted_string}->msgstr);
}


my $schema = Koha::Database->new()->schema();
my $authorised_value_rs = $schema->resultset('AuthorisedValue');
my $authorised_value_category_rs = $schema->resultset('AuthorisedValueCategory');
my $marc_tag_structure_rs = $schema->resultset('MarcTagStructure');
my $marc_subfield_structure_rs = $schema->resultset('MarcSubfieldStructure');
my $auth_type_rs = $schema->resultset('AuthType');
my $auth_tag_structure_rs = $schema->resultset('AuthTagStructure');
my $auth_subfield_structure_rs = $schema->resultset('AuthSubfieldStructure');

my $av_defaults = $defaults->{av};
while (my ($category, $values) = each %$authorised_values) {
    foreach my $authorised_value (@$values) {
        foreach my $key (keys %$av_defaults) {
            unless (exists $authorised_value->{$key}) {
                $authorised_value->{$key} = $av_defaults->{$key};
            }
        }
        $authorised_value->{category} = $category;
        $authorised_value->{lib} = t($authorised_value->{lib});

        my $value = $authorised_value->{authorised_value};
        my $av = $authorised_value_rs->find({
            category => $category,
            authorised_value => $value,
        });
        if ($av) {
            say "Authorised value already exists ($category, $value)";
            if ($force) {
                say "Force mode is active, updating authorised value ($category, $value)";
                $av->update($authorised_value);
            }
            next;
        }

        my $cat = $authorised_value_category_rs->find($category);
        if (!$cat) {
            say "Adding authorised value category $category";
            $authorised_value_category_rs->create({
                category_name => $category,
            });
        }

        say "Adding authorised value ($category, $value)";
        $authorised_value_rs->create($authorised_value);
    }
}

my $tag_defaults = $defaults->{tag};
for my $tag (@tags) {
    foreach my $key (keys %$tag_defaults) {
        unless (exists $tag->{$key}) {
            $tag->{$key} = $tag_defaults->{$key};
        }
    }
    $tag->{liblibrarian} = t($tag->{liblibrarian});

    my $mts = $marc_tag_structure_rs->find('', $tag->{tagfield});
    if ($mts) {
        say "Field already exists: " . $tag->{tagfield};
        if ($force) {
            say "Force mode is active, updating field " . $tag->{tagfield};
            $mts->update($tag);
        }
        next;
    }

    say "Adding field " . $tag->{tagfield};
    $marc_tag_structure_rs->create($tag);
}

my @mss = $marc_subfield_structure_rs->search({ frameworkcode => '' });
my %tab_for_field;
foreach my $mss (@mss) {
    next if $mss->tab < 0;
    next if exists $tab_for_field{$mss->tagfield};
    $tab_for_field{$mss->tagfield} = $mss->tab;
}

my $subfield_defaults = $defaults->{subfield};
for my $subfield (@subfields) {
    foreach my $key (keys %$subfield_defaults) {
        unless (exists $subfield->{$key}) {
            $subfield->{$key} = $subfield_defaults->{$key};
        }
    }
    $subfield->{liblibrarian} = t($subfield->{liblibrarian});

    # If other subfields exist in this field, use the same tab
    if (exists $tab_for_field{$subfield->{tagfield}}) {
        $subfield->{tab} = $tab_for_field{$subfield->{tagfield}};
    }

    my $mss = $marc_subfield_structure_rs->find('', $subfield->{tagfield}, $subfield->{tagsubfield});
    if ($mss) {
        say sprintf('Subfield already exists: %s$%s', $subfield->{tagfield}, $subfield->{tagsubfield});
        if ($force) {
            say sprintf('Force mode is active, updating subfield %s$%s', $subfield->{tagfield}, $subfield->{tagsubfield});

            # Do not modify the tab of existing subfield
            my %values = %$subfield;
            delete $values{tab};

            $mss->update(\%values);
        }
        next;
    }

    say sprintf('Adding subfield %s$%s', $subfield->{tagfield}, $subfield->{tagsubfield});
    $marc_subfield_structure_rs->create($subfield);
}

for my $authtype (@$authtypes) {
    $authtype->{authtypetext} = t($authtype->{authtypetext});

    my $at = $auth_type_rs->find($authtype->{authtypecode});
    if ($at) {
        say "Authority type already exists: " . $authtype->{authtypecode};
        if ($force) {
            say "Force mode is active, updating authority type " . $authtype->{authtypecode};
            $at->update($authtype);
        }
        next;
    }

    say "Adding authority type " . $authtype->{authtypecode};
    $auth_type_rs->create($authtype);
}

my $authtag_defaults = $defaults->{authtag};
for my $authtag (@authtags) {
    foreach my $key (keys %$authtag_defaults) {
        unless (exists $authtag->{$key}) {
            $authtag->{$key} = $authtag_defaults->{$key};
        }
    }
    $authtag->{liblibrarian} = t($authtag->{liblibrarian});

    my $ats = $auth_tag_structure_rs->find($authtag->{authtypecode}, $authtag->{tagfield});
    if ($ats) {
        say sprintf('Auth field already exists: %s (%s)', $authtag->{tagfield}, $authtag->{authtypecode});
        if ($force) {
            say sprintf('Force mode is active, updating auth field %s (%s)', $authtag->{tagfield}, $authtag->{authtypecode});
            $ats->update($authtag);
        }
        next;
    }

    say sprintf('Adding auth field %s (%s)', $authtag->{tagfield}, $authtag->{authtypecode});
    $auth_tag_structure_rs->create($authtag);
}

my @ass = $auth_subfield_structure_rs->search();
my %tab_for_authfield;
foreach my $ass (@ass) {
    my $authtypecode = $ass->get_column('authtypecode');
    $tab_for_authfield{$authtypecode} //= {};

    next if $ass->tab < 0;
    next if exists $tab_for_authfield{$authtypecode}->{$ass->tagfield};

    $tab_for_authfield{$authtypecode}->{$ass->tagfield} = $ass->tab;
}

my $authsubfield_defaults = $defaults->{authsubfield};
for my $authsubfield (@authsubfields) {
    foreach my $key (keys %$authsubfield_defaults) {
        unless (exists $authsubfield->{$key}) {
            $authsubfield->{$key} = $authsubfield_defaults->{$key};
        }
    }
    $authsubfield->{liblibrarian} = t($authsubfield->{liblibrarian});

    # If other subfields exist in this field, use the same tab
    if (exists $tab_for_authfield{$authsubfield->{authtypecode}}->{$authsubfield->{tagfield}}) {
        $authsubfield->{tab} = $tab_for_authfield{$authsubfield->{authtypecode}}->{$authsubfield->{tagfield}};
    }

    my $ass = $auth_subfield_structure_rs->find($authsubfield->{authtypecode}, $authsubfield->{tagfield}, $authsubfield->{tagsubfield});
    if ($ass) {
        say sprintf('Auth subfield already exists: %s$%s (%s)', $authsubfield->{tagfield}, $authsubfield->{tagsubfield}, $authsubfield->{authtypecode});
        if ($force) {
            say sprintf('Force mode is active, updating auth subfield %s$%s (%s)', $authsubfield->{tagfield}, $authsubfield->{tagsubfield}, $authsubfield->{authtypecode});

            # Do not modify the tab of existing subfield
            my %values = %$authsubfield;
            delete $values{tab};

            $ass->update(\%values);
        }
        next;
    }

    say sprintf('Adding auth subfield %s$%s (%s)', $authsubfield->{tagfield}, $authsubfield->{tagsubfield}, $authsubfield->{authtypecode});
    $auth_subfield_structure_rs->create($authsubfield);
}
