<?php
/** 
 * @package     VikBooking
 * @subpackage  core
 * @author      E4J s.r.l.
 * @copyright   Copyright (C) 2025 E4J s.r.l. All Rights Reserved.
 * @license     http://www.gnu.org/licenses/gpl-2.0.html GNU/GPL
 * @link        https://vikwp.com
 */

// No direct access
defined('ABSPATH') or die('No script kiddies please!');

/**
 * Worker helper trait to use for status change behaviors.
 * 
 * @since 1.18 (J) - 1.8 (WP)
 */
trait VBOTaskStatusHelperWorker
{
    /**
     * Mark the task as in progress.
     * 
     * @param   VBOTaskTaskregistry $task
     * 
     * @return  void
     */
    public function startWork(VBOTaskTaskregistry $task)
    {
        $now = JFactory::getDate('now', JFactory::getApplication()->get('offset'));

        $data = [
            'id' => $task->getID(),
            'workstartedon' => $now->toSql($local = false),
            'finishedby' => 0,
        ];

        if (!$task->get('beganon')) {
            // work started right now
            $data['beganon'] = $now->toSql($local = true);
        }

        if (!$task->get('beganby')) {
            // work started by the current user
            $data['beganby'] = $this->getWorkerID();
        }

        try {
            // update the task
            VBOTaskModelTask::getInstance()->update($data);
        } catch (Exception $error) {
            // ignore and go ahead
        }
    }

    /**
     * Mark the task as in pause.
     * 
     * @param   VBOTaskTaskregistry $task
     * 
     * @return  void
     */
    public function pauseWork(VBOTaskTaskregistry $task)
    {
        try {
            // update the task
            VBOTaskModelTask::getInstance()->update([
                'id' => $task->getID(),
                'workstartedon' => JFactory::getDate()->toSql($local = false),
                'realduration' => $this->calculateTotalDuration($task),
            ]);
        } catch (Exception $error) {
            // ignore and go ahead
        }
    }

    /**
     * Mark the task as finished.
     * 
     * @param   VBOTaskTaskregistry $task
     * 
     * @return  void
     */
    public function finishWork(VBOTaskTaskregistry $task)
    {
        $now = JFactory::getDate('now', JFactory::getApplication()->get('offset'));

        $data = [
            'id' => $task->getID(),
            'workstartedon' => $now->toSql($local = false),
            'realduration' => $this->calculateTotalDuration($task),
        ];

        if (!$task->get('finishedon')) {
            // work finished right now
            $data['finishedon'] = $now->toSql($local = true);
        }

        if (!$task->get('finishedby')) {
            // work finished by the current user
            $data['finishedby'] = $this->getWorkerID();
        }

        try {
            // update the task
            VBOTaskModelTask::getInstance()->update($data);
        } catch (Exception $error) {
            // ignore and go ahead
        }
    }

    /**
     * Displays the elsapsed duration for the ongoing work.
     * 
     * @param   VBOTaskTaskregistry  $task
     * @param   bool                 $paused
     * 
     * @return  string
     */
    public function displayOngoingWork(VBOTaskTaskregistry $task, bool $paused = false)
    {
        if ($paused) {
            // use elapsed duration
            $duration = $task->get('realduration');
        } else {
            // calculate duration on the fly
            $duration = $this->calculateTotalDuration($task);
        }

        if ($duration < 60) {
            return JText::translate('VBO_TASK_STATUS_DISPLAY_STARTED');
        }

        return JText::sprintf('VBO_TASK_STATUS_DISPLAY_ONGOING', $this->formatSeconds($duration));
    }

    /**
     * Displays the elsapsed duration for the finished work.
     * 
     * @param   VBOTaskTaskregistry  $task
     * 
     * @return  string
     */
    public function displayFinishedWork(VBOTaskTaskregistry $task)
    {
        // use elapsed duration
        $duration = $task->get('realduration');

        if ($duration < 60) {
            // do not display anything in case the duration is less than a minute
            return '';
        }

        return JText::sprintf('VBO_TASK_STATUS_DISPLAY_FINISHED', $this->formatSeconds($duration));
    }

    /**
     * Calculate the total elapsed seconds since the work started.
     * 
     * @param   VBOTaskTaskregistry  $task
     * 
     * @return  int
     */
    protected function calculateTotalDuration(VBOTaskTaskregistry $task)
    {
        $duration = (int) $task->get('realduration', 0);

        if ($task->get('finishedby')) {
            // task already finished, do not calculate extra time
            return $duration;
        }

        // obtain the date and time when the work started
        $workStartedOn = $task->get('workstartedon');

        if (!$workStartedOn) {
            // work never started, use the began date and time
            $workStartedOn = $task->get('beganon');
        }

        if (!$workStartedOn) {
            // work never began, use the due date and time
            $workStartedOn = $task->get('dueon');
        }

        if ($workStartedOn) {
            $workStartedOn = JFactory::getDate($workStartedOn);
            $now = JFactory::getDate('now');

            // calculate the difference between the current time and the start working time
            if ($now > $workStartedOn) {
                $diff = $workStartedOn->diff($now);
                $duration += $diff->s + $diff->i * 60 + $diff->h * 3600 + $diff->days * 86400;
            }
        }

        return $duration;
    }

    /**
     * Returns the identifier of the currently logged-in user.
     * 
     * @return  int  The user ID.
     */
    protected function getWorkerID()
    {
        if (JFactory::getApplication()->isClient('administrator')) {
            // return the CMS user ID
            return JFactory::getUser()->id;
        }

        // get logged in operator
        $operator = VikBooking::getOperatorInstance()->getOperatorAccount();

        if (!$operator) {
            // return the CMS user ID, if any
            return JFactory::getUser()->id;
        }

        return (int) $operator['id'];
    }

    /**
     * Helper method to format the specified seconds to the closest unit.
     * For example, 150 minutes will be formatted as "2 hours, 30 minutes".
     *
     * @param   int     $seconds
     *
     * @return  string
     */
    protected function formatSeconds(int $seconds)
    {
        // convert seconds in minutes
        $minutes = floor($seconds / 60);
        
        $units = [];

        while ($minutes >= 60) {
            if ($minutes >= 1440) {
                // calculate days
                $units[] = JText::plural('VBO_N_DAYS', floor($minutes / 1440));

                // recalculate remaining minutes
                $minutes = $minutes % 1440;
            } else {
                // calculate hours
                $units[] = JText::plural('VBO_N_HOURS', floor($minutes / 60));

                // recalculate remaining minutes
                $minutes = $minutes % 60;
            }
        }
        
        if ($minutes > 0) {
            $units[] = JText::plural('VBO_N_MINUTES', $minutes);
        }
            
        return implode(', ', $units);
    }
}
