#!/usr/bin/perl

# ---------------------------------------------------------------
# Prontus CMS
# http://www.prontus.cl
# by Altavoz.net
#
# licensed under LGPL license.
# http://www.prontus.cl/license.html
# ---------------------------------------------------------------
# ---------------------------------------------------------------
# PROPOSITO .
# -----------
# Cargar archivos adjuntos y multimedia de artículos Prontus a un almacenamiento S3
#
use strict;
use warnings;
use utf8;

BEGIN {
    use FindBin qw($Bin);
    my $pathLibsProntus = $Bin;
    unshift(@INC, $pathLibsProntus);
    $pathLibsProntus = "$pathLibsProntus/xcoding";
    unshift(@INC, $pathLibsProntus);
}

END {
    &tareas_salida();
}

use sigtrap 'handler' => \&signal_catch, 'INT';
use sigtrap 'handler' => \&signal_catch, 'TERM';
use sigtrap 'handler' => \&signal_catch, 'KILL';

use Net::Amazon::S3;
use JSON;
require HTTP::Headers;

# Captura STDERR
use lib_stdlog;
&lib_stdlog::set_stdlog();
use prontus_varglb; &prontus_varglb::init();
use mime_types;
use glib_fildir_03;
use lib_prontus;
use lib_artic;
use Artic;
use lib_waitlock;
use lib_s3;
use lib_maxrunning;
use glib_hrfec_03;
# para invocar a generar_xml_artic
use MIME::Base64;
use mime_types;
use lib_xcoding; &lib_xcoding::init();
# para invocar a generar_xml_artic

use Data::Dumper;

our $LOCK = '';
our $LOCKFH;
our $BD;

main: {
    lib_prontus::setUtf8();

    if (scalar @ARGV < 1) {
        print "Parámetros insuficientes. Modo de uso:\n";
        print "perl $0 <prontus_id>\no\n";
        print "perl $0 <prontus_id> <ts_articulo>\n";
        exit;
    }

    # Soporta un maximo de n copias corriendo.
    if (&lib_maxrunning::maxExcedido(1)) {
        exit;
    }

    $prontus_varglb::PRONTUS_ID = $ARGV[0];

    my $ts = '';
    if (defined($ARGV[1]) && $ARGV[1] =~ /^\d{14}$/) {
        $ts = $ARGV[1];
    } elsif ($ARGV[1]) {
        print "Parámetro [$ARGV[1]] no es válido\n";
        exit;
    }

    if (!lib_prontus::valida_prontus($prontus_varglb::PRONTUS_ID)) {
        print STDERR "Prontus ID no es válido\n";
        exit;
    }

    my $msg_load = &lib_prontus::load_config_by_id($prontus_varglb::PRONTUS_ID);
    if ($msg_load ne '') {
        print STDERR "Ha ocurrido un error: $msg_load\n";
        exit;
    }

    my $dst_base_mm    = $prontus_varglb::DIR_SERVER . $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'DIR_SITE'} . $prontus_varglb::DIR_EXMEDIA;
    my $dst_base_docs  = $prontus_varglb::DIR_SERVER . $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'DIR_SITE'} . $prontus_varglb::DIR_ASOCFILE;

    # borra directorios vacíos antes de buscar archivos para cargar
    system("find $dst_base_mm -mindepth 1 -type d -empty -delete") if (-d $dst_base_mm);
    system("find $dst_base_docs -mindepth 1 -type d -empty -delete") if (-d $dst_base_docs);

    # Conectar a BD
    my $msg;
    ($BD, $msg) = &lib_prontus::conectar_prontus_bd();
    if (!ref($BD)) {
        print STDERR $msg;
        print $msg;
        exit;
    }

    if (!lib_s3::check_config_s3()) {
        print STDERR "S3 no habilitado y/o configurado\n";
        exit;
    }

    my ($result, $msg_err) = &lib_prontus::get_url("https://".$prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'S3_ENDPOINT'}, 10);
    if ($msg_err) {
        print "Error al conectar con endpoint S3 [$msg_err]\n";
        exit;
    }

    print STDERR "[" , &glib_hrfec_03::get_dtime_pack4() , "] Inicio\n";

    if ($ts ne '') {
        &upload_content_s3($ts, $dst_base_mm, $dst_base_docs);
    } else {
        my @params = (0, time - $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'S3_ANTIGUEDAD_MINIMA'} * 86400);
        my $sql = "select ART_ID from ART where ART_S3 = ? AND ART_MODTIME <= ? order by ART_MODTIME ASC, ART_ID ASC";

        my $salida = &glib_dbi_03::ejecutar_sql_bind_param($BD, $sql, \@params, \($ts));
        if ($BD->errstr) {
            print STDERR $BD->errstr, "\n";
            exit;
        }

        while ($salida->fetch) {
            &upload_content_s3($ts, $dst_base_mm, $dst_base_docs);
        }
        $salida->finish;
    }
    print STDERR "[" , &glib_hrfec_03::get_dtime_pack4() , "] Terminado\n";
}

#-----------------------------------------------------------------------#
sub upload_content_s3 {
    my ($ts, $dst_base_mm, $dst_base_docs) = @_;

    my $path_files = "$dst_base_mm/".substr($ts, 0, 8)."$prontus_varglb::DIR_MMEDIA/*$ts*";

    print STDERR "[" , &glib_hrfec_03::get_dtime_pack4() , "] Procesar [$ts]\n";

    my @upload;
    my %patterns;
    my %hay_medias;
    # listar los archivos de media asociados al ts
    my %sizes;
    my @entries = glob "$path_files";
    foreach my $entry (@entries) {
        if (-d $entry) {
            my @archivos = &glib_fildir_03::lee_dir($entry);
            # hacemos una lista con los archivos generados
            foreach my $archivo (@archivos) {
                next if($archivo eq '.' || $archivo eq '..');
                # armamos la ruta completa
                my $filepath = "$entry/$archivo";
                # obtenemos la ruta relativa
                if ($filepath =~ /.*\/([^\/]+\/site\/\w+\/\d{8}\/mmedia\/(multimedia_\w+\d+)\d{14}.*)$/) {
                    $sizes{$2} = 0 if (!defined($sizes{$2}));
                    $sizes{$2} += -s $filepath;
                    push(@upload, $1);
                }
            }
        } elsif ($entry =~ /.*\/(([^\/]+\/site\/\w+\/\d{8}\/mmedia\/(multimedia_\w+\d+)\d{14}).*\.(\w+))$/) {
            $sizes{$3} = 0 if (!defined($sizes{$3}));
            $sizes{$3} += -s $entry;
            # no se suben json o block
            # si hay otro tipo de archivo, puede ser mp4, mp3, wav, ogg, etc se suben
            if ($4 ne 'json' && $4 ne 'block') {
                # hay archivos de medios no solo json
                $hay_medias{$3} = 1;
                $patterns{$2} = 1;
                push(@upload, $1);
            }
        }
    }

    # listar archivos asociados del articulo
    $path_files = "$dst_base_docs/".substr($ts, 0, 8)."/$ts/*";
    @entries = glob "$path_files";
    foreach my $entry (@entries) {
        if ($entry =~ /.*\/([^\/]+\/site\/\w+\/\d{8}\/\d{14}\/.+)$/) {
            push(@upload, $1)
        }
    }

    lib_s3::connect_s3();
    my $bucket = lib_s3::connect_bucket();

    # borrar todos los archivos de media asociados que seran reemplazados,
    # obtenidos en la busqueda de archivos multimedia a subir
    # por ej, si se subira multimedia_video1, borra todos los archivos de
    # multimedia_video1 antiguos, usa el patron <prontus_id>/site/mm/<fechac>/mmedia/multimedia_videoN<ts>
    foreach my $prefix (keys %patterns) {
        lib_s3::delete_files_pattern($bucket, $prefix);
    }

    # subir los archivos listados
    foreach my $file_name (@upload) {
        my $file = "$prontus_varglb::DIR_SERVER/$file_name";
        if (!lib_s3::upload_multipart($bucket, $file, $file_name)) {
            print STDERR "[" , &glib_hrfec_03::get_dtime_pack4() , "] Error al subir contenidos [$file_name]\n";
            print "[" , &glib_hrfec_03::get_dtime_pack4() , "] Error al subir contenidos [$file_name]\n";
            exit;
        }
    }

    # se asegura el xml mientras se edita y guarda
    $LOCK = "$prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'DIR_LOCKS'}/$ts.mutex";
    $LOCKFH = &lib_waitlock::lock_file($LOCK);

    # carga campos articulo
    my $fullpath_xml = "$prontus_varglb::DIR_SERVER$prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'DIR_SITE'}".
                    "$prontus_varglb::DIR_ARTIC/". substr($ts, 0, 8) ."/xml/$ts.xml";
    my %campos_art = %{lib_artic::carga_art_xml($fullpath_xml)};
    # marca articulo como ya procesado
    $campos_art{'_s3'} = 1;

    # completa tamaños individuales si faltan
    foreach my $campo (keys %campos_art) {
        if ($campo =~ /^multimedia_\w+$/) {
            next if(defined($campos_art{$campo.".bytes"}));
            $campos_art{$campo.".bytes"} = -s "$dst_base_mm/".substr($ts, 0, 8)."$prontus_varglb::DIR_MMEDIA/$campos_art{$campo}";
        }
        elsif ($campo =~ /^asocfile_\w+$/) {
            next if(defined($campos_art{$campo.".bytes"}));
            $campos_art{$campo.".bytes"} = -s "$dst_base_docs/".substr($ts, 0, 8)."/$ts/$campos_art{$campo}";
        }
    }

    # completa campos despues de agregar el tamaño en bytes
    lib_artic::completar_campos_artic_guardar($ts, \%campos_art);

    # tamaño completo de multimedias
    foreach my $key (keys %sizes) {
        next if (!$hay_medias{$key});
        # si el archivo de media existe significa que se acaba de subir al s3 por lo tanto hay que actualizar el tamaño
        # sino existe se conserva el valor presente en el artículo
        if ($key =~ /video/) {
            # sumamos el tamaño de las thumbnails y poster
            @entries = glob "$dst_base_mm/".substr($ts, 0, 8). "$prontus_varglb::DIR_THUMBS/$key$ts*";
            foreach my $entry (@entries) {
                $sizes{$key} += -s $entry;
            }
            $campos_art{$key.'.total'} = $sizes{$key};
        }
        # es un audio mp3
        elsif ($key =~ /audio/) {
            $campos_art{$key.'.total'} = $sizes{$key};
        }
    }

    # prefijo de adjuntos
    my $prefix_asoc = substr("$prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'DIR_SITE'}$prontus_varglb::DIR_ASOCFILE/".substr($ts, 0, 8)."/$ts/", 1);
    my %asocfiles;

    $campos_art{'_s3_media_size'} = 0;
    $campos_art{'_s3_asset_size'} = 0;
    foreach my $nom_campo (keys %campos_art) {

        # listamos los asociados para poder borrar despues los que ya no existen
        if (substr($nom_campo, 0, 14) eq '_hidd_asocfile') {
            $asocfiles{ "$prefix_asoc$campos_art{$nom_campo}"} = 1;
        } elsif ($nom_campo =~ /^(multimedia_\w+)\.total$/) {
            my $marca = $1;
            $campos_art{'_s3_media_size'} += $campos_art{$nom_campo};
            # el total incluye los archivos cargados y los archivos locales, debemos restar los locales
            @entries = glob "$dst_base_mm/".substr($ts, 0, 8). "$prontus_varglb::DIR_THUMBS/$marca$ts*";
            foreach my $entry (@entries) {
                $campos_art{'_s3_media_size'} -= -s $entry;
            }
            @entries = glob "$dst_base_mm/".substr($ts, 0, 8). "$prontus_varglb::DIR_MMEDIA/$marca$ts*";
            foreach my $entry (@entries) {
                $entry =~ /\.(\w+)$/;
                if ($1 eq 'json' && $1 ne 'block') {
                    $campos_art{'_s3_media_size'} -= -s $entry;
                }
            }

        } elsif ($nom_campo =~ /^asocfile_\w+\.bytes$/) {
            $campos_art{'_s3_asset_size'} += $campos_art{$nom_campo};
        }
        # marcamos medias y asocfiles como cargados en s3
        if ($nom_campo =~ /^(asocfile_\w+|multimedia_\w+)$/) {
            $campos_art{$1.".s3"} = 1;
        }
    }

    $campos_art{'_s3_size'} = $campos_art{'_s3_media_size'} + $campos_art{'_s3_asset_size'};

    if (!defined($campos_art{'_modtime'}) || $campos_art{'_modtime'} eq '') {
        $campos_art{'_modtime'} = (stat($fullpath_xml))[9];
    }

    # actualizar xml de artículo
    my $artic_obj;
    # se instancia el articulo con los campos nuevos
    unless($artic_obj = Artic->new(
                'prontus_id'         => $prontus_varglb::PRONTUS_ID,
                'public_server_name' => $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'PUBLIC_SERVER_NAME'},
                'cpan_server_name'   => $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'CPAN_SERVER_NAME'},
                'document_root'      => $prontus_varglb::DIR_SERVER,
                'ts'                 => $ts,
                'campos'             => \%campos_art,
                'no_check_dirs'      => 1)) {
        print STDERR "[Error] inicializando objeto articulo: $Artic::ERR\n";
        print "[Error] inicializando objeto articulo: $Artic::ERR\n";
        return;
    };

    $artic_obj->generar_xml_artic(1);

    my ($disk_size, $imag_size, $asset_size, $media_size) = $artic_obj->get_disk_size();
    $artic_obj->update_art_size(($campos_art{'_s3_size'} + $disk_size), $disk_size, $imag_size, $asset_size, $media_size);

    $artic_obj->_flush_xml();
    # xml de articulo ya se actualizó, se libera el mutex
    &lib_waitlock::unlock_file($LOCK, $LOCKFH);

    # Actualiza la tabla de articulos con los cambios hechos.
    $artic_obj->art_update_bd($BD);

    # borrar adjuntos que no existen en el articulo de S3
    # lista archivos usando el patron <prontus_id>/site/docs/<fechac>/<ts>/
    my @s3_files = lib_s3::list_files_pattern($bucket, $prefix_asoc);
    foreach my $remote (@s3_files) {
        # archivo no esta en el artículo
        if (!defined($asocfiles{$remote->key})) {
            lib_s3::delete_file_key($remote->key);
        }
    }

    # carga correcta y actualizacion de xml articulo, borra archivos locales
    foreach my $file_name (@upload) {
        print STDERR "[" , &glib_hrfec_03::get_dtime_pack4() , "] Borrar Local /$file_name\n";
        unlink "$prontus_varglb::DIR_SERVER/$file_name";
    }

    # borrar directorios vacios
    my $dir = "$dst_base_mm/".substr($ts, 0, 8);
    system("find $dir -mindepth 1 -type d -empty -delete") if (-d $dir);

    print STDERR "[" , &glib_hrfec_03::get_dtime_pack4() , "] Completado [$ts]\n";
}

# ---------------------------------------------------------------
# funcion para capturar las señales INT y TERM y logearlas
sub signal_catch {
    print STDERR  "Terminado por signal @_\n";
    exit(0);
}

sub tareas_salida {
    if ($LOCK ne '') {
        &lib_waitlock::unlock_file($LOCK, $LOCKFH);
    }
}
