<?php

namespace PloverCRM\Modules\Integrations\Endpoints;

use PloverCRM\Core\Api\AbstractEndpoint;
use PloverCRM\Modules\Integrations\Services\DataSourceApiSerializer;
use PloverCRM\Modules\Integrations\Services\DataSourceService;
use PloverCRM\Modules\Integrations\Services\WebhookService;
use PloverCRM\Modules\Integrations\Services\WebhooksLogger;
use WP_REST_Request;

class DataSourceWebhooksEndpoint extends AbstractEndpoint
{
  public function getRoute(): string
  {
    return '/data-sources/webhooks(?:/(?P<id>\d+))?';
  }

  public function getMethod()
  {
    return ['GET', 'POST', 'PUT', 'DELETE'];
  }

  public function handle(WP_REST_Request $request)
  {
    $method = $request->get_method();
    $webhookId = absint($request->get_param('id'));

    switch ($method) {
      case 'GET':
        return $webhookId > 0
          ? $this->getWebhook($webhookId)
          : $this->listWebhooks();

      case 'POST':
        return $this->createWebhook($request);

      case 'PUT':
        return $this->updateWebhook($request, $webhookId);

      case 'DELETE':
        return $this->deleteWebhook($webhookId);
    }

    return $this->error('The requested method is not supported for this endpoint', 405);
  }

  private function listWebhooks()
  {
    $webhooks = array_map(function ($webhook) {
      $webhook['url'] = WebhookService::getWebhookUrl((string) ($webhook['token'] ?? ''));
      $webhook['has_payload'] = !empty($webhook['last_payload']);
      return DataSourceApiSerializer::webhook($webhook);
    }, WebhookService::getAll());

    return $this->success([
      'webhooks' => $webhooks,
    ]);
  }

  private function getWebhook(int $webhookId)
  {
    $webhook = WebhookService::getById($webhookId);
    if (!$webhook) {
      return $this->error('Webhook not found.', 404, 'data_source_webhook_not_found');
    }

    $webhook['url'] = WebhookService::getWebhookUrl((string) ($webhook['token'] ?? ''));
    return $this->success(DataSourceApiSerializer::webhook($webhook));
  }

  private function createWebhook(WP_REST_Request $request)
  {
    $payload = $request->get_json_params();
    if (!is_array($payload)) {
      $payload = $request->get_params();
    }

    $name = sanitize_text_field((string) ($payload['name'] ?? ''));
    if ($name === '') {
      return $this->error('Webhook name is required.', 422, 'missing_webhook_name');
    }

    $webhook = WebhookService::create($name);
    if (!$webhook) {
      return $this->error('Failed to create the webhook.', 500, 'data_source_webhook_create_failed');
    }

    (new WebhooksLogger())->info('Webhook created', [
      'webhook_id' => (int) ($webhook['id'] ?? 0),
      'webhook_name' => $name,
      'status' => (string) ($webhook['status'] ?? 'active'),
    ]);

    $webhook['url'] = WebhookService::getWebhookUrl((string) ($webhook['token'] ?? ''));
    return $this->success(
      DataSourceApiSerializer::webhook($webhook),
      [],
      201
    );
  }

  private function updateWebhook(WP_REST_Request $request, int $webhookId)
  {
    if ($webhookId <= 0) {
      return $this->error('Webhook not found.', 404, 'data_source_webhook_not_found');
    }

    $existing = WebhookService::getById($webhookId);
    if (!$existing) {
      return $this->error('Webhook not found.', 404, 'data_source_webhook_not_found');
    }

    $payload = $request->get_json_params();
    if (!is_array($payload)) {
      $payload = $request->get_params();
    }

    $updatePayload = [];
    if (isset($payload['name'])) {
      $updatePayload['name'] = sanitize_text_field((string) $payload['name']);
    }
    if (isset($payload['status'])) {
      $updatePayload['status'] = sanitize_text_field((string) $payload['status']);
    }

    if (empty($updatePayload)) {
      return $this->error('Nothing to update.', 422, 'empty_webhook_update');
    }

    $hasChanges = false;
    foreach ($updatePayload as $key => $value) {
      if ((string) ($existing[$key] ?? '') !== (string) $value) {
        $hasChanges = true;
        break;
      }
    }

    if (!$hasChanges) {
      $existing['url'] = WebhookService::getWebhookUrl((string) ($existing['token'] ?? ''));
      return $this->success(DataSourceApiSerializer::webhook($existing));
    }

    $updated = WebhookService::update($webhookId, $updatePayload);
    if (!$updated) {
      return $this->error('Failed to update the webhook.', 500, 'data_source_webhook_update_failed');
    }

    $webhook = WebhookService::getById($webhookId);
    (new WebhooksLogger())->info('Webhook updated', [
      'webhook_id' => $webhookId,
      'webhook_name' => (string) ($webhook['name'] ?? ''),
      'status' => (string) ($webhook['status'] ?? ''),
    ]);

    (new DataSourceService($this->container))->syncWebhookIntegrationName(
      (string) $webhookId,
      (string) ($webhook['name'] ?? '')
    );

    $webhook['url'] = WebhookService::getWebhookUrl((string) ($webhook['token'] ?? ''));
    return $this->success(DataSourceApiSerializer::webhook($webhook));
  }

  private function deleteWebhook(int $webhookId)
  {
    if ($webhookId <= 0) {
      return $this->error('Webhook not found.', 404, 'data_source_webhook_not_found');
    }

    $existing = WebhookService::getById($webhookId);
    if (!$existing) {
      return $this->error('Webhook not found.', 404, 'data_source_webhook_not_found');
    }

    $deleted = WebhookService::deleteWithMappings($webhookId);
    if (!$deleted) {
      return $this->error('Failed to delete the webhook.', 500, 'data_source_webhook_delete_failed');
    }

    (new WebhooksLogger())->warning('Webhook deleted', [
      'webhook_id' => $webhookId,
      'webhook_name' => (string) ($existing['name'] ?? ''),
      'status' => 'deleted',
    ]);

    return $this->success([
      'deleted' => true,
      'webhookId' => $webhookId,
    ]);
  }
}
