<?php

/*
 * This file is part of RaspiSMS.
 *
 * (c) Pierre-Lin Bonnemaison <plebwebsas@gmail.com>
 *
 * This source file is subject to the GPL-3.0 license that is bundled
 * with this source code in the file LICENSE.
 */

namespace controllers\internals;

use Exception;

    class Received extends StandardController
    {
        protected $model;

        /**
         * Return the list of entries for a user.
         *
         * @param int     $id_user        : User id
         * @param ?int    $limit          : Number of entry to return
         * @param ?int    $offset         : Number of entry to avoid
         * @param ?string $search         : String to search for
         * @param ?array  $search_columns : List of columns to search on
         * @param ?string $order_column   : Name of the column to order by
         * @param bool    $order_desc     : Should result be ordered DESC, if false order ASC
         * @param bool    $count          : Should the query only count results
         * @param bool    $unread         : Should only unread messages be returned
         *
         * @return array : Entrys list
         */
        public function datatable_list_for_user(int $id_user, ?int $limit = null, ?int $offset = null, ?string $search = null, ?array $search_columns = [], ?string $order_column = null, bool $order_desc = false, bool $count = false, bool $unread = false)
        {
            return $this->get_model()->datatable_list_for_user($id_user, $limit, $offset, $search, $search_columns, $order_column, $order_desc, $count, $unread);
        }

        /**
         * Return the list of unread messages for a user.
         *
         * @param int  $id_user  : User id
         * @param ?int $nb_entry : Number of entry to return
         * @param ?int $page     : Pagination, used to calcul offset, $nb_entry * $page
         *
         * @return array : Entrys list
         */
        public function list_unread_for_user(int $id_user, ?int $nb_entry = null, ?int $page = null)
        {
            return $this->get_model()->list_unread_for_user($id_user, $nb_entry, $nb_entry * $page);
        }

        /**
         * Create a received.
         *
         * @param $id_user : Id of user to create received for
         * @param int $id_phone : Id of the number the message was send with
         * @param $at : Reception date
         * @param $text : Text of the message
         * @param string $origin    : Number of the sender
         * @param string $status    : Status of the received message
         * @param bool   $command   : Is the sms a command
         * @param bool   $mms       : Is the sms a mms
         * @param array  $media_ids : Ids of the medias to link to received
         *
         * @return mixed : false on error, new received id else
         */
        public function create(int $id_user, int $id_phone, $at, string $text, string $origin, string $status = 'unread', bool $command = false, bool $mms = false, array $media_ids = [])
        {
            $received = [
                'id_user' => $id_user,
                'id_phone' => $id_phone,
                'at' => $at,
                'text' => $text,
                'origin' => $origin,
                'status' => $status,
                'command' => $command,
                'mms' => $mms,
            ];

            //use a transaction to ensure received and media links are created at the same time
            $this->bdd->beginTransaction();

            $id_received = $this->get_model()->insert($received);
            if (!$id_received) 
            {
                $this->bdd->rollBack();

                return false;
            }

            //Link medias
            $internal_media = new Media($this->bdd);
            foreach ($media_ids as $media_id)
            {
                $id_media_received = $internal_media->link_to($media_id, 'received', $id_received);
                if (!$id_media_received)
                {
                    $this->bdd->rollBack();

                    return false;
                }
            }

            //All ok, commit
            $success = $this->bdd->commit();
            if (!$success)
            {
                return false;
            }

            //Check if the received message is a SMS STOP and we must register it
            $internal_smsstop = new SmsStop($this->bdd);
            $is_stop = $internal_smsstop->check_for_stop($received['text']);
            if ($is_stop)
            {
                $stop_exists = (bool) $internal_smsstop->get_by_number_for_user($id_user, $origin);
                if ($stop_exists)
                {
                    return $id_received;
                }

                $internal_smsstop->create($id_user, $origin);

                //If stop response enabled, respond to user 
                //(this will happen only for first stop, any further stop will not trigger responses)
                $internal_setting = new Setting($this->bdd);
                $user_settings = $internal_setting->gets_for_user($id_user);

                if ((int) ($user_settings['smsstop_respond'] ?? false))
                {
                    $response = $user_settings['smsstop_response'];
                    $internal_scheduled = new Scheduled($this->bdd);
                    $internal_scheduled->create($id_user, (new \DateTime())->format('Y-m-d H:i:s'), $response, $id_phone, null, false, false, \models\SmsStop::SMS_STOP_TAG, [['number' => $origin, 'data' => '[]']]);
                }
            }

            return $id_received;
        }

        /**
         * Update a received message for a user to mark the message as read.
         *
         * @param int $id_user     : user id
         * @param int $id_received : received id
         *
         * @return bool : false on error, true on success
         */
        public function mark_as_read_for_user(int $id_user, int $id_received): bool
        {
            $received = [
                'status' => 'read',
            ];

            return (bool) $this->get_model()->update_for_user($id_user, $id_received, $received);
        }

        /**
         * Update a received message for a user to mark the message as unread.
         *
         * @param int $id_user     : user id
         * @param int $id_received : received id
         *
         * @return bool : false on error, true on success
         */
        public function mark_as_unread_for_user(int $id_user, int $id_received): bool
        {
            $received = [
                'status' => 'unread',
            ];

            return (bool) $this->get_model()->update_for_user($id_user, $id_received, $received);
        }

        /**
         * Return number of unread messages for a user.
         *
         * @param int $id_user : User id
         *
         * @return array
         */
        public function count_unread_for_user(int $id_user)
        {
            return $this->get_model()->count_unread_for_user($id_user);
        }

        /**
         * Return x last receiveds message for a user, order by date.
         *
         * @param int $id_user  : User id
         * @param int $nb_entry : Number of receiveds messages to return
         *
         * @return array
         */
        public function get_lasts_by_date_for_user(int $id_user, int $nb_entry)
        {
            return $this->get_model()->get_lasts_by_date_for_user($id_user, $nb_entry);
        }

        /**
         * Return receiveds for an origin and a user.
         *
         * @param int    $id_user : User id
         * @param string $origin  : Number who sent the message
         *
         * @return array
         */
        public function gets_by_origin_and_user(int $id_user, string $origin)
        {
            return $this->get_model()->gets_by_origin_and_user($id_user, $origin);
        }

        /**
         * Return receiveds for an origin and a user since a date.
         *
         * @param int    $id_user : User id
         * @param string $since   : Date we want messages since format Y-m-d H:i:s
         * @param string $origin  : Number who sent the message
         *
         * @return array
         */
        public function gets_since_date_by_origin_and_user(int $id_user, string $since, string $origin)
        {
            return $this->get_model()->gets_since_date_by_origin_and_user($id_user, $since, $origin);
        }

        /**
         * Get number of sended SMS for every date since a date for a specific user.
         *
         * @param int       $id_user : user id
         * @param \DateTime $date    : Date since which we want the messages
         *
         * @return array
         */
        public function count_by_day_since_for_user(int $id_user, $date)
        {
            $counts_by_day = $this->get_model()->count_by_day_since_for_user($id_user, $date);
            $return = [];

            foreach ($counts_by_day as $count_by_day)
            {
                $return[$count_by_day['at_ymd']] = $count_by_day['nb'];
            }

            return $return;
        }

        /**
         * Return all discussions (ie : numbers we have a message received from or sended to) for a user.
         *
         * @param int $id_user : User id
         *
         * @return array
         */
        public function get_discussions_for_user(int $id_user, ?int $nb_entry = null, ?int $page = null)
        {
            return $this->get_model()->get_discussions_for_user($id_user, $nb_entry, $page);
        }

        /**
         * Get SMS received since a date for a user.
         *
         * @param int $id_user : User id
         * @param $date : La date depuis laquelle on veux les SMS (au format 2014-10-25 20:10:05)
         *
         * @return array : Tableau avec tous les SMS depuis la date
         */
        public function get_since_by_date_for_user(int $id_user, $date)
        {
            return $this->get_model()->get_since_by_date_for_user($id_user, $date);
        }

        /**
         * Find messages received since a date for a certain origin and user.
         *
         * @param int $id_user : User id
         * @param $date : Date we want messages sinces
         * @param string $origin : Origin number
         *
         * @return array
         */
        public function get_since_by_date_for_origin_and_user(int $id_user, $date, string $origin)
        {
            return $this->get_model()->get_since_by_date_for_origin_and_user($id_user, $date, $origin);
        }

        /**
         * Find last received message for an origin and user.
         *
         * @param int    $id_user : User id
         * @param string $origin  : Origin number
         *
         * @return array
         */
        public function get_last_for_origin_and_user(int $id_user, string $origin)
        {
            return $this->get_model()->get_last_for_origin_and_user($id_user, $origin);
        }

        /**
         * Receive a SMS message.
         *
         * @param int $id_user  : Id of user to create sended message for
         * @param int $id_phone : Id of the phone the message was sent to
         * @param $text : Text of the message
         * @param string  $origin : Number of the sender
         * @param ?string $at     : Message reception date, if null use current date
         * @param string  $status : Status of a the sms. By default \models\Received::STATUS_UNREAD
         * @param bool    $mms    : Is the sms a mms
         * @param array   $medias : Empty array if no medias, or medias to create and link to the received message. Format : [[
         *                        string 'filepath' => local path to a readable copy of the media,
         *                        ?string 'extension' => extension to use for the file or null
         *                        ], ...]
         *
         * @return array : [
         *               bool 'error' => false if success, true else
         *               ?string 'error_message' => null if success, error message else
         *               ]
         */
        public function receive(int $id_user, int $id_phone, string $text, string $origin, ?string $at = null, string $status = \models\Received::STATUS_UNREAD, bool $mms = false, array $medias = []): array
        {
            $return = [
                'error' => false,
                'error_message' => null,
            ];

            $at = $at ?? (new \DateTime())->format('Y-m-d H:i:s');
            $is_command = false;

            //Process the message to check plus potentially execute command and anonymize text
            $internal_command = new Command($this->bdd);
            $response = $internal_command->analyze_and_process($id_user, $text);
            if (false !== $response)
            { //Received sms is a command an we must use anonymized text
                $is_command = true;
                $text = $response;
            }

            //We create medias to link to the sms
            $internal_media = new Media($this->bdd);
            $media_ids = [];
            if ($mms)
            {
                foreach ($medias as $media)
                {
                    try
                    {
                        $new_media_id = $internal_media->create($id_user, $media['filepath'], $media['extension']);
                        $media_ids[] = $new_media_id;
                    }
                    catch (\Throwable $t)
                    {
                        $return['error_message'] = $t->getMessage();

                        continue; //Better loose the media than the message
                    }
                }
            }

            //Cut received messages by 1000 chars max
            $text_parts = mb_str_split($text, 1000);
            foreach ($text_parts as $text)
            {
                $received_id = $this->create($id_user, $id_phone, $at, $text, $origin, $status, $is_command, $mms, $media_ids);
                if (!$received_id)
                {
                    $return['error'] = true;
                    $return['error_message'] = 'Impossible to insert the sms in database.';

                    return $return;
                }

                $received = [
                    'id' => $received_id,
                    'at' => $at,
                    'text' => $text,
                    'destination' => $id_phone,
                    'origin' => $origin,
                    'command' => $is_command,
                    'mms' => $mms,
                    'medias' => $internal_media->gets_in_for_user($id_user, $media_ids),
                ];

                $internal_webhook = new Webhook($this->bdd);
                $internal_webhook->trigger($id_user, \models\Webhook::TYPE_RECEIVE_SMS, $received);

                $internal_user = new User($this->bdd);
                $internal_user->transfer_received($id_user, $received);
            }

            return $return;
        }

        /**
         * Get the model for the Controller.
         */
        protected function get_model(): \models\Received
        {
            $this->model = $this->model ?? new \models\Received($this->bdd);

            return $this->model;
        }
    }
