package Cpanel::Pkgacct::Components::CCSPkgAcct;

# cpanel - /var/cpanel/perl/Cpanel/PkgAcct/Components/CCSPkgAcct.pm
#                                                   Copyright 2019 cPanel, L.L.C.
#                                                            All rights Reserved.
# copyright@cpanel.net                                          http://cpanel.net
# This code is subject to the cPanel license.  Unauthorized copying is prohibited

use strict;
use warnings;

use parent 'Cpanel::Pkgacct::Component';

use Try::Tiny;

use Cpanel::Autodie         ();
use Cpanel::Debug           ();
use Cpanel::SafeRun::Object ();
use Cpanel::JSON            ();
use DBD::Pg                 ();
use File::Path              ();

use lib '/var/cpanel/perl';

use Cpanel::CCS::DBUtils   ();
use Cpanel::CCS::Delegates ();
use Cpanel::CCS::Userdata  ();

=head1 NAME

Cpanel::Pkgacct::Component::CCSPkgAcct

=head1 SYNOPSIS

Used internally by pkgacct to 'perform' CCS related actions.

=head1 DESCRIPTION

Module that defines what to back up in a pkgacct tarball from CCS.

=head1 SUBROUTINES

=head2 perform

Backs up data from the account to $PKGROOT/calendar_and_contacts.

=cut

sub perform {
    my ($self) = @_;

    my $work_dir = $self->get_work_dir();
    my $ccs_dir  = $work_dir . "/calendar_and_contacts";
    my $user     = $self->get_user();

    my $users_hr = Cpanel::CCS::Userdata::get_cpanel_account_users_uuids($user);
    my @users    = keys %{ $users_hr->{'users'} };

    # Should be impossible to hit this condition, but be paranoid.
    return if ( !@users );

    # Create the dump dirs
    my %dump_types = (
        'calendars' => 'ics',
        'contacts'  => 'vcard',
    );
    for ( keys(%dump_types) ) { File::Path::make_path("$ccs_dir/$_"); }

    # Dump persistence data
    _debug("Saving CCS user persistence data...");
    Cpanel::JSON::DumpFile( $ccs_dir . '/persistence.json', $users_hr );

    # Connect to the DB, get the delegates
    my $dbh = Cpanel::CCS::DBUtils::get_dbh();

    # Dump delegation data
    my $delegates_ar = Cpanel::CCS::Delegates::get( Cpanel::CCS::Userdata::get_users($user), $dbh );
    _debug("Saving CCS user delegates data...");
    Cpanel::JSON::DumpFile( $ccs_dir . '/delegates.json', $delegates_ar );

    # XXX TODO Possibly paralellizing this could be advantageous
    foreach my $user ( keys( %{ $users_hr->{'users'} } ) ) {
        _debug("Processing calendar & contacts for '$user'...");
        my $uuid = $users_hr->{'users'}{$user};
        foreach my $type ( keys(%dump_types) ) {
            my ( $query_string, $query_args ) = _get_query_for_type( $type, $uuid );
            my $sth = $dbh->prepare($query_string);
            $sth->execute(@$query_args);
            my $num_rows = $sth->rows;
            next if !$num_rows;

            # Write the relevant file from the dump
            my $dump_file = "$ccs_dir/$type/${uuid}_${type}.$dump_types{$type}";

            Cpanel::Autodie::open( my $dh, ">", $dump_file );
            binmode( $dh, ":encoding(UTF-8)" ) or die "Can't set binmode to UTF-8 on $dump_file: $!";

            # Just emit all data, hold your nose while doing so.
            while ( my $text = $sth->fetch ) {
                for (@$text) {
                    my $txt = $_;
                    $txt =~ tr/'//d;
                    print $dh $txt;
                }
            }

            _debug("Done exporting $type objects for $user: Dumped $num_rows objects");
        }
    }

    return;
}

sub _get_query_for_type {
    my ( $type, $uuid ) = @_;

    my %querydata = (
        'calendars' => {
            'args'  => [ $uuid, '1', 'f' ],
            'query' => "SELECT icalendar_text
                FROM
                  calendar_object
                  INNER JOIN calendar_bind ON calendar_bind.calendar_resource_id = calendar_object.calendar_resource_id
                  INNER JOIN calendar_metadata ON calendar_metadata.resource_id = calendar_bind.calendar_resource_id
                  INNER JOIN calendar_home ON calendar_home.resource_id = calendar_bind.calendar_home_resource_id
                WHERE calendar_home.owner_uid = ?
                AND calendar_bind.bind_status = ?
                AND calendar_metadata.is_in_trash = ?;",
        },
        'contacts' => {
            'args'  => [ $uuid, 'f' ],
            'query' => "SELECT vcard_text
                FROM
                  addressbook_object
                  INNER JOIN addressbook_home ON addressbook_home.resource_id = addressbook_object.addressbook_home_resource_id
                WHERE addressbook_home.owner_uid = ?
                AND addressbook_object.is_in_trash = ?;",
        },
    );
    return ( $querydata{$type}{'query'}, $querydata{$type}{'args'} );
}

# I just got tired of writing this several times
sub _debug {
    return Cpanel::Debug::log_info(@_) if $Cpanel::Debug::level;
}

1;
