<?php

namespace VendorTasksDokanClickUp\API;

use VendorTasksDokanClickUp\API\VendorTaskFormatter;
use VendorTasksDokanClickUp\Settings\PluginSettings;
use WP_Error;

if ( ! defined( 'ABSPATH' ) ) {
	exit;
}

/**
 * ClickUp API Client
 *
 * Handles all interactions with the ClickUp API v2
 *
 * @since 1.0.0
 * @package VendorTasksDokanClickUp
 * @subpackage API
 */
class ClickUpClient {

	/**
	 * ClickUp API base URL
	 *
	 * @var string
	 */
	private const API_BASE_URL = 'https://api.clickup.com/api/v2';

	/**
	 * Cache group for wp_cache
	 *
	 * @var string
	 */
	private const CACHE_GROUP = 'vendor_tasks_dokan_clickup';

	/**
	 * Cache expiration time (1 hour)
	 *
	 * @var int
	 */
	private const CACHE_EXPIRATION = HOUR_IN_SECONDS;

	/**
	 * API Token
	 *
	 * @var string
	 */
	private string $api_token;

	/**
	 * List ID
	 *
	 * @var string
	 */
	private string $list_id;

	/**
	 * Class constructor
	 *
	 * @since 1.0.0
	 */
	public function __construct() {
		$ps              = new PluginSettings();
		$this->api_token = $ps->get_api_token();
		$this->list_id   = $ps->get_list_id();
	}

	/**
	 * Create a task in ClickUp
	 *
	 * @since 1.0.0
	 *
	 * @see https://developer.clickup.com/reference/addtasktolist
	 * @see https://developer.clickup.com/reference/createtask
	 *
	 * @param array  $task_data Task data to create.
	 * @param string $list_id   Optional list ID override (used by list routing, F-13). Falls back to configured list_id.
	 * @return array|WP_Error Response array or WP_Error on failure.
	 */
	public function create_task( array $task_data, string $list_id = '' ) {
		$target_list = $list_id ?: $this->list_id;

		if ( empty( $this->api_token ) || empty( $target_list ) ) {
			return new WP_Error(
				'missing_credentials',
				__( 'ClickUp API token or List ID is not configured.', 'alamin-vendor-tasks-dokan-clickup' )
			);
		}

		$endpoint = sprintf( '%s/list/%s/task', self::API_BASE_URL, $target_list );

		$body = array(
			'name'     => $task_data['name'] ?? '',
			'priority' => $task_data['priority'] ?? 3,
		);

		if ( isset( $task_data['markdown_content'] ) ) {
			$body['markdown_content'] = $task_data['markdown_content'];
		} elseif ( isset( $task_data['description'] ) ) {
			$body['description'] = $task_data['description'];
		}

		// Only send status if explicitly configured; omitting lets ClickUp use the list default
		if ( ! empty( $task_data['status'] ) ) {
			$body['status'] = $task_data['status'];
		}

		// Add custom fields if provided
		if ( ! empty( $task_data['custom_fields'] ) ) {
			$body['custom_fields'] = $task_data['custom_fields'];
		}

		// Add tags if provided
		if ( ! empty( $task_data['tags'] ) ) {
			$body['tags'] = $task_data['tags'];
		}

		// Add assignees if provided
		if ( ! empty( $task_data['assignees'] ) ) {
			$body['assignees'] = $task_data['assignees'];
		}

		$response = $this->make_request( $endpoint, 'POST', $body );

		if ( is_wp_error( $response ) ) {
			vendor_tasks_dokan_clickup()->log(
				sprintf( 'Failed to create ClickUp task: %s', $response->get_error_message() ),
				'error'
			);
			return $response;
		}

		vendor_tasks_dokan_clickup()->log(
			sprintf( 'ClickUp task created successfully: %s', $response['id'] ?? 'unknown' ),
			'info'
		);

		return $response;
	}

	/**
	 * Make HTTP request to ClickUp API
	 *
	 * @since 1.0.0
	 *
	 * @param string $endpoint API endpoint URL.
	 * @param string $method HTTP method (GET, POST, PUT, DELETE).
	 * @param array  $body Request body data.
	 * @return array|WP_Error Response data or WP_Error on failure.
	 */
	private function make_request( string $endpoint, string $method = 'GET', array $body = array() ) {
		$args = array(
			'method'  => $method,
			'headers' => array(
				'Authorization' => $this->api_token,
				'Accept'        => 'application/json',
				'Content-Type'  => 'application/json',
			),
			'timeout' => 30,
		);

		if ( ! empty( $body ) && in_array( $method, array( 'POST', 'PUT' ), true ) ) {
			$args['body'] = wp_json_encode( $body );
		}

		$args     = apply_filters( 'vendor_tasks_dokan_clickup_client_args', $args, $endpoint, $method );
		$response = wp_remote_request( $endpoint, $args );

		if ( is_wp_error( $response ) ) {
			return $response;
		}

		$status_code = wp_remote_retrieve_response_code( $response );
		$body_data   = json_decode( wp_remote_retrieve_body( $response ), true );

		// Handle non-200 responses
		if ( $status_code < 200 || $status_code >= 300 ) {
			$error_message = $body_data['err'] ?? $body_data['error'] ?? 'Unknown API error';
			return new WP_Error(
				'clickup_api_error',
				sprintf(
				/* translators: 1: HTTP status code, 2: Error message */
					__( 'ClickUp API error (HTTP %1$d): %2$s', 'alamin-vendor-tasks-dokan-clickup' ),
					$status_code,
					$error_message
				),
				array( 'status' => $status_code )
			);
		}

		return $body_data;
	}

	/**
	 * Test API connection
	 *
	 * @since 1.0.0
	 *
	 * @return bool|WP_Error True if connection successful, WP_Error on failure.
	 */
	public function test_connection() {
		if ( empty( $this->api_token ) ) {
			return new WP_Error(
				'missing_token',
				__( 'API token is required.', 'alamin-vendor-tasks-dokan-clickup' )
			);
		}

		$endpoint = sprintf( '%s/user', self::API_BASE_URL );
		$response = $this->make_request( $endpoint, 'GET' );

		if ( is_wp_error( $response ) ) {
			return $response;
		}

		return true;
	}

	/**
	 * Get list information
	 *
	 * @since 1.0.0
	 *
	 * @return array|WP_Error List data or WP_Error on failure.
	 */
	public function get_list() {
		if ( empty( $this->api_token ) || empty( $this->list_id ) ) {
			return new WP_Error(
				'missing_credentials',
				__( 'API token and List ID are required.', 'alamin-vendor-tasks-dokan-clickup' )
			);
		}

		$endpoint = sprintf( '%s/list/%s', self::API_BASE_URL, $this->list_id );
		return $this->make_request(
			$endpoint,
			'GET',
			array(
				'include_markdown_description' => true,
				'custom_task_ids'              => true,
			)
		);
	}

	/**
	 * Get authorized workspaces (teams)
	 *
	 * @since 1.0.0
	 *
	 * @return array|WP_Error Workspaces data or WP_Error on failure.
	 */
	public function get_workspaces() {
		if ( empty( $this->api_token ) ) {
			return new WP_Error(
				'missing_token',
				__( 'API token is required.', 'alamin-vendor-tasks-dokan-clickup' )
			);
		}

		// Generate cache key based on API token hash
		$cache_key = 'workspaces_' . md5( $this->api_token );

		// Try to get from cache
		$cached = $this->get_cache( $cache_key );
		if ( false !== $cached ) {
			return $cached;
		}

		$endpoint = sprintf( '%s/team', self::API_BASE_URL );
		$response = $this->make_request( $endpoint, 'GET' );

		// Cache successful responses only
		if ( ! is_wp_error( $response ) ) {
			$this->set_cache( $cache_key, $response );
		}

		return $response;
	}

	/**
	 * Get spaces for a workspace
	 *
	 * @since 1.0.0
	 *
	 * @param string $workspace_id Workspace/Team ID.
	 * @return array|WP_Error Spaces data or WP_Error on failure.
	 */
	public function get_spaces( string $workspace_id ) {
		if ( empty( $this->api_token ) || empty( $workspace_id ) ) {
			return new WP_Error(
				'missing_credentials',
				__( 'API token and Workspace ID are required.', 'alamin-vendor-tasks-dokan-clickup' )
			);
		}

		return $this->make_request( sprintf( '%s/team/%s/space?archived=false', self::API_BASE_URL, $workspace_id ) );
	}

	/**
	 * Get all lists for a workspace (including folderless and spaces)
	 *
	 * @since 1.0.0
	 *
	 * @param string $workspace_id Workspace/Team ID.
	 * @return array|WP_Error Lists data or WP_Error on failure.
	 */
	public function get_lists_for_workspace( string $workspace_id ) {
		if ( empty( $this->api_token ) || empty( $workspace_id ) ) {
			return new WP_Error(
				'missing_credentials',
				__( 'API token and Workspace ID are required.', 'alamin-vendor-tasks-dokan-clickup' )
			);
		}

		// Generate cache key based on API token hash and workspace ID
		$cache_key = 'lists_' . md5( $this->api_token . $workspace_id );

		// Try to get from cache
		$cached = $this->get_cache( $cache_key );
		if ( false !== $cached ) {
			return $cached;
		}

		$all_lists = array();

		// Get spaces
		$spaces = $this->get_spaces( $workspace_id );
		if ( is_wp_error( $spaces ) ) {
			return $spaces;
		}

		// Iterate through each space to get lists and folders
		foreach ( $spaces['spaces'] ?? array() as $space ) {
			// Get folderless lists in space
			if ( ! empty( $space['id'] ) ) {
				$space_endpoint = sprintf( '%s/space/%s/list?archived=false', self::API_BASE_URL, $space['id'] );
				$space_lists    = $this->make_request( $space_endpoint, 'GET' );

				if ( ! is_wp_error( $space_lists ) && ! empty( $space_lists['lists'] ) ) {
					foreach ( $space_lists['lists'] as $list ) {
						$all_lists[] = array(
							'id'   => $list['id'],
							'name' => sprintf( '%s / %s', $space['name'], $list['name'] ),
						);
					}
				}

				// Get folders in space
				$folders = $this->make_request( sprintf( '%s/space/%s/folder?archived=false', self::API_BASE_URL, $space['id'] ), );

				if ( ! is_wp_error( $folders ) && ! empty( $folders['folders'] ) ) {
					foreach ( $folders['folders'] as $folder ) {
						// Get lists in folder
						$folder_endpoint = sprintf( '%s/folder/%s/list?archived=false', self::API_BASE_URL, $folder['id'] );
						$folder_lists    = $this->make_request( $folder_endpoint, 'GET' );

						if ( ! is_wp_error( $folder_lists ) && ! empty( $folder_lists['lists'] ) ) {
							foreach ( $folder_lists['lists'] as $list ) {
								$all_lists[] = array(
									'id'   => $list['id'],
									'name' => sprintf( '%s / %s / %s', $space['name'], $folder['name'], $list['name'] ),
								);
							}
						}
					}
				}
			}
		}

		// Cache the result
		$this->set_cache( $cache_key, $all_lists );

		return $all_lists;
	}

	/**
	 * Get a single ClickUp task by ID
	 *
	 * @since 1.1.0
	 *
	 * @see https://developer.clickup.com/reference/gettask
	 *
	 * @param string $task_id ClickUp task ID.
	 * @return array|WP_Error Task data or WP_Error (404 if deleted).
	 */
	public function get_task( string $task_id ) {
		if ( empty( $this->api_token ) || empty( $task_id ) ) {
			return new WP_Error(
				'missing_credentials',
				__( 'ClickUp API token or task ID is missing.', 'alamin-vendor-tasks-dokan-clickup' )
			);
		}

		$endpoint = sprintf( '%s/task/%s', self::API_BASE_URL, $task_id );
		return $this->make_request( $endpoint, 'GET' );
	}

	/**
	 * Update an existing ClickUp task
	 *
	 * @since 1.1.0
	 *
	 * @see https://developer.clickup.com/reference/updatetask
	 *
	 * @param string $task_id   ClickUp task ID.
	 * @param array  $task_data Fields to update (name, description, markdown_content, status, priority, etc.).
	 * @return array|WP_Error Updated task data or WP_Error on failure.
	 */
	public function update_task( string $task_id, array $task_data ) {
		if ( empty( $this->api_token ) || empty( $task_id ) ) {
			return new WP_Error(
				'missing_credentials',
				__( 'ClickUp API token or task ID is missing.', 'alamin-vendor-tasks-dokan-clickup' )
			);
		}

		$endpoint = sprintf( '%s/task/%s', self::API_BASE_URL, $task_id );
		return $this->make_request( $endpoint, 'PUT', $task_data );
	}

	/**
	 * Add a comment to a ClickUp task
	 *
	 * @since 1.1.0
	 *
	 * @see https://developer.clickup.com/reference/createtaskcomment
	 *
	 * @param string $task_id        ClickUp task ID.
	 * @param string $comment_text   Plain text comment body.
	 * @return array|WP_Error Response array or WP_Error on failure.
	 */
	public function add_task_comment( string $task_id, string $comment_text ) {
		if ( empty( $this->api_token ) || empty( $task_id ) ) {
			return new WP_Error(
				'missing_credentials',
				__( 'ClickUp API token or task ID is missing.', 'alamin-vendor-tasks-dokan-clickup' )
			);
		}

		$endpoint = sprintf( '%s/task/%s/comment', self::API_BASE_URL, $task_id );
		return $this->make_request( $endpoint, 'POST', array( 'comment_text' => $comment_text ) );
	}

	/**
	 * Delete a ClickUp task
	 *
	 * @since 1.1.0
	 *
	 * @see https://developer.clickup.com/reference/deletetask
	 *
	 * @param string $task_id ClickUp task ID.
	 * @return array|WP_Error Empty array on success or WP_Error on failure.
	 */
	public function delete_task( string $task_id ) {
		if ( empty( $this->api_token ) || empty( $task_id ) ) {
			return new WP_Error(
				'missing_credentials',
				__( 'ClickUp API token or task ID is missing.', 'alamin-vendor-tasks-dokan-clickup' )
			);
		}

		$endpoint = sprintf( '%s/task/%s', self::API_BASE_URL, $task_id );
		return $this->make_request( $endpoint, 'DELETE' );
	}

	/**
	 * Get members for a workspace
	 *
	 * Parses the member list from the /team response (already cached).
	 *
	 * @since 1.1.0
	 *
	 * @param string $workspace_id Workspace/Team ID.
	 * @return array|WP_Error Flat array of [ id, username, email ] or WP_Error.
	 */
	public function get_workspace_members( string $workspace_id ) {
		$workspaces = $this->get_workspaces();
		if ( is_wp_error( $workspaces ) ) {
			return $workspaces;
		}

		foreach ( $workspaces['teams'] ?? array() as $workspace ) {
			if ( $workspace['id'] !== $workspace_id ) {
				continue;
			}

			$members = array();
			foreach ( $workspace['members'] ?? array() as $member ) {
				$user = $member['user'] ?? array();
				if ( empty( $user['id'] ) ) {
					continue;
				}
				$members[] = array(
					'id'       => $user['id'],
					'username' => $user['username'] ?? ( $user['email'] ?? 'Unknown' ),
					'email'    => $user['email'] ?? '',
				);
			}
			return $members;
		}

		return new WP_Error( 'workspace_not_found', __( 'Workspace not found.', 'alamin-vendor-tasks-dokan-clickup' ) );
	}

	/**
	 * Format vendor data for ClickUp task
	 *
	 * Delegates to VendorTaskFormatter::format(). Kept for backwards compatibility.
	 *
	 * @since 1.0.0
	 *
	 * @param int    $vendor_id Vendor user ID.
	 * @param string $event_key Event key matching a key in event_templates setting.
	 * @return array<string, mixed> Formatted task data.
	 */
	public function format_vendor_task_data( int $vendor_id, string $event_key = 'vendor_registered' ): array {
		return ( new VendorTaskFormatter() )->format( $vendor_id, $event_key );
	}

	/**
	 * Get cached data using wp_cache (object cache only)
	 *
	 * @since 1.0.0
	 *
	 * @param string $key Cache key.
	 * @return mixed Cached data or false if not found.
	 */
	private function get_cache( string $key ) {
		return wp_cache_get( $key, self::CACHE_GROUP );
	}

	/**
	 * Set cached data using wp_cache (object cache only)
	 *
	 * @since 1.0.0
	 *
	 * @param string $key Cache key.
	 * @param mixed  $data Data to cache.
	 * @return bool True on success, false on failure.
	 */
	private function set_cache( string $key, $data ): bool {
		return wp_cache_set( $key, $data, self::CACHE_GROUP, self::CACHE_EXPIRATION );
	}

	/**
	 * Clear all ClickUp caches
	 *
	 * Flushes the entire cache group. This works with persistent object cache
	 * implementations (Redis, Memcached, etc.). For default WordPress object cache,
	 * cache is already non-persistent and will clear on each request.
	 *
	 * @since 1.0.0
	 *
	 * @return void
	 */
	public function clear_cache(): void {
		wp_cache_flush_group( self::CACHE_GROUP );
	}
}
