<?php

namespace Limb_Chatbot\Includes\Api\V1\Controllers\Integrations;

use Limb_Chatbot\Includes\AI_Providers\Gemini\Data_Schemas\Setting;
use Limb_Chatbot\Includes\Api\V1\Controllers\Rest_Controller;
use Limb_Chatbot\Includes\Data_Objects\Config;
use Limb_Chatbot\Includes\Exceptions\Error_Codes;
use Limb_Chatbot\Includes\Exceptions\Exception;
use Limb_Chatbot\Includes\Integrations\Slack\Services\Slack_Agent_Service;
use Limb_Chatbot\Includes\Integrations\Slack\Services\Slack_Event_Handler_Service;
use Limb_Chatbot\Includes\Integrations\Slack\Slack;
use Limb_Chatbot\Includes\Integrations\Slack\Utilities\Channels_Utility;
use Limb_Chatbot\Includes\Integrations\Slack\Utilities\Users_Utility;
use Limb_Chatbot\Includes\Services\Collection;
use Limb_Chatbot\Includes\Services\Helper;
use Limb_Chatbot\Includes\Utilities\Slack_Channels_Utility;
use Limb_Chatbot\Includes\Utilities\Slack_Users_Utility;
use WP_Error;
use WP_REST_Request;
use WP_REST_Response;
use WP_REST_Server;

/**
 * Slack Controller
 *
 * REST API controller for Slack integration endpoints.
 * Provides endpoints for:
 * - Fetching channels and users from Slack workspaces
 * - Creating WordPress agent users from Slack users
 * - Receiving Slack Events API webhooks for live agent messages and commands
 *
 * @package Limb_Chatbot\Includes\Api\V1\Controllers\Integrations
 * @since 1.0.0
 */
class Slack_Controller extends Rest_Controller {

	/**
	 * REST route base.
	 *
	 * @var string
	 * @since 1.0.0
	 */
	protected $rest_base = 'integrations/slack';

	/**
	 * Registers REST routes for Slack integration.
	 *
	 * @return void
	 * @since 1.0.0
	 */
	public function register_routes() {
		// GET /integrations/slack/channels?config_id=24
		register_rest_route(
			$this->namespace,
			'/' . $this->rest_base . '/channels',
			array(
				array(
					'methods'             => WP_REST_Server::READABLE,
					'callback'            => array( $this, 'get_channels' ),
					'permission_callback' => array( $this, 'permission_callback' ),
					'args'                => $this->get_channels_args(),
				),
				'schema' => array( $this, 'get_channels_schema' ),
			)
		);

		// GET /integrations/slack/agents?config_id=24
		register_rest_route(
			$this->namespace,
			'/' . $this->rest_base . '/agents',
			array(
				array(
					'methods'             => WP_REST_Server::READABLE,
					'callback'            => array( $this, 'get_agents' ),
					'permission_callback' => array( $this, 'permission_callback' ),
					'args'                => $this->get_config_id_arg(),
				),
			)
		);

		// POST /integrations/slack/agents?config_id=24
		register_rest_route(
			$this->namespace,
			'/'. $this->rest_base .'/agents',
			array(
				array(
					'methods'             => WP_REST_Server::CREATABLE,
					'callback'            => array( $this, 'create_agents' ),
					'permission_callback' => array( $this, 'permission_callback' ),
					'args'                => $this->get_slack_agent_ids_arg(),
				),
			)
		);

		// POST /integrations/slack/events (Public webhook for Slack Events API)
		register_rest_route(
			$this->namespace,
			'/' . $this->rest_base . '/events',
			array(
				array(
					'methods'             => WP_REST_Server::CREATABLE,
					'callback'            => array( $this, 'handle_event' ),
					'permission_callback' => '__return_true', // Public endpoint, validated via Slack signature
				),
			)
		);
	}

	/**
	 * Gets argument definitions for channels endpoint.
	 *
	 * @return array Argument definitions.
	 * @since 1.0.0
	 */
	private function get_channels_args(): array {
		return array_merge(
			$this->get_config_id_arg(),
			array(
				'types'            => array(
					'description'       => __( 'Comma-separated list of conversation types', 'limb-chatbot' ),
					'type'              => 'string',
					'required'          => false,
					'default'           => 'public_channel,private_channel',
					'sanitize_callback' => 'sanitize_text_field',
				),
				'exclude_archived' => array(
					'description' => __( 'Exclude archived channels', 'limb-chatbot' ),
					'type'        => 'boolean',
					'required'    => false,
					'default'     => true,
				),
			)
		);
	}

	/**
	 * Gets argument definition for config_id parameter.
	 *
	 * @return array Argument definition.
	 * @since 1.0.0
	 */
	private function get_config_id_arg(): array {
		return array(
			'config_id' => array(
				'description'       => __( 'Slack configuration ID', 'limb-chatbot' ),
				'type'              => 'integer',
				'required'          => true,
				'sanitize_callback' => 'absint',
				'validate_callback' => function ( $value ) {
					$config = Config::find( $value );
					if ( ! $config instanceof Config || ! $config->get_related_to_instance() instanceof Slack ) {
						return false;
					}

					return true;
				},
			),
		);
	}

	/**
	 * Gets channels from Slack workspace.
	 *
	 * @param  WP_REST_Request  $request  REST request object.
	 *
	 * @return WP_REST_Response|WP_Error Response with channels list.
	 * @since 1.0.0
	 */
	public function get_channels( $request ) {
		try {
			$config = Config::find( $request->get_param( 'config_id' ) );
			// Create global utility
			$global_utility                   = new Slack_Channels_Utility( $config );
			// Create provider utility and fetch channels
			$utility  = new Channels_Utility( $global_utility );
			$channels = $utility->list();

			return rest_ensure_response( new Collection( $channels ) );
		} catch ( \Exception $e ) {
			Helper::log( $e, __METHOD__ );

			return Helper::get_wp_error( $e );
		}
	}

	/**
	 * Gets the JSON schema for channels response.
	 *
	 * @return array JSON schema.
	 * @since 1.0.0
	 */
	public function get_channels_schema(): array {
		return array(
			'$schema'    => 'http://json-schema.org/draft-04/schema#',
			'title'      => 'slack_channels',
			'type'       => 'object',
			'properties' => array(
				'success'  => array(
					'description' => __( 'Whether the request was successful', 'limb-chatbot' ),
					'type'        => 'boolean',
					'context'     => array( 'view' ),
					'readonly'    => true,
				),
				'channels' => array(
					'description' => __( 'Array of Slack channels', 'limb-chatbot' ),
					'type'        => 'array',
					'context'     => array( 'view' ),
					'readonly'    => true,
					'items'       => array(
						'type'       => 'object',
						'properties' => array(
							'id'              => array( 'type' => 'string' ),
							'name'            => array( 'type' => 'string' ),
							'is_channel'      => array( 'type' => 'boolean' ),
							'is_group'        => array( 'type' => 'boolean' ),
							'is_im'           => array( 'type' => 'boolean' ),
							'is_mpim'         => array( 'type' => 'boolean' ),
							'is_private'      => array( 'type' => 'boolean' ),
							'is_archived'     => array( 'type' => 'boolean' ),
							'is_general'      => array( 'type' => 'boolean' ),
							'is_member'       => array( 'type' => 'boolean' ),
							'num_members'     => array( 'type' => 'integer' ),
							'topic'           => array( 'type' => 'string' ),
							'purpose'         => array( 'type' => 'string' ),
							'created'         => array( 'type' => 'integer' ),
							'creator'         => array( 'type' => 'string' ),
							'name_normalized' => array( 'type' => 'string' ),
						),
					),
				),
				'total'    => array(
					'description' => __( 'Total number of channels', 'limb-chatbot' ),
					'type'        => 'integer',
					'context'     => array( 'view' ),
					'readonly'    => true,
				),
			),
		);
	}

	/**
	 * Gets agents (users) from Slack workspace.
	 *
	 * @param  WP_REST_Request  $request  REST request object.
	 *
	 * @return WP_REST_Response|WP_Error Response with agents list.
	 * @since 1.0.0
	 */
	public function get_agents( $request ) {
		try {
			$config = Config::find( $request->get_param( 'config_id' ) );
			// Create global utility
			$global_utility = new Slack_Users_Utility( $config );
			// Create provider utility and fetch users
			$utility = new Users_Utility( $global_utility );
			$agents  = $utility->list();

			return rest_ensure_response( new Collection( $agents ) );
		} catch ( \Exception $e ) {
			Helper::log( $e, __METHOD__ );

			return Helper::get_wp_error( $e );
		}
	}

	/**
	 * Creates agents (WordPress and Chatbot users) from Slack user IDs.
	 *
	 * @param  WP_REST_Request  $request  REST request object.
	 *
	 * @return WP_REST_Response|WP_Error Response with creation results.
	 * @since 1.0.0
	 */
	public function create_agents( $request ) {
		try {
			$config   = Config::find( $request->get_param( 'config_id' ) );
			$user_ids = $request->get_param( 'ids' );
			// Create agents
			$service = new Slack_Agent_Service();
			$results = $service->create_agents_from_slack_users( $user_ids, $config );

			return rest_ensure_response( $results );
		} catch ( \Exception $e ) {
			Helper::log( $e, __METHOD__ );

			return Helper::get_wp_error( $e );
		}
	}

	/**
	 * Checks permission for Slack endpoints.
	 *
	 * Requires user to be logged in and have manage_options capability.
	 *
	 * @param  WP_REST_Request  $request  REST request object.
	 *
	 * @return bool True if authorized, false otherwise.
	 * @since 1.0.0
	 */
	public function permission_callback( $request ): bool {
		if ( ! parent::permission_callback( $request ) ) {
			return false;
		}

		return current_user_can( 'manage_options' );
	}

	/**
	 * Gets argument definition for Slack agent IDs.
	 *
	 * @return array Argument definition.
	 * @since 1.0.0
	 */
	private function get_slack_agent_ids_arg(): array {
		return array_merge(
			$this->get_config_id_arg(),
			array(
				'ids' => array(
					'description'       => __( 'Array of Slack user IDs', 'limb-chatbot' ),
					'type'              => 'array',
					'required'          => true,
					'validate_callback' => function ( $value ) {
						return is_array( $value ) && ! empty( $value );
					},
				),
			)
		);
	}

	/**
	 * Handle Slack event webhook.
	 *
	 * Public endpoint for receiving Slack Events API callbacks.
	 * Processes events including URL verification and message events.
	 *
	 * @param WP_REST_Request $request Request object.
	 *
	 * @return WP_REST_Response Response object.
	 * @since 1.0.0
	 */
	public function handle_event( $request ): WP_REST_Response {
		try {
			$body = $request->get_json_params();

			// Handle URL verification challenge (required by Slack)
			if ( isset( $body['type'] ) && $body['type'] === 'url_verification' ) {
				return rest_ensure_response( [
					'challenge' => $body['challenge'] ?? ''
				] );
			}

			// Verify Slack signature for security
			if ( ! $config = $this->verify_slack_signature( $request ) ) {
				return rest_ensure_response( [
					'success' => false,
					'message' => __( 'Invalid signature', 'limb-chatbot' )
				] );
			}

			// Handle event callback
			if ( isset( $body['type'] ) && $body['type'] === 'event_callback' ) {
				$event   = $body['event'] ?? [];
				$handler = new Slack_Event_Handler_Service();
				$handler->handle_event( $event, $config );

				return rest_ensure_response( [
					'success' => true,
					'message' => __( 'Event processed successfully', 'limb-chatbot' )
				] );
			}

			return rest_ensure_response( [
				'success' => true,
				'message' => __( 'Event received', 'limb-chatbot' )
			] );

		} catch ( \Exception $e ) {
			Helper::log( $e, __METHOD__ );

			return rest_ensure_response( [
				'success' => false,
				'message' => __( 'Internal server error', 'limb-chatbot' )
			] );
		}
	}

	/**
	 * Verify Slack request signature.
	 *
	 * Validates that the request came from Slack by verifying
	 * the X-Slack-Signature header using the signing secret.
	 *
	 * @param WP_REST_Request $request Request object.
	 *
	 * @return Config|null True if signature is valid.
	 * @since 1.0.0
	 */
	private function verify_slack_signature( WP_REST_Request $request ): ?Config {
		$slack_signature = $request->get_header( 'X-Slack-Signature' );
		$timestamp       = $request->get_header( 'X-Slack-Request-Timestamp' );
		$body            = $request->get_body();

		// Check if timestamp is recent (within 5 minutes)
		if ( abs( time() - intval( $timestamp ) ) > 300 ) {
			return null;
		}

		$configs = Config::where(['related_to' => Slack::class]);
		if ($configs->is_empty()){
			return null;
		}
		foreach($configs->get() as $config){
			if (!$config instanceof Config){
				continue;
			}
			if (!empty($config->get_params()['signing_secret'])){
				$signing_secret = $config->get_params()['signing_secret'];
				// Create signature base string
				$sig_base_string = 'v0:' . $timestamp . ':' . $body;
				$my_signature    = 'v0=' . hash_hmac( 'sha256', $sig_base_string, $signing_secret );

				// Compare signatures
				if( hash_equals( $my_signature, $slack_signature )){
					return $config;
				}
			}
		}
		return null;
	}
}

