<?php

namespace HelpGent\App\BackgroundProcessors;

defined( 'ABSPATH' ) || exit;

use HelpGent\App\EnumeratedList\BackgroundProcessStatus;
use Exception;

class BackgroundProcess extends CoreBackgroundProcess {
    protected $action = 'background_process';

    protected $run_task_async = true;

    protected $after_complete_callback = null;

    protected $after_inactive_callback = null;

    public function __construct( string $action_prefix, bool $run_task_async = true ) {
        $this->action         = "{$action_prefix}_{$this->action}";
        $this->run_task_async = $run_task_async;
        parent::__construct();
    }

    public function set_after_complete_callback( $callback ) {
        $this->after_complete_callback = $callback;
    }

    public function set_after_inactive_callback( $callback ) {
        $this->after_inactive_callback = $callback;
    }

    public function task( $item ) {
        if ( $this->run_task_async ) {
            return $this->run_task( $item );
        }

        return $this->run_tasks( $item );
    }

    public function run_task( $item ) {
        $is_valid_task = $this->is_valid_task( $item );

        if ( ! $is_valid_task ) {
            return false;
        }

        try {
            $args     = isset( $item[ 'args' ] ) && is_array( $item[ 'args' ] ) ? $item[ 'args' ] : [];
            $callback = call_user_func_array( $item[ 'callback' ], [ $args ] );

            if ( false === $callback ) {
                return false;
            }

            if ( is_array( $callback ) ) {
                $item[ 'args' ] = array_merge( $args, $callback );
            }

            return $item;
        } catch ( Exception $e ) {
            return false;
        }
    }

    public function run_tasks( $items ) {
        $key = array_key_first( $items );

        if ( is_null( $key ) ) {
            return false;
        }

        $item = $this->run_task( $items[ $key ] );

        if ( $item !== false ) {
            $items[ $key ] = $item;
            return $items;
        }

        array_shift( $items );

        if ( empty( $items ) ) {
            return false;
        }

        return $items;
    }

    public function is_valid_task( $item ): bool {
        if ( ! is_array( $item ) ) {
            return false;
        }

        if ( ! isset( $item[ 'callback' ] ) ) {
            return false;
        }

        if ( ! is_callable( $item[ 'callback' ] ) ) {
            return false;
        }

        return true;
    }

    public function is_finished(): bool {
        return absint( $this->get_option( "{$this->identifier}_is_completed" ) ) === 1;
    }

    public function set_is_finished( bool $status ): void {
        $this->update_option( "{$this->identifier}_is_completed", $status ? 1 : 0 );
    }

    public function is_stopped(): bool {
        return absint( $this->get_option( "{$this->identifier}_is_stopped" ) ) === 1;
    }

    public function before_init(): void {
        $this->set_is_finished( false );
        $this->set_is_stopped( false );
    }

    public function set_is_stopped( bool $status ): void {
        $this->update_option( "{$this->identifier}_is_stopped", $status ? 1 : 0 );
    }

    public function reset() {
        $this->set_is_stopped( false );
        $this->set_is_finished( false );
    }

    public function status(): string {
        if ( $this->is_active() ) {
            return BackgroundProcessStatus::RUNNING;
        }
        
        if ( $this->is_paused() ) {
            return BackgroundProcessStatus::PAUSED;
        }

        if ( $this->is_cancelled() || $this->is_stopped() ) {
            return BackgroundProcessStatus::CANCELLED;
        }

        if ( $this->is_finished() ) {
            return BackgroundProcessStatus::DONE;
        }

        return BackgroundProcessStatus::IDLE;
    }

    public function complete(): void {
        parent::complete();

        $this->set_is_finished( true );

        if ( $this->after_complete_callback ) {
            try {
                call_user_func( $this->after_complete_callback );
            } catch ( Exception $e ) {
            }
        }
    }

    public function paused() {
        parent::paused();

        if ( $this->after_inactive_callback ) {
            try {
                call_user_func( $this->after_inactive_callback );
            } catch ( Exception $e ) {
            }
        }
    }

    public function cancelled(): void {
        parent::cancelled();

        $this->set_is_stopped( true );

        if ( $this->after_inactive_callback ) {
            try {
                call_user_func( $this->after_inactive_callback );
            } catch ( Exception $e ) {
            }
        }
    }

    public function get_option( $option_name, $default = false ) {
        return helpgent_option()->get_by_name( $option_name, $default );
    }

    public function update_option( $option_name, $option_value ) {
        return helpgent_option()->update( $option_name, $option_value );
    }
}