<?php
/********************************************************************************
 * (c)1337 aheadware.com - All rights reserved
 ********************************************************************************/

/**
 * Class TB_Shared_Ent_TeamData_Appointment
 *
 * @property integer $id
 * @property integer $team_id
 * @property string $category_id
 * @property array $category_ids_js
 * @property string $visibility
 * @property string $subject
 * @property DateTime $start_dt
 * @property DateTime $end_dt
 * @property DateTime $deadline_dt
 * @property DateTime $deadline_reject_dt
 * @property integer $serial_id
 * @property integer|null $min_attendees
 * @property integer|null $max_attendees
 * @property string|null $location
 * @property string|null $comment
 * @property integer $price_factor
 * @property string $state
 */
class TB_Shared_Ent_TeamData_Appointment extends Francis_Db_Row
{

    const STATE_OPEN = 'open';
    const STATE_CANCELLED = 'cancelled';
    const STATE_DRAFT = 'draft';

    const VISIBLE_ALL = 'ALL';
    const VISIBLE_CATEGORIES = 'CATEGORIES';

    /**
     * Return appointment of given teamId
     *
     * @param integer $teamId
     * @return array TB_Shared_Ent_TeamData_Appointment[]
     */
    public static function getByTeamId( $teamId ) {
        $sql = 'SELECT * FROM ' . self::getTable() . ' WHERE team_id = :team_id ORDER BY `start_dt` ASC';
        return self::findMany( $sql, array( ':team_id' => $teamId ) );
    }

    public static function getCountByTeamId( $teamId )
    {
        $dbh = self::getDbh();
        $sql = 'SELECT COUNT(id) AS cnt FROM ' . self::getTable() . ' WHERE team_id = :team_id ';
        $stmt = $dbh->prepare( $sql );
        $stmt->execute( array(
            ':team_id' => $teamId
        ));
        $res = $stmt->fetch( PDO::FETCH_COLUMN );
        return $res;
    }

    public static function getCountByTeamIdAndAttendeeStatus( $teamId, $attendeeStatus = NULL )
    {
        $dbh = self::getDbh();
        $sqlArgs = array( ':team_id' => $teamId );
        $sql  = 'SELECT COUNT(app.id) AS cnt FROM ' . self::getTable() . ' AS app ';
        $sql .= 'LEFT JOIN attendee AS att ON att.appointment_id = app.id WHERE ';
        $sql .= 'app.team_id = :team_id ';
        if ( $attendeeStatus )
        {
            $sql .= 'AND att.status = :attendee_status';
            $sqlArgs[ ':attendee_status' ] = $attendeeStatus;
        }

        $stmt = $dbh->prepare( $sql );
        $stmt->execute( $sqlArgs );
        $res = $stmt->fetch( PDO::FETCH_COLUMN );
        return $res;
    }

    public static function getSerials( $rootId )
    {
        $sql = 'SELECT * FROM ' . self::getTable() . ' WHERE serial_id = :serial_id ORDER BY `start_dt` ASC';
        return self::findMany( $sql, array( ':serial_id' => $rootId) );
    }

    public static function getUpcomingBySerialId( $serialId )
    {
        $sql = 'SELECT * FROM ' . self::getTable() . ' WHERE serial_id = :serial_id AND end_dt > NOW() ORDER BY `start_dt` ASC';
        return self::findMany( $sql, array( ':serial_id' => $serialId) );
    }

    public static function getSubjectsForAutocompletion( $teamId )
    {
        $dbh = self::getDbh();
        $sql = 'SELECT DISTINCT(subject) FROM ' . self::getTable() . ' WHERE team_id = :team_id AND subject IS NOT NULL ORDER BY id DESC LIMIT 100';
        $stmt = $dbh->prepare( $sql );
        $stmt->execute( array(
            ':team_id' => $teamId
        ));
        $res = $stmt->fetchAll( PDO::FETCH_COLUMN );
        return $res;
    }

    public static function getLocationsForAutocompletion( $teamId )
    {
        $dbh = self::getDbh();
        $sql = 'SELECT DISTINCT(location) FROM ' . self::getTable() . ' WHERE team_id = :team_id AND location IS NOT NULL ORDER BY id DESC LIMIT 100';
        $stmt = $dbh->prepare( $sql );
        $stmt->execute( array(
            ':team_id' => $teamId
        ));
        $res = $stmt->fetchAll( PDO::FETCH_COLUMN );
        return $res;
    }

    /**
     * @param $teamId
     * @param $dtStart
     * @param $dtEnd
     * @return array
     */
    public static function getByTeamIdAndTimespan( $teamId, $dtStart, $dtEnd )
    {
        $sql = 'SELECT * FROM ' . self::getTable() . ' WHERE team_id = :team_id ';
        $sql .= ' AND state = "open" ';
        $sql .= ' AND start_dt > :start_dt AND end_dt <= :end_dt ';
        $sql .= 'ORDER BY `start_dt` ASC';
        return self::findMany(
            $sql,
            array(
                ':team_id' => $teamId,
                ':start_dt' => TB_Server_Utils_Helper::getMySqlDateTimeStringFromPHPDateTime( $dtStart ),
                ':end_dt' => TB_Server_Utils_Helper::getMySqlDateTimeStringFromPHPDateTime( $dtEnd )
            )
        );
    }

    /**
     * @param $profileId
     * @param bool $includePastAppointments
     */
    public static function getAcceptedByProfile( $profileId, $includePastAppointments = false )
    {
        $sql  = 'SELECT appointment.* FROM appointment ';
        $sql .= 'LEFT JOIN attendee ON appointment.id = attendee.appointment_id ';
        $sql .= 'WHERE ';
        $sql .= 'appointment.state = "open" AND ';
        $sql .= 'attendee.profile_id = :profile_id AND ';
        $sql .= 'attendee.status = "' . TB_Shared_Ent_TeamData_Attendee::STATUS_ACCEPTED . '" ';
        if ( false === $includePastAppointments )
        {
            $sql .= 'AND appointment.start_dt < UTC_TIMESTAMP()';
        }

        return self::findMany( $sql, array( ':profile_id' => $profileId ) );
    }

    public static function getAssignedForContractId( $contractId )
    {
        $sql  = 'SELECT appointment.* FROM appointment ';
        $sql .= 'LEFT JOIN attendee ON appointment.id = attendee.appointment_id ';
        $sql .= 'WHERE ';
        $sql .= 'attendee.contract_id = :contract_id ';

        return self::findMany( $sql, array( ':contract_id' => $contractId ) );
    }

    public static function getForTeamIdWithProfileAttendanceInteraction( $teamId, $profileId, $includePastAppointments = false )
    {
        $sql  = 'SELECT appointment.* FROM appointment ';
        $sql .= 'LEFT JOIN attendee ON appointment.id = attendee.appointment_id ';
        $sql .= 'WHERE ';
        $sql .= 'appointment.team_id = :team_id AND ';
        $sql .= 'attendee.profile_id = :profile_id ';
        if ( false === $includePastAppointments )
        {
            $sql .= 'AND appointment.start_dt < UTC_TIMESTAMP()';
        }

        return self::findMany( $sql, array( ':team_id' => $teamId, ':profile_id' => $profileId ) );
    }

    public static function getLogByGroupAndProfileId( $groupId, $profileId, $startDt, $endDt )
    {
        $sql  = 'SELECT a.*, app.subject, app.start_dt, app.end_dt ';
        $sql .= 'FROM `attendee` AS a ';
        $sql .= 'LEFT JOIN `appointment` AS app ON a.appointment_id = app.id ';
        $sql .= 'WHERE app.team_id = :team_id ';
        $sql .= 'AND a.profile_id = :profile_id ';
        $sql .= "AND a.has_attended = 'attended' ";
        $sql .= 'AND app.start_dt >= :start_dt ';
        $sql .= 'AND app.end_dt <= :end_dt ';
        $sql .= "AND app.state != '" . self::STATE_DRAFT . "' ";
        $sql .= "AND app.state != '" . self::STATE_CANCELLED . "' ";
        $sql .= 'ORDER BY app.start_dt ASC';

        $dbh = self::getDbh();

        $stmt = $dbh->prepare( $sql );
        $stmt->execute( array( ':team_id' => $groupId, ':profile_id' => $profileId, ':start_dt' => $startDt, ':end_dt' => $endDt ));
        $rows = $stmt->fetchAll( PDO::FETCH_ASSOC );

        return $rows;
    }

    public static function getByCourseCategoryId( $courseCategoryId )
    {
        $whereJson = json_encode( array( $courseCategoryId ) );
        $sql = 'SELECT * FROM ' . self::getTable() . ' WHERE ';
        $sql .= 'JSON_CONTAINS( `category_ids_js`, :where_json ) ';

        $params = array( ':where_json' => $whereJson );
        return self::findMany( $sql, $params );
    }

    /**
     * @param array $teamIds
     * @param array|null $filter  [ version : string, previous : boolean, appointmentCategoryIds : array ]
     * @return array
     */
    public static function getAllForTeamIds( array $teamIds, $filter = NULL )
    {
        $appointments = array();
        $filterGetOld = ( is_array( $filter ) && array_key_exists( 'previous', $filter ) ) ? $filter[ 'previous' ] : false;
        $filterCategories = isset( $filter ) && isset( $filter[ 'appointmentCategoryIds' ] ) ? $filter[ 'appointmentCategoryIds' ] : [];

        if ( 0 === count( $teamIds ) || 0 === count( $filterCategories ) ) return $appointments;

        $teamIds  = array_unique( $teamIds );

        $dbh = TB_Shared_Ent_TeamData_Profile::getDbh();
        $in  = str_repeat('?,', count( $teamIds ) - 1) . '?';
        $sql = "SELECT * FROM " . self::getTable() . " WHERE team_id IN ($in) ";
        $filterCondition = implode(
                " OR ",
                array_map( function( $filterCategoryId )
                {
                    return "JSON_CONTAINS( category_ids_js, " . '\'["' . $filterCategoryId . '"]\')';
                },
                $filterCategories
            )
        );
        if ( strlen( $filterCondition ) )
        {
            $sql .= " AND (" . $filterCondition . ") ";
        }
        /*
        $inCategories = str_repeat( '?,', count( $filterCategories ) - 1 ) . '?';
        $sql .= "AND COALESCE( category_id, -1 ) IN ( $inCategories ) ";
        */
        if ( false === $filterGetOld )
        {
            $sql .= "AND end_dt > UTC_TIMESTAMP() ";
        }
        $sql .= "ORDER BY start_dt ASC";
        $stmt = $dbh->prepare( $sql );
        $stmt->execute( array_merge( $teamIds ) );
        $appointments = $stmt->fetchAll( PDO::FETCH_CLASS, get_called_class() );

        /** @var Francis_Db_Row $ent */
        foreach( $appointments as $ent ) {
            $ent->afterLoad();
        }

        return $appointments;
    }

    /**
     * Delete all appointments by given team id
     * @param int $teamId
     * @return bool
     */
    public static function deleteByTeamId( $teamId ) {
        $sql = 'DELETE FROM ' . self::getTable() . ' WHERE team_id  = :team_id';
        return self::execute( $sql, array( ':team_id' => $teamId) );
    }

    /**
     * Return table name
     *
     * @return null|string
     */
    public static function getTable() {
        return 'appointment';
    }

    /**
     * Return db handler
     *
     * @return Francis_Db_MySql|null
     */
    public static function getDbh() {
        return TB_Shared_Db_TeamData::get();
    }

    /**
     * @return null|string
     */
    public function getName() {
        return $this->subject;
    }

    public function hasStarted()
    {
        $nowDt = new DateTime();
        return ( $this->start_dt < $nowDt );
    }

    public function isOver()
    {
        $nowDt = new DateTime();
        return ( $this->end_dt < $nowDt );
    }

    public function isDealineOver()
    {
        $nowDt = new DateTime();
        return ( $nowDt > $this->deadline_dt );
    }

    public function isDeadlineRejectOver()
    {
        $nowDt = new DateTime();
        return ( $nowDt > $this->deadline_reject_dt );
    }

    public function isSerial()
    {
        return ( 0 < (int)$this->serial_id );
    }

    public function isCancelled()
    {
        return ( $this->state === self::STATE_CANCELLED );
    }

    public function getRefId()
    {
        return 'appointment-' . $this->id;
    }

    public function isDraft()
    {
        return ( $this->state === self::STATE_DRAFT );
    }

    public function isVisibleByProfile( TB_Shared_Ent_TeamData_Profile $profile )
    {
        $isVisible = true;

        if ( $this->visibility === TB_Shared_Ent_TeamData_Appointment::VISIBLE_CATEGORIES &&
             false === $profile->isOwnerOfTeam( $this->team_id ) )
        {
            $isVisible = false;

            foreach( $this->category_ids_js as $cid )
            {
                if ( $profile->hasCategoryId( $cid ) )
                {
                    $isVisible = true;
                    break;
                }
            }

            if ( false === $this->isDraft() )
            {
                $attendee = TB_Shared_Ent_TeamData_Attendee::getByAppointmentIdAndProfileId( $this->id, $profile->id );
                if ( $attendee && $attendee->status === TB_Shared_Ent_TeamData_Attendee::STATUS_ACCEPTED )
                {
                    $isVisible = true;
                }
            }
        }

        return $isVisible;
    }

    /**
     * Called during deletion
     */
    public function deleteReferences() {

        // Delete all attendees
        $sql  = "DELETE FROM " . TB_Shared_Ent_TeamData_Attendee::getTable() . " ";
        $sql .=  "WHERE appointment_id = :appointment_id";
        $params = array( ':appointment_id' => $this->id );

        try {
            // Delete
            TB_Shared_Ent_TeamData_Attendee::execute( $sql, $params );
        } catch( Exception $e ) {
            TB_Server_Utils_Log::get()->log( $e );
        }

        // Delete all chats
        $sql  = "DELETE FROM " . TB_Shared_Ent_TeamData_Chat::getTable() . " ";
        $sql .=  "WHERE ref_id = :ref_id";
        $params = array( ':ref_id' => $this->getRefId() );

        try {
            // Delete
            TB_Shared_Ent_TeamData_Chat::execute( $sql, $params );
        } catch( Exception $e ) {
            TB_Server_Utils_Log::get()->log( $e );
        }

        // Delete all notifications
        $sql  = "DELETE FROM " . TB_Shared_Ent_TeamData_Notification::getTable() . " ";
        $sql .=  "WHERE ref_id = :ref_id";
        $params = array( ':ref_id' => $this->getRefId() );

        try {
            // Delete
            TB_Shared_Ent_TeamData_Notification::execute( $sql, $params );
        } catch( Exception $e ) {
            TB_Server_Utils_Log::get()->log( $e );
        }

        $sql  = "DELETE FROM " . TB_Shared_Ent_TeamData_Notification::getTable() . " ";
        $sql .=  "WHERE ref_id = :ref_id";
        $params = array( ':ref_id' => 'appointment-trainerupdate-' . $this->id );

        try {
            // Delete
            TB_Shared_Ent_TeamData_Notification::execute( $sql, $params );
        } catch( Exception $e ) {
            TB_Server_Utils_Log::get()->log( $e );
        }

        try
        {
            TB_Shared_Ent_TeamData_Job::deleteByRefId( 'appointment-reminder-' . $this->id );
        }
        catch( Exception $e )
        {
            TB_Server_Utils_Log::get()->log( $e );
        }

   }

    /**
     * @param TB_Shared_Ent_TeamData_Team $team
     */
   public static function createTeamAppointmentsForTeam( TB_Shared_Ent_TeamData_Team $team )
   {
       // For now - it works only for yoga
       if ( $team->category !== 'YOGA' && $team->category !== 'HORSES' && $team->category !== 'DOGSCHOOL' ) return;

       $startDt = new DateTime( "now", new DateTimeZone( 'UTC' ) );
       $startDt->setTime( 16, 0, 0 );
       $startDt->modify( '+2 day' );
       $endDt = clone $startDt;
       $endDt->modify( '+1 hour' );
       $deadlineDt = clone $endDt;
       $deadlineDt->modify( '-1 hour' );
       $rejectDt = clone $deadlineDt;
       $categories = [ $team->getDefaultCategoryId() ];

       $demoData = [];

       if ( $team->category === 'YOGA' )
       {
           $demoData = [
               [ 'subject' => 'Sunrise Yoga', 'state' => TB_Shared_Ent_TeamData_Appointment::STATE_OPEN, 'location' => 'Kurpark', 'comment' => 'Bitte bringt eine Yoga Matte mit.' ],
               [ 'subject' => 'Meditation', 'state' => TB_Shared_Ent_TeamData_Appointment::STATE_DRAFT, 'location' => 'Raum der Entspannung', 'comment' => 'Thema ist heute die Innere Stille.' ],
               [ 'subject' => 'Yoga II', 'state' => TB_Shared_Ent_TeamData_Appointment::STATE_OPEN, 'location' => '', 'comment' => 'Dehnung und Entspannung.' ]
           ];
       }
       else if ( $team->category === 'HORSES' )
       {
           $demoData = [
               [ 'subject' => 'Corona Versorgung', 'state' => TB_Shared_Ent_TeamData_Appointment::STATE_OPEN, 'location' => '', 'comment' => 'Bitte denkt an den Mindestabstand von 2 Metern.' ],
               [ 'subject' => 'Stallpflege', 'state' => TB_Shared_Ent_TeamData_Appointment::STATE_DRAFT, 'location' => '', 'comment' => '' ],
               [ 'subject' => 'Orga Treffen', 'state' => TB_Shared_Ent_TeamData_Appointment::STATE_OPEN, 'location' => 'Reitercafe', 'comment' => 'Vorbesprechung zur Jahresversammlung.' ]
           ];
       }
       else if ( $team->category === 'DOGSCHOOL' )
       {
           $demoData = [
               [ 'subject' => "❤ Leinenführkurs | mit Esther", 'state' => TB_Shared_Ent_TeamData_Appointment::STATE_OPEN, 'location' => 'Innenstadt', 'comment' => 'Bitte denkt an eine kurze Leine.' ],
               [ 'subject' => "Mantrailing M2", 'state' => TB_Shared_Ent_TeamData_Appointment::STATE_DRAFT, 'location' => 'Hundeplatz', 'comment' => 'Denkt an eure Geruchsprobe.' ],
               [ 'subject' => "★ Tageskurs: Junge Hunde erziehen | mit Marion", 'state' => TB_Shared_Ent_TeamData_Appointment::STATE_OPEN, 'location' => 'Hundeschule', 'comment' => 'Wasser + Leckerlies nicht vergessen.' ]
           ];
       }

       foreach( $demoData as $dd )
       {
           $entApp = new TB_Shared_Ent_TeamData_Appointment();
           $entApp->team_id = $team->id;
           $entApp->visibility = TB_Shared_Ent_TeamData_Appointment::VISIBLE_ALL;

           $entApp->subject = $dd[ 'subject' ];
           $entApp->state = $dd ['state' ];
           $entApp->location = $dd[ 'location' ];
           $entApp->comment = $dd[ 'comment' ];
           $entApp->start_dt = $startDt;
           $entApp->end_dt = $endDt;
           $entApp->deadline_dt = $deadlineDt;
           $entApp->deadline_reject_dt = $rejectDt;
           $entApp->category_ids_js = $categories;
           $entApp->save();

           if ( $entApp->state === TB_Shared_Ent_TeamData_Appointment::STATE_OPEN )
           {
               TB_Server_Core_JobManager::createForAppointment( $entApp );
           }

           // Modify for next
           $startDt->modify( '+1 week' );
           $endDt->modify( '+1 week' );
           $deadlineDt->modify( '+1 week' );
           $rejectDt->modify( '+1 week' );
       }
   }

   public static function getTeamAppointmentsForAttendedProfileId( $teamId, $profileId )
   {
       $sql  = 'SELECT a.id AS `attendee_id`, a.contract_id AS `attendee_contract_id`, app.* FROM `attendee` AS a ';
       $sql .= 'LEFT JOIN appointment AS `app` ON a.appointment_id = app.id ';
       $sql .= 'WHERE ';
       $sql .= 'app.team_id = :team_id AND a.profile_id = :profile_id AND ';
       $sql .= 'app.state = "open" AND ';
       $sql .= 'app.start_dt < NOW() AND ';
       $sql .= 'a.has_attended = "attended"';//  AND app.start_dt < NOW()';

       $dbh = self::getDbh();

       $stmt = $dbh->prepare( $sql );
       $stmt->execute( array( ':team_id' => $teamId, ':profile_id' => $profileId ) );
       $rows = $stmt->fetchAll( PDO::FETCH_ASSOC );

       return $rows;
   }

   public static function getAppointmentAttendeesForTeamIdAndTimespan( $teamId, $startDate, $endDate )
   {
       $sql  = 'SELECT a.id AS `attendee_id`, a.profile_id AS `profile_id`, ';
       $sql .= 'app.id AS `appointment_id`, app.subject AS `appointment_subject`, app.start_dt AS `appointment_start`, ';
       $sql .= 'app.end_dt AS `appointment_end`, app.location AS `appointment_location`, ';
       $sql .= 'wrk.hourly_rate AS `hourly_rate`, wrk.additional_pay AS `additional_pay`, wrk.gross_hour AS `gross_hour`, ';
       $sql .= 'wrk.net_hour AS `net_hour`, wrk.pause_hour AS `pause_hour`, wrk.notice AS `notice` ';
       $sql .= 'FROM `attendee` AS a ';
       $sql .= 'LEFT JOIN appointment AS `app` ON a.appointment_id = app.id ';
       $sql .= 'LEFT JOIN kkspecial_profile_workshift AS `wrk` ON a.appointment_id = wrk.appointment_id AND a.profile_id = wrk.profile_id ';
       $sql .= 'WHERE ';
       $sql .= 'app.team_id = :team_id AND ';
       $sql .= 'app.start_dt >= :start_date AND ';
       $sql .= 'app.end_dt <= :end_date AND ';
       $sql .= 'app.end_dt <= NOW() AND ';
       $sql .= 'app.state = "open" AND ';
       //$sql .= 'a.status = "accepted" AND ';
       $sql .= 'a.has_attended = "attended" AND ';
       $sql .= 'a.appointment_id = app.id ';

       $dbh = self::getDbh();

       $stmt = $dbh->prepare( $sql );
       $stmt->execute( array( ':team_id' => $teamId, ':start_date' => $startDate, ':end_date' => $endDate ) );
       $rows = $stmt->fetchAll( PDO::FETCH_ASSOC );

       return $rows;
   }
}