<?php

namespace PloverCRM\Core\Api;

use WP_REST_Request;
use WP_REST_Response;
use WP_Error;
use PloverCRM\Common\StandalonePasswordManager;
use PloverCRM\Common\RoleManager;

/**
 * Abstract REST API Endpoint
 */
abstract class AbstractEndpoint
{
    /**
     * @var \PloverCRM\Core\Container
     */
    protected $container;

    /**
     * Constructor
     * 
     * @param \PloverCRM\Core\Container $container
     */
    public function __construct($container)
    {
        $this->container = $container;
    }

    /**
     * Get endpoint route
     * 
     * @return string
     */
    abstract public function getRoute(): string;

    /**
     * Get HTTP method
     * 
     * @return string|array
     */
    abstract public function getMethod();

    /**
     * Handle the request
     * 
     * @param WP_REST_Request $request
     * @return WP_REST_Response|WP_Error
     */
    abstract public function handle(WP_REST_Request $request);

    /**
     * Check permissions
     * 
     * @param WP_REST_Request $request
     * @return bool|\WP_Error
     */
    public function checkPermission(WP_REST_Request $request)
    {
        // Try standalone username/password authentication first.
        $passwordAuth = $this->authenticateWithPassword($request);

        if ($passwordAuth) {
            // Set the current user for this request
            wp_set_current_user((int) $passwordAuth['user_id']);
        }

        return \PloverCRM\Common\Permissions::adminPermissionCheck();
    }

    /**
     * Check a WordPress capability after authenticating the request user.
     *
     * This keeps standalone username/password requests and WordPress-cookie
     * requests on the same permission path for admin-only endpoints.
     *
     * @param WP_REST_Request $request
     * @param string $capability
     * @return bool|\WP_Error
     */
    protected function checkAuthenticatedCapability(WP_REST_Request $request, string $capability)
    {
        $passwordAuth = $this->authenticateWithPassword($request);

        if ($passwordAuth) {
            wp_set_current_user((int) $passwordAuth['user_id']);
        }

        $user = wp_get_current_user();

        if (!$user || !$user->ID) {
            return new \WP_Error(
                'unauthorized',
                'Authentication is required to access this resource',
                ['status' => 401]
            );
        }

        if (user_can($user, $capability)) {
            return true;
        }

        // CRM super admins should retain admin-level access for app-managed
        // resources even when they are not WordPress administrators.
        if (RoleManager::isSuperAdmin((int) $user->ID)) {
            return true;
        }

        return new \WP_Error(
            'administrator_required',
            'Administrator access is required to access this resource.',
            ['status' => 403]
        );
    }

    /**
     * Authenticate using standalone username/password credentials from the request headers.
     * 
     * @param WP_REST_Request $request
     * @return array|null Authentication payload if authenticated, null otherwise
     */
    protected function authenticateWithPassword(WP_REST_Request $request): ?array
    {
        $authHeader = $request->get_header('Authorization');

        if ($authHeader && strpos($authHeader, 'Basic ') === 0) {
            $decoded = base64_decode(substr($authHeader, 6), true);

            if (!is_string($decoded) || strpos($decoded, ':') === false) {
                return null;
            }

            [$username, $password] = explode(':', $decoded, 2);

            return StandalonePasswordManager::validateCredentials($username, $password);
        }

        return null;
    }

    /**
     * Register the endpoint
     * 
     * @param string $namespace
     * @return void
     */
    public function register(string $namespace): void
    {
        register_rest_route($namespace, $this->getRoute(), [
            'methods' => $this->getMethod(),
            'callback' => [$this, 'handle'],
            'permission_callback' => [$this, 'checkPermission'],
        ]);
    }

    /**
     * Success response
     * 
     * @param mixed $data
     * @param array $meta
     * @param int $status HTTP status code
     * @return WP_REST_Response
     */
    protected function success($data, array $meta = [], int $status = 200): WP_REST_Response
    {
        $response = [
            'success' => true,
            'data' => $data,
        ];

        if (!empty($meta)) {
            $response['meta'] = $meta;
        }

        return new WP_REST_Response($response, $status);
    }

    /**
     * Error response
     * 
     * @param string $message
     * @param int $status HTTP status code
     * @param array|string $codeOrData Error code string or array with 'code' and additional data
     * @return WP_Error
     */
    protected function error(string $message, int $status = 400, $codeOrData = ''): WP_Error
    {
        $code = '';
        $data = ['status' => $status];

        if (is_array($codeOrData)) {
            $code = $codeOrData['code'] ?? '';
            unset($codeOrData['code']);
            $data = array_merge($data, $codeOrData);
        } else {
            $code = $codeOrData;
        }

        if (empty($code)) {
            $code = $this->getErrorCodeFromStatus($status);
        }

        return new WP_Error($code, $message, $data);
    }

    /**
     * Get error code from HTTP status
     */
    private function getErrorCodeFromStatus(int $status): string
    {
        $codes = [
            400 => 'bad_request',
            401 => 'unauthorized',
            403 => 'forbidden',
            404 => 'not_found',
            405 => 'method_not_allowed',
            409 => 'conflict',
            422 => 'validation_error',
            500 => 'internal_error',
            503 => 'service_unavailable',
        ];

        return $codes[$status] ?? 'error';
    }
}
