<?php
// phpcs:ignoreFile WordPress.Security.NonceVerification.Recommended
// phpcs:ignoreFile WordPress.Security.NonceVerification.Missing

class SLN_Shortcode_Salon
{
    const NAME = 'salon_booking';
    const STEP_KEY = 'sln_step_page';
    const STEP_DEFAULT = 'date';

    private $plugin;
    private $attrs;

    private $steps;
    private $currentStep;

    function __construct(SLN_Plugin $plugin, $attrs)
    {
        $this->plugin = $plugin;
        $this->attrs = $attrs;
    }

    public static function init(SLN_Plugin $plugin)
    {
        add_shortcode(self::NAME, array(__CLASS__, 'create'));
    }

    public static function create($attrs)
    {
        SLN_TimeFunc::startRealTimezone();
        SLN_Action_InitScripts::preloadEnqueueScript();

        $obj = new self(SLN_Plugin::getInstance(), $attrs);

        $ret = $obj->execute();

        SLN_TimeFunc::endRealTimezone();

        return $ret;
    }

    public function execute()
    {
        return $this->dispatchStep($this->getCurrentStep());
    }

    private function dispatchStep($curr)
    {
        $found = false;
        $obj = null;
        $settings = $this->plugin->getSettings();
        SLN_Plugin::addLog(sprintf('[Wizard] dispatchStep requested="%s"', $curr));

        $steps = $this->maybeReverseSteps($this->getSteps());
        $stepsList = $this->maybeReverseSteps($this->getStepsList());
        foreach ($steps as $step) {
            if ($curr == $step || $found) {
                $found = true;
                $this->currentStep = $step;
                $obj = $this->getStepObject($step);
                if (!$obj->isValid()) {
                    SLN_Plugin::addLog(sprintf('[Wizard] step="%s" is not valid, rendering current output', $step));
                    return $this->render($obj->render());
                }

                if (!$settings->isFormStepsAltOrder()) {
                    if ($step === 'details' && $curr === 'details') {
                        if (!$obj->setResources()) {
                            SLN_Plugin::addLog('[Wizard] details step setResources() returned false');
                            return $this->render($obj->render());
                        }
                        if ($settings->isAttendantsEnabled() && !$obj->setAttendantsAuto()) {
                            SLN_Plugin::addLog('[Wizard] details step setAttendantsAuto() returned false');
                            return $this->render($obj->render());
                        }
                    }
                }else{
                    if ($step === 'date') {
                        if (!$obj->setResources()) {
                            SLN_Plugin::addLog('[Wizard] date step setResources() returned false');
                            return $this->render($obj->render());
                        }
                        if ($settings->isAttendantsEnabled() && !$obj->setAttendantsAuto()) {
                            SLN_Plugin::addLog('[Wizard] date step setAttendantsAuto() returned false');
                            return $this->render($obj->render());
                        }
                    }
                }
                SLN_Plugin::addLog(sprintf('[Wizard] step="%s" rendered successfully', $step));
            }
        }
        if (!$found) {
            SLN_Plugin::addLog(sprintf('[Wizard] requested step "%s" not in immediate sequence, attempting fallback chain', $curr));
            $index = array_search($curr, $stepsList);
            if ($index !== false) {
                $_stepsList = array_slice($stepsList, $index);
                foreach ($steps as $step) {
                    foreach ($_stepsList as $_step) {
                        if ($step == $_step || $found) {
                            $found = true;
                            $this->currentStep = $_step;
                            $obj = $this->getStepObject($_step);
                            if (!$obj->isValid()) {
                                SLN_Plugin::addLog(sprintf('[Wizard] fallback step="%s" not valid, rendering current output', $_step));
                                return $this->render($obj->render());
                            }
                            SLN_Plugin::addLog(sprintf('[Wizard] fallback step="%s" rendered successfully', $_step));
                        }
                    }
                }
            }
            if (!$found) {
                SLN_Plugin::addLog(sprintf('[Wizard] unable to resolve step for request "%s"', $curr));
            }
        }

        // SAFETY NET: never return empty output. The loops above only return when a step
        // needs rendering (its isValid() returned false). If every resolved step validated
        // and auto-advanced, execution reaches here and the method would otherwise return
        // null — producing a blank wizard / "0" output. Render the last resolved step (e.g.
        // the final "thankyou" step) so the user always sees the wizard.
        if ($obj instanceof SLN_Shortcode_Salon_Step) {
            SLN_Plugin::addLog(sprintf('[Wizard] dispatchStep safety-net render of resolved step "%s"', $this->currentStep));
            return $this->render($obj->render());
        }

        // Last resort: resolve the requested/current step directly.
        SLN_Plugin::addLog(sprintf('[Wizard] dispatchStep last-resort render for requested "%s"', $curr));
        return $this->render($this->getStepObject($this->getCurrentStep())->render());
    }

    /**
     * @param $step
     * @return SLN_Shortcode_Salon_Step
     * @throws Exception
     */
    public function getStepObject($step)
    {
        $obj = apply_filters('sln.shortcode_salon.getStepObject', null, $this, $step);
        if($obj)
            return $obj;

        $class = __CLASS__.'_'.ucwords($step).'Step';
        $class_alt = __CLASS__.'_'.ucwords($step).'AltStep';

        if ($this->plugin->getSettings()->isFormStepsAltOrder() && class_exists($class_alt)) {
            $obj = new $class_alt($this->plugin, $this, $step);
        }
        else {
            $obj = new $class($this->plugin, $this, $step);
        }
        return $obj;
    }

    protected function render($content)
    {
        $salon = $this;
        $step = $this->getStepObject($this->getCurrentStep());
            return $this->plugin->loadView('shortcode/salon', compact('content', 'salon'));
        }


    public function getCurrentStep()
    {
        if (!isset($this->currentStep)) {
            $steps = $this->getSteps();
            $stepDefault = array_shift($steps);

            if (!$stepDefault) {
                $stepDefault = self::STEP_DEFAULT;
            }

            // If the first step was skipped for this booking attempt (forecast skip
            // is stored on the BookingBuilder, not in PHP session), advance the
            // default to the next step.  This prevents the reversed-steps dispatch
            // pass from ending without rendering anything.
            //
            // A ?sln_reset_forecast=1 URL parameter clears the flag so testers can
            // force the forecast to re-appear.
            if ( $stepDefault === 'forecast' && ! isset( $_GET[ self::STEP_KEY ] ) ) {
                $bb = $this->plugin->getBookingBuilder();

                if ( ! empty( $_GET['sln_reset_forecast'] ) ) {
                    $bb->set( 'skip_forecast', null );
                    $bb->save();
                    if ( ! empty( $_SESSION['sln_skip_forecast'] ) ) {
                        unset( $_SESSION['sln_skip_forecast'] );
                    }
                }

                // Advance past the forecast step when it was explicitly skipped for
                // this booking attempt OR when it would not render anything (e.g. a
                // logged-in customer without enough history, or no available
                // suggestions). The skip-state path of ForecastStep::isValid() does
                // not persist the skip flag, so relying on it alone leaves 'forecast'
                // as the current step and the reversed dispatch pass renders it blank.
                $skipForecast = ! empty( $bb->get( 'skip_forecast' ) );
                if ( ! $skipForecast ) {
                    $forecastObj = $this->getStepObject( 'forecast' );
                    if ( method_exists( $forecastObj, 'shouldDisplayAsFirstStep' )
                        && ! $forecastObj->shouldDisplayAsFirstStep() ) {
                        $skipForecast = true;
                    }
                }

                if ( $skipForecast ) {
                    $next = array_shift( $steps );
                    if ( $next ) {
                        $stepDefault = $next;
                    }
                }
            }

            $this->currentStep = isset($_GET[self::STEP_KEY]) ? sanitize_text_field(wp_unslash($_GET[self::STEP_KEY])) : $stepDefault;
            unset($steps);
        }

        return $this->currentStep;
    }

    public function getPrevStep()
    {
	// fix sms step after login: use fresh steps each time without overwriting the cached $this->steps
	$steps = $this->initSteps();

        $curr = $this->getCurrentStep();
        $prev = null;
        foreach ($steps as $step) {
            if ($curr == $step) {
                return $prev;
            } else {
                $prev = $step;
            }
        }
    }

    private function needServices()
    {
        /** @var SLN_Repository_ServiceRepository $repo */
        $repo = $this->plugin->getRepository(SLN_Plugin::POST_TYPE_SERVICE);
        $primary_services = array_filter($repo->getAllPrimary(), function($service) {
            return !$service->isHideOnFrontend();
        });

        if(count($primary_services) === 1) {
            $bb = $this->plugin->getBookingBuilder();
            $bb->addService(reset($primary_services));
            $bb->save();
            return false;
        }
        return true;
    }

    private function needSecondary()
    {
        /** @var SLN_Repository_ServiceRepository $repo */
        $repo = $this->plugin->getRepository(SLN_Plugin::POST_TYPE_SERVICE);
        foreach ($repo->getAll() as $service) {
            if ($service->isSecondary() && !$service->isHideOnFrontend()) {
                return true;
            }
        }
    }

    private function needPayment()
    {
        return true;
    }

    private function needAttendant()
    {
        if(!$this->plugin->getSettings()->isAttendantsEnabled()) {
            return false;
        }

        if($this->plugin->getSettings()->isAttendantsEnabledOnlyBackend()) {
            return false;
        }

        $bb = $this->plugin->getBookingBuilder();

        if (!empty($bb->getServices())){
            foreach ($bb->getServices() as $service) {
                if ($service->isAttendantsEnabled()) {
                    return true;
                }
            }
        } else {
            return true;
        }


        return false;
    }

    public function needSms()
    {
        // SMS step is needed if SMS is enabled in backend settings
        // Phone number will be collected in details/fbphone steps if needed
        $sms_enabled = $this->plugin->getSettings()->get('sms_enabled');
        
        if (!$sms_enabled) {
            return false; // SMS not enabled, no SMS step needed
        }
        
        // SMS is enabled - show the verification step
        // Phone collection will be handled by details or fbphone step
        // SMS step itself will show error if phone is missing (handled in SmsStep::render())
        return true;
    }

    public function needFbphone()
    {
        // fbphone step is needed when:
        // - User is logged in but doesn't have phone number
        // - AND (phone is required OR SMS is enabled)
        $user_id = get_current_user_id();
        if (!$user_id) {
            return false; // Not logged in, fbphone not needed
        }
        
        $has_phone = get_user_meta($user_id, '_sln_phone', true) || 
                     (isset($_SESSION['sln_detail_step']) && !empty($_SESSION['sln_detail_step']['phone']));
        
        if ($has_phone) {
            return false; // Already has phone, not needed
        }
        
        // Need phone if required or SMS enabled
        return (
            SLN_Enum_CheckoutFields::getField('phone')->isRequiredNotHidden() 
            || $this->plugin->getSettings()->get('sms_enabled')
        ) ? true : false;
    }

    public function getSteps()
    {
        if (!isset($this->steps)) {
            $this->steps = $this->initSteps();
        }

        return $this->steps;
    }

    protected function initSteps()
    {
        $ret = array(
            'date',
            'services',
            'secondary',
            'attendant',
            'details',
            'fbphone',
            'sms',
            'summary',
            'thankyou',
        );

        if ($this->plugin->getSettings()->isFormStepsAltOrder()) {
            $ret = array(
                'services',
                'secondary',
                'attendant',
                'date',
                'details',
                'fbphone',
                'sms',
                'summary',
                'thankyou',
            );
        }

        if (!$this->needSecondary()) {
            unset($ret[array_search('secondary', $ret)]);
            if(!$this->needServices()) {
                unset($ret[array_search('services', $ret)]);
            }
        }

        if (!$this->needPayment()) {
            unset($ret[array_search('thankyou', $ret)]);
        }
        if (!$this->needAttendant()) {
            unset($ret[array_search('attendant', $ret)]);
        }
        if (!$this->needFbphone()) {
            unset($ret[array_search('fbphone', $ret)]);
        }
        if (!$this->needSms()) {
            unset($ret[array_search('sms', $ret)]);
        }

        return apply_filters('sln.shortcode_salon.initSteps', $ret, $this->attrs);
    }

    protected function maybeReverseSteps($steps) {
        if (!(isset($_GET['submit_'.$this->getCurrentStep()]) && $_GET['submit_'.$this->getCurrentStep()] === 'next' ||
            isset($_POST['submit_'.$this->getCurrentStep()]) && $_POST['submit_'.$this->getCurrentStep()] === 'next')) {
            $steps = array_reverse($steps);
        }

        return $steps;
    }

    public function getStyleShortcode()
    {
        return isset($this->attrs['style']) ?
            $this->attrs['style']
            : $this->plugin->getSettings()->getStyleShortcode();
    }

    protected function getStepsList()
    {
        $ret = array(
            'date',
            'services',
            'secondary',
            'attendant',
            'details',
            'fbphone',
            'sms',
            'summary',
            'thankyou',
        );

        if ($this->plugin->getSettings()->isFormStepsAltOrder()) {
            $ret = array(
                'services',
                'secondary',
                'attendant',
                'date',
                'details',
                'fbphone',
                'sms',
                'summary',
                'thankyou',
            );
        }

        return apply_filters('sln.shortcode_salon.initSteps', $ret, $this->attrs);
    }
}
