<?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\AdsLogger;
use PloverCRM\Modules\Integrations\Services\FormsLogger;
use PloverCRM\Modules\Integrations\Services\WebhookService;
use PloverCRM\Modules\Integrations\Services\WebhooksLogger;
use WP_REST_Request;

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

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

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

    switch ($method) {
      case 'GET':
        return $integrationId > 0
          ? $this->getIntegration($integrationId)
          : $this->listIntegrations($request);

      case 'POST':
        return $this->saveIntegration($request, 0);

      case 'PUT':
        return $this->saveIntegration($request, $integrationId);

      case 'DELETE':
        return $this->deleteIntegration($integrationId);
    }

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

  private function listIntegrations(WP_REST_Request $request)
  {
    $service = new DataSourceService($this->container);
    $result = $service->getAllIntegrations([
      'page' => max(1, (int) $request->get_param('page')),
      'per_page' => max(1, (int) ($request->get_param('per_page') ?: 100)),
      'status' => sanitize_text_field((string) $request->get_param('status')),
      'provider' => sanitize_text_field((string) $request->get_param('provider')),
      'category' => sanitize_text_field((string) $request->get_param('category')),
    ]);

    return $this->success(
      [
        'integrations' => array_map(function ($integration) use ($service) {
          return DataSourceApiSerializer::integration($integration, $service);
        }, $result['integrations']),
      ],
      [
        'page' => (int) $result['page'],
        'per_page' => (int) $result['per_page'],
        'total' => (int) $result['total'],
        'total_pages' => (int) $result['total_pages'],
      ]
    );
  }

  private function getIntegration(int $integrationId)
  {
    $service = new DataSourceService($this->container);
    $integration = $service->getIntegration($integrationId);

    if (!$integration) {
      return $this->error('Data source integration not found.', 404, 'data_source_not_found');
    }

    return $this->success(DataSourceApiSerializer::integration($integration, $service));
  }

  private function saveIntegration(WP_REST_Request $request, int $integrationId)
  {
    $service = new DataSourceService($this->container);
    $payload = $request->get_json_params();
    if (!is_array($payload)) {
      $payload = $request->get_params();
    }

    $existing = $integrationId > 0 ? $service->getIntegration($integrationId) : null;
    if ($integrationId > 0 && !$existing) {
      return $this->error('Data source integration not found.', 404, 'data_source_not_found');
    }

    $normalized = $this->normalizePayload($payload, $existing ? get_object_vars($existing) : []);

    if ($normalized['name'] === '') {
      return $this->error('Integration name is required.', 422, 'missing_name');
    }

    if ($normalized['provider'] === '') {
      return $this->error('A provider is required.', 422, 'missing_provider');
    }

    if ($normalized['source_id'] === '') {
      return $this->error('A source is required.', 422, 'missing_source');
    }

    $provider = $service->getProvider($normalized['provider']);
    if (!$provider) {
      return $this->error('The selected provider is not available.', 422, 'invalid_provider');
    }

    $category = $provider->getCategory();

    if ($normalized['provider'] === 'webhook') {
      $webhook = WebhookService::getById((int) $normalized['source_id']);
      if (!$webhook) {
        return $this->error('The selected webhook endpoint no longer exists.', 422, 'invalid_webhook_source');
      }

      $normalized['name'] = sanitize_text_field((string) ($webhook['name'] ?? $normalized['name']));
      $normalized['status'] = 'active';
      $normalized['event_type'] = 'data_received';

      $conflicts = $service->getAllIntegrationsForSource(
        'webhook',
        $normalized['source_id'],
        $integrationId > 0 ? $integrationId : null
      );

      if (!empty($conflicts)) {
        return $this->error(
          'This webhook endpoint already has a mapping. Open the endpoint card and edit the existing mapping instead of creating another one.',
          409,
          'webhook_mapping_already_exists'
        );
      }
    }

    $validation = $this->validateRequiredFieldMapping($normalized['field_mappings']);
    if ($validation !== true) {
      return $validation;
    }

    if ($integrationId > 0) {
      $updated = $service->updateIntegration($integrationId, $normalized);

      if (!$updated) {
        return $this->error('Failed to update the data source integration.', 500, 'data_source_update_failed');
      }

      $saved = $service->getIntegration($integrationId);
      $this->getLoggerForCategory($category)->info('Integration updated', [
        'integration_id' => $integrationId,
        'integration_name' => $normalized['name'],
        'provider' => $normalized['provider'],
        'category' => $category,
        'source_id' => $normalized['source_id'],
        'status' => $normalized['status'],
      ]);

      return $this->success(
        DataSourceApiSerializer::integration($saved, $service)
      );
    }

    $savedId = $service->createIntegration($normalized);
    if (!$savedId) {
      return $this->error('Failed to create the data source integration.', 500, 'data_source_create_failed');
    }

    $saved = $service->getIntegration((int) $savedId);
    $this->getLoggerForCategory($category)->info('Integration created', [
      'integration_id' => (int) $savedId,
      'integration_name' => $normalized['name'],
      'provider' => $normalized['provider'],
      'category' => $category,
      'source_id' => $normalized['source_id'],
      'status' => $normalized['status'],
    ]);

    return $this->success(
      DataSourceApiSerializer::integration($saved, $service),
      [],
      201
    );
  }

  private function deleteIntegration(int $integrationId)
  {
    if ($integrationId <= 0) {
      return $this->error('Data source integration not found.', 404, 'data_source_not_found');
    }

    $service = new DataSourceService($this->container);
    $existing = $service->getIntegration($integrationId);
    if (!$existing) {
      return $this->error('Data source integration not found.', 404, 'data_source_not_found');
    }

    $deleted = $service->deleteIntegration($integrationId);
    if (!$deleted) {
      return $this->error('Failed to delete the data source integration.', 500, 'data_source_delete_failed');
    }

    $category = sanitize_key((string) ($existing->category ?? 'forms'));
    $this->getLoggerForCategory($category)->warning('Integration deleted', [
      'integration_id' => $integrationId,
      'integration_name' => (string) ($existing->name ?? ''),
      'provider' => (string) ($existing->provider ?? ''),
      'category' => $category,
      'source_id' => (string) ($existing->source_id ?? ''),
      'status' => 'deleted',
    ]);

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

  private function getLoggerForCategory(string $category)
  {
    switch ($category) {
      case 'ads':
        return new AdsLogger();

      case 'webhooks':
        return new WebhooksLogger();

      case 'forms':
      default:
        return new FormsLogger();
    }
  }

  private function normalizePayload(array $payload, array $existing = []): array
  {
    return [
      'name' => sanitize_text_field((string) ($payload['name'] ?? $existing['name'] ?? '')),
      'provider' => sanitize_text_field((string) ($payload['provider'] ?? $payload['providerKey'] ?? $existing['provider'] ?? '')),
      'source_id' => sanitize_text_field((string) ($payload['source_id'] ?? $payload['sourceId'] ?? $existing['source_id'] ?? '')),
      'event_type' => sanitize_text_field((string) ($payload['event_type'] ?? $payload['eventType'] ?? $existing['event_type'] ?? 'default')),
      'field_mappings' => $this->normalizeArrayValue($payload['field_mappings'] ?? $payload['fieldMappings'] ?? $existing['field_mappings'] ?? []),
      'lists' => array_map('intval', $this->normalizeArrayValue($payload['lists'] ?? $existing['lists'] ?? [])),
      'tags' => array_map('intval', $this->normalizeArrayValue($payload['tags'] ?? $existing['tags'] ?? [])),
      'options' => $this->normalizeArrayValue($payload['options'] ?? $existing['options'] ?? []),
      'conditions' => $this->normalizeArrayValue($payload['conditions'] ?? $existing['conditions'] ?? []),
      'status' => sanitize_text_field((string) ($payload['status'] ?? $existing['status'] ?? 'active')),
    ];
  }

  private function normalizeArrayValue($value): array
  {
    if (is_array($value)) {
      return $value;
    }

    if (is_string($value) && $value !== '') {
      $decoded = json_decode($value, true);
      return is_array($decoded) ? $decoded : [];
    }

    return [];
  }

  private function validateRequiredFieldMapping(array $fieldMappings)
  {
    $crm = $this->container->get('crm');
    $uniqueFieldInfo = $crm->getRequiredUniqueField();
    $requiredField = !empty($uniqueFieldInfo['is_custom_field'])
      ? 'cf_' . (string) $uniqueFieldInfo['field']
      : (string) $uniqueFieldInfo['field'];

    foreach ($fieldMappings as $mapping) {
      if (
        (string) ($mapping['crm_field'] ?? $mapping['crmField'] ?? '') === $requiredField
        && !empty($mapping['source_field'] ?? $mapping['sourceField'] ?? '')
      ) {
        return true;
      }
    }

    return $this->error(
      sprintf('The required field "%s" must be mapped before saving.', (string) ($uniqueFieldInfo['label'] ?? 'Required field')),
      422,
      'missing_required_field_mapping'
    );
  }
}
