package series_events;

use strict;
use warnings;
no warnings 'redefine';

use Data::Dumper;
use Date::Calc;
use markup();

use db();
use log();
use time();
use uac();
use events();
use series();
use series_dates();
use studios();
use studio_timeslot_dates();
use event_history();
use images();

# check permissions, insert and update events related to series

#use base 'Exporter';
our @EXPORT_OK = qw(
  check_permission
  save_content
  save_event_time
  insert_event
  delete_event
  set_playout_status
);

sub debug;

# update main fields of the event by id
# do not check for project,studio,series
# all changed columns are returned for history handling
sub save_content($$) {
    my $config = shift;
    my $entry  = shift;

    return undef unless defined $entry->{id};

    for my $attr ( keys %$entry ) {
        next unless defined $entry->{$attr};
        $entry->{$attr} =~ s/^\s+//g;
        $entry->{$attr} =~ s/\s+$//g;
    }

    for my $attr ( 'image', 'series_image' ) {
        $entry->{$attr} = images::normalizeName( $entry->{$attr} ) if defined $entry->{$attr};
    }

    for my $attr ( 'content', 'topic' ) {
        next unless defined $entry->{$attr};
        if (($entry->{content_format}//'') eq 'markdown'){
            $entry->{ 'html_' . $attr } = markup::markdown_to_html( $entry->{$attr} );
        }else{
            $entry->{ 'html_' . $attr } = markup::creole_to_html( $entry->{$attr} );
        }
    }

    $entry->{modified_at} = time::time_to_datetime( time() );
    #update only existing atributes

    #TODO: double check series_name (needed for reassignment but not for editing...)
    my @keys = ();
    for my $key (
        'series_name',  'title',              'excerpt',            'content',
        'html_content', 'user_title',         'user_excerpt',       'topic',
        'html_topic',   'episode',            'image',              'image_label',
        'series_image', 'series_image_label', 'podcast_url',        'archive_url',
        'live',         'published',          'playout',            'archived',
        'rerun',        'draft',              'disable_event_sync', 'modified_by',
        'content_format'
      )
    {
        push @keys, $key if defined $entry->{$key};
    }
    $entry->{episode}   = undef if ( defined $entry->{episode} ) && ( $entry->{episode} eq '0' );
    $entry->{published} = 0     if ( defined $entry->{draft} )   && ( $entry->{draft} eq '1' );

    my $values = join( ",", map { $_ . '=?' } (@keys) );
    my @bind_values = map { $entry->{$_} } (@keys);

    push @bind_values, $entry->{id};
    my $query = qq{
		update calcms_events 
		set    $values
		where  id=?
	};

    my $dbh = db::connect($config);
    my $result = db::put( $dbh, $query, \@bind_values );
    unless ( defined $result ) {
        print STDERR "error on updating event\n";
        return undef;
    }

    return $entry;
}

sub set_episode{
    my $config = shift;
    my $entry  = shift;

    return undef unless defined $entry->{id};
    return undef unless defined $entry->{episode};

    my $query = qq{
		update calcms_events 
		set    episode=?
		where  id=?
	};
    my $bind_values= [ $entry->{episode}, $entry->{id} ];
    my $dbh = db::connect($config);
    my $result = db::put( $dbh, $query, $bind_values );
    unless ( defined $result ) {
        print STDERR "error on setting episode in event\n";
        return undef;
    }
    return $entry;
}

# save event time by id
# do not check project, studio, series
# for history handling all changed columns are returned
sub save_event_time($$) {
    my $config = shift;
    my $entry  = shift;

    return undef unless defined $entry->{id};
    return undef unless defined $entry->{duration};
    return undef unless defined $entry->{start_date};

    my $dbh   = db::connect($config);
    my $event = {
        id    => $entry->{id},
        start => $entry->{start_date},
        end   => time::add_minutes_to_datetime( $entry->{start_date}, $entry->{duration} )
    };

    my $day_start = $config->{date}->{day_starting_hour};
    my $event_hour = int( ( split( /[\-\:\sT]/, $event->{start} ) )[3] );

    my @update_columns = ();
    my $bind_values    = [];
    push @update_columns, 'start=?';
    push @$bind_values,   $event->{start};

    push @update_columns, 'end=?';
    push @$bind_values,   $event->{end};

    # add start date
    my $start_date = time::add_hours_to_datetime( $event->{start}, -$day_start );
    push @update_columns, 'start_date=?';
    push @$bind_values,   $start_date;
    $event->{start_date} = $start_date;

    # add end date
    my $end_date = time::add_hours_to_datetime( $event->{end}, -$day_start );
    push @update_columns, 'end_date=?';
    push @$bind_values,   $end_date;
    $event->{end_date} = $end_date;

    my $update_columns = join( ",\n", @update_columns );
    my $update_sql = qq{
		update calcms_events
		set 	$update_columns
		where	id=?
	};
    push @$bind_values, $event->{id};

    db::put( $dbh, $update_sql, $bind_values );
    return $event;
}

sub set_playout_status ($$) {
    my $config = shift;
    my $entry  = shift;

    return undef unless defined $entry->{project_id};
    return undef unless defined $entry->{studio_id};
    return undef unless defined $entry->{start};
    return undef unless defined $entry->{playout};

    my $dbh = db::connect($config);

    # check if event is assigned to project and studio
    my $sql = qq{
		select  se.event_id event_id
		from calcms_series_events se, calcms_events e
        where 
            se.event_id=e.id
        and e.start=?
		and se.project_id=?
		and se.studio_id=?
	};
    my $bind_values = [ $entry->{start}, $entry->{project_id}, $entry->{studio_id} ];

    my $events = db::get( $dbh, $sql, $bind_values );
    return undef if scalar(@$events) != 1;
    my $event_id = $events->[0]->{event_id};
    $sql = qq{
		update  calcms_events
		set 	playout=?
		where	id=?
		and     start=?
	};
    $bind_values = [ $entry->{playout}, $event_id, $entry->{start} ];
    my $result = db::put( $dbh, $sql, $bind_values );
    return $result;
}

# is event assigned to project, studio and series?
sub is_event_assigned($$) {
    my $config = shift;
    my $entry  = shift;

    return 0 unless defined $entry->{project_id};
    return 0 unless defined $entry->{studio_id};
    return 0 unless defined $entry->{series_id};
    return 0 unless defined $entry->{event_id};

    my $dbh = db::connect($config);

    my $sql = q{
        select * from calcms_series_events 
        where project_id=? and studio_id=? and series_id=? and event_id=?
    };
    my $bind_values = [ $entry->{project_id}, $entry->{studio_id}, $entry->{series_id}, $entry->{event_id} ];
    my $results = db::get( $dbh, $sql, $bind_values );

    return 1 if scalar @$results >= 1;
    return 0;
}

sub delete_event ($$) {
    my $config = shift;
    my $entry  = shift;

    return undef unless defined $entry->{project_id};
    return undef unless defined $entry->{studio_id};
    return undef unless defined $entry->{series_id};
    return undef unless defined $entry->{event_id};
    return undef unless defined $entry->{user};

    #is event assigned to project, studio and series?
    unless ( is_event_assigned( $config, $entry ) == 1 ) {
        print STDERR
"cannot delete event with project_id=$entry->{project_id}, studio_id=$entry->{studio_id}, series_id=$entry->{series_id}, event_id=$entry->{event_id}";
        return 0;
    }

    event_history::insert_by_event_id( $config, $entry );

    #delete the association
    series::unassign_event( $config, $entry );

    # delete the event
    my $dbh = db::connect($config);
    my $sql = q{
        delete from calcms_events 
        where id=?
    };
    my $bind_values = [ $entry->{event_id} ];
    db::put( $dbh, $sql, $bind_values );

    return 1;
}

#check permissions
# options:           conditions (studio_id, series_id,...)
# key permission:    permissions to be checked (one of)
# key check_for:     user, studio, series, events, schedule
# return error text or 1 if okay
sub check_permission($$) {
    my $request = shift;
    my $options = shift;

    return "missing permission at check" unless defined $options->{permission};
    return "missing check_for at check"  unless defined $options->{check_for};
    return "missing user at check"       unless defined $request->{user};
    return "missing project_id at check" unless defined $options->{project_id};
    return "missing studio_id at check"  unless defined $options->{studio_id};
    return "missing series_id at check"  unless defined $options->{series_id};

    my $permissions = $request->{permissions};
    my $config      = $request->{config};

    my $studio_check = studios::check( $config, $options );
    return $studio_check if ( $studio_check ne '1' );
    print STDERR "check studio ok\n";

    my $project_check = project::check( $config, $options );
    return $project_check if ( $project_check ne '1' );
    print STDERR "check project ok\n";

    #check if permissions are set (like create_event)
    my $found = 0;
    for my $permission ( split /\,/, $options->{permission} ) {
        $found = 1 if ( defined $permissions->{$permission} ) && ( $permissions->{$permission} ) eq '1';
    }
    return 'missing permission to ' . $options->{permission} if $found == 0;
    delete $options->{permission};

    #convert check list to hash
    my $check = { map { $_ => 1 } @{ $options->{check_for} } };
    delete $options->{check_for};

    # is project assigned to studio
    return "studio is not assigned to project" unless project::is_studio_assigned( $config, $options ) == 1;

    #get studio names
    my $studios = studios::get(
        $config,
        {
            project_id => $options->{project_id},
            studio_id  => $options->{studio_id}
        }
    );
    return "unknown studio" unless defined $studios;
    return "unknown studio" unless scalar @$studios == 1;
    my $studio = $studios->[0];
    my $studio_name = $studio->{name} || '';

    #get series names
    my $series = series::get(
        $config,
        {
            project_id => $options->{project_id},
            studio_id  => $options->{studio_id},
            series_id  => $options->{series_id}
        }
    );
    my $series_name = $series->[0]->{series_name} || '';
    $series_name .= ' - ' . $series->[0]->{title} if $series->[0]->{series_name} ne '';

    my $draft = 0;
    $draft = 1 if ( defined $options->{draft} ) && ( $options->{draft} == 1 );

    #check all items from checklist
    if ( ( defined $check->{user} ) && ( uac::is_user_assigned_to_studio( $request, $options ) == 0 ) ) {
        return "User '$request->{user}' is not assigned to studio $studio_name ($options->{studio_id})";
    }

    if ( ( defined $check->{studio} ) && ( project::is_series_assigned( $config, $options ) == 0 ) ) {
        return
"Series '$series_name' ($options->{series_id}) is not assigned to studio '$studio_name' ($options->{studio_id})";
    }

    # check series and can user update events
    if ( ( defined $check->{series} ) && ( series::can_user_update_events( $request, $options ) == 0 ) ) {
        return "unknown series" unless defined $series;
        return "User $request->{user} cannot update events for series '$series_name' ($options->{series_id})";
    }

    # check series and can user create events
    if ( ( defined $check->{create_events} ) && ( series::can_user_create_events( $request, $options ) == 0 ) ) {
        return "unknown series" unless defined $series;
        return "User $request->{user} cannot create events for series '$series_name' ($options->{series_id})";
    }

    if (   ( $draft == 0 )
        && ( defined $check->{studio_timeslots} )
        && ( studio_timeslot_dates::can_studio_edit_events( $config, $options ) == 0 ) )
    {
        return "requested time is not assigned to studio '$studio_name' ($options->{studio_id})";
    }

    #check if event is assigned to user,project,studio,series,location
    if ( defined $check->{events} ) {
        return "missing event_id" unless defined $options->{event_id};
        my $result = series::is_event_assigned_to_user( $request, $options );
        return $result if $result ne '1';
    }

    # prevent editing events that are over for more than 14 days
    if ( ( $draft == 0 ) && ( defined $check->{event_age} ) ) {
        if (
            series::is_event_older_than_days(
                $config,
                {
                    project_id => $options->{project_id},
                    studio_id  => $options->{studio_id},
                    series_id  => $options->{series_id},
                    event_id   => $options->{event_id},
                    max_age    => 14
                }
            ) == 1
          )
        {
            return "show is over for more than 2 weeks"
              unless ( ( defined $permissions->{update_event_after_week} )
                && ( $permissions->{update_event_after_week} eq '1' ) );
        }
    }

    #check if schedule event exists for given date
    if ( ( $draft == 0 ) && ( defined $check->{schedule} ) ) {
        return "unknown series" unless defined $series;
        return "missing start_at at check_permission" unless defined $options->{start_date};

        #TODO: check "is_event_scheduled" if start_at could be moved to start_date
        $options->{start_at} = $options->{start_date};
        return "No event scheduled for series '$series_name' ($options->{series_id})"
          if ( series_dates::is_event_scheduled( $request, $options ) == 0 );
    }

    return '1';
}

#not handled, yet:
# responsible, status, rating, podcast_url, media_url, visible, time_of_day, recurrence, reference, created_at
# category, time_of_day,

#insert event
sub insert_event ($$) {
    my $config  = shift;
    my $options = shift;

    my $project_id = $options->{project_id};
    my $studio     = $options->{studio};
    my $serie      = $options->{serie};
    my $params     = $options->{event};
    my $user       = $options->{user};

    return 0 unless defined $studio;
    return 0 unless defined $serie;
    return 0 unless defined $params;
    return 0 unless defined $user;
    return 0 unless defined $studio->{location};

    my $projects = project::get( $config, { project_id => $project_id } );
    if ( scalar @$projects == 0 ) {
        print STDERR "project not found at insert event\n";
        return 0;
    }
    my $projectName = $projects->[0]->{name};
    my $event       = {
        project  => $projectName,
        location => $studio->{location},    # location from studio
    };

    #print '<pre>';
    $event = series_events::add_event_dates( $config, $event, $params );

    #get event content from series
    for my $attr (
        'program', 'series_name', 'title',       'excerpt', 'content', 'topic',
        'image',   'episode',     'podcast_url', 'archive_url'
      )
    {
        $event->{$attr} = $serie->{$attr} if defined $serie->{$attr};
    }
    $event->{series_image}       = $serie->{image}   if defined $serie->{image};
    $event->{series_image_label} = $serie->{licence} if defined $serie->{licence};

    #overwrite series values from parameters
    for my $attr (
        'program', 'series_name', 'title', 'user_title', 'excerpt',     'user_except',
        'content', 'topic',       'image', 'episode',    'podcast_url', 'archive_url'
      )
    {
        $event->{$attr} = $params->{$attr} if defined $params->{$attr};
    }
    
    if (($event->{'content_format'}//'') eq 'markdown'){
        $event->{'html_content'} = markup::markdown_to_html( $event->{'content'} ) if defined $event->{'content'};
        $event->{'html_topic'}   = markup::markdown_to_html( $event->{'topic'} )   if defined $event->{'topic'};
    }else{
        $event->{'html_content'} = markup::creole_to_html( $event->{'content'} ) if defined $event->{'content'};
        $event->{'html_topic'}   = markup::creole_to_html( $event->{'topic'} )   if defined $event->{'topic'};
    }

    #add event status
    for my $attr ( 'live', 'published', 'playout', 'archived', 'rerun', 'draft', 'disable_event_sync' ) {
        $event->{$attr} = $params->{$attr} || 0;
    }

    if ( $serie->{has_single_events} eq '1' ) {
        delete $event->{series_name};
        delete $event->{episode};
    }

    $event->{modified_at} = time::time_to_datetime( time() );
    $event->{created_at}  = time::time_to_datetime( time() );
    $event->{modified_by} = $user;

    my $dbh = db::connect($config);
    my $event_id = db::insert( $dbh, 'calcms_events', $event );

    #add to history
    $event->{project_id} = $project_id;
    $event->{studio_id}  = $studio->{id};
    $event->{series_id}  = $serie->{series_id};
    $event->{event_id}   = $event_id;
    event_history::insert( $config, $event );
    return $event_id;
}

#set start, end, start-date, end_date to an event
sub add_event_dates($$$) {
    my $config = shift;
    my $event  = shift;
    my $params = shift;

    #start and end datetime
    $event->{start} = $params->{start_date};
    $event->{end} = time::add_minutes_to_datetime( $params->{start_date}, $params->{duration} );

    #set program days
    my $day_start = $config->{date}->{day_starting_hour};
    $event->{start_date} = time::date_cond( time::add_hours_to_datetime( $event->{start}, -$day_start ) );
    $event->{end_date}   = time::date_cond( time::add_hours_to_datetime( $event->{end},   -$day_start ) );
    return $event;
}

sub update_series_images ($$) {
    my $config  = shift;
    my $options = shift;

    return "missing project_id"   unless defined $options->{project_id};
    return "missing studio_id"    unless defined $options->{studio_id};
    return "missing series_id"    unless defined $options->{series_id};
    return "missing series_image" unless defined $options->{series_image};

    #print "save $options->{series_image}\n";

    my $events = series::get_events(
        $config,
        {
            project_id => $options->{project_id},
            studio_id  => $options->{studio_id},
            series_id  => $options->{series_id},
        }
    );

    for my $event (@$events) {
        $event->{series_image} = $options->{series_image};
        series_events::save_content( $config, $event );
    }
}

sub error ($) {
    my $msg = shift;
    print "ERROR: $msg<br/>\n";
}

#do not delete last line!
1;
