<?php

namespace PloverCRM\Modules\Integrations\Services;

use PloverCRM\Modules\Automations\Services\AutomationRepository;
use PloverCRM\Modules\Automations\Services\DefinitionValidator;

/**
 * Maps conversions configuration to the generic Automations engine.
 */
class ConversionAutomationService
{
    private const STAGE_EVENT = 'contact_stage_changed';
    private const GOOGLE_ACTION = 'send_google_ads_conversion';
    private const META_ACTION = 'send_meta_capi_event';

    public static function getGoogleMappings(int $connectionId): array
    {
        $automations = self::findMappings(self::GOOGLE_ACTION, $connectionId);

        return array_map(function (array $automation) {
            $definition = $automation['definition_json'] ?? [];
            $action = self::getActionConfig($definition, self::GOOGLE_ACTION);

            return [
                'id' => $automation['id'],
                'connection_id' => absint($action['connection_id'] ?? 0),
                'trigger_type' => 'stage_changed',
                'trigger_id' => self::getStageTrigger($definition),
                'conversion_action_id' => (string) ($action['conversion_action_id'] ?? ''),
                'conversion_action_name' => (string) ($action['conversion_action_name'] ?? ''),
                'conversion_value' => $action['conversion_value'] ?? null,
                'currency_code' => (string) ($action['currency_code'] ?? 'USD'),
                'status' => $automation['status'],
            ];
        }, $automations);
    }

    public static function saveGoogleMapping(array $data): int
    {
        $connectionId = absint($data['connection_id'] ?? 0);
        $stage = sanitize_text_field($data['trigger_id'] ?? '');
        $conversionActionId = sanitize_text_field($data['conversion_action_id'] ?? '');
        $conversionActionName = sanitize_text_field($data['conversion_action_name'] ?? '');

        if ($connectionId <= 0 || $stage === '' || $conversionActionId === '') {
            throw new \InvalidArgumentException('Connection, stage, and conversion action are required.');
        }

        $connection = GoogleAdsConnectionService::getConnection($connectionId);
        $connectionName = $connection ? $connection->customer_name : 'Google Ads';

        $definition = [
            'version' => 1,
            'meta' => [
                'managed_by' => 'conversions',
                'platform' => 'google_ads',
            ],
            'trigger' => [
                'event' => self::STAGE_EVENT,
            ],
            'conditions' => [
                [
                    'field' => 'context.stage.to',
                    'operator' => 'equals',
                    'value' => $stage,
                ],
            ],
            'actions' => [
                [
                    'type' => self::GOOGLE_ACTION,
                    'config' => [
                        'connection_id' => $connectionId,
                        'conversion_action_id' => $conversionActionId,
                        'conversion_action_name' => $conversionActionName,
                        'conversion_value' => ($data['conversion_value'] ?? '') !== ''
                            ? (float) $data['conversion_value']
                            : null,
                        'currency_code' => strtoupper((string) ($data['currency_code'] ?? 'USD')),
                    ],
                ],
            ],
        ];

        self::assertValidDefinition($definition);

        return AutomationRepository::save([
            'id' => absint($data['id'] ?? 0),
            'name' => sprintf('Google Ads Conversion: %s', $stage),
            'description' => sprintf('Send a Google Ads conversion when a contact moves to %s on %s.', $stage, $connectionName),
            'status' => 'active',
            'builder_mode' => 'conversions_google_ads',
            'trigger_event' => self::STAGE_EVENT,
            'definition_json' => $definition,
        ]);
    }

    public static function deleteGoogleMapping(int $automationId): bool
    {
        return self::deleteMapping($automationId, self::GOOGLE_ACTION);
    }

    public static function getMetaMappings(int $connectionId): array
    {
        $automations = self::findMappings(self::META_ACTION, $connectionId);

        return array_map(function (array $automation) {
            $definition = $automation['definition_json'] ?? [];
            $action = self::getActionConfig($definition, self::META_ACTION);

            return [
                'id' => $automation['id'],
                'connection_id' => absint($action['connection_id'] ?? 0),
                'trigger_type' => 'stage_changed',
                'trigger_id' => self::getStageTrigger($definition),
                'event_name' => (string) ($action['event_name'] ?? ''),
                'custom_event_name' => (string) ($action['custom_event_name'] ?? ''),
                'custom_data' => $action['custom_data'] ?? [],
                'custom_data_parameters' => $action['custom_data_parameters'] ?? [],
                'status' => $automation['status'],
            ];
        }, $automations);
    }

    public static function saveMetaMapping(array $data): int
    {
        $connectionId = absint($data['connection_id'] ?? 0);
        $stage = sanitize_text_field($data['trigger_id'] ?? '');
        $eventName = sanitize_text_field($data['event_name'] ?? '');
        $customEventName = sanitize_text_field($data['custom_event_name'] ?? '');

        if ($connectionId <= 0 || $stage === '' || $eventName === '') {
            throw new \InvalidArgumentException('Connection, stage, and event name are required.');
        }

        $connection = MetaCapiConnectionService::getConnection($connectionId);
        $connectionName = $connection ? $connection->name : 'Meta CAPI';

        $definition = [
            'version' => 1,
            'meta' => [
                'managed_by' => 'conversions',
                'platform' => 'meta_capi',
            ],
            'trigger' => [
                'event' => self::STAGE_EVENT,
            ],
            'conditions' => [
                [
                    'field' => 'context.stage.to',
                    'operator' => 'equals',
                    'value' => $stage,
                ],
            ],
            'actions' => [
                [
                    'type' => self::META_ACTION,
                    'config' => [
                        'connection_id' => $connectionId,
                        'event_name' => $eventName,
                        'custom_event_name' => $customEventName,
                        'custom_data' => is_array($data['custom_data'] ?? null) ? $data['custom_data'] : [],
                        'custom_data_parameters' => is_array($data['custom_data_parameters'] ?? null) ? $data['custom_data_parameters'] : [],
                    ],
                ],
            ],
        ];

        self::assertValidDefinition($definition);

        return AutomationRepository::save([
            'id' => absint($data['id'] ?? 0),
            'name' => sprintf('Meta CAPI Event: %s', $stage),
            'description' => sprintf('Send a Meta CAPI event when a contact moves to %s on %s.', $stage, $connectionName),
            'status' => 'active',
            'builder_mode' => 'conversions_meta_capi',
            'trigger_event' => self::STAGE_EVENT,
            'definition_json' => $definition,
        ]);
    }

    public static function deleteMetaMapping(int $automationId): bool
    {
        return self::deleteMapping($automationId, self::META_ACTION);
    }

    private static function findMappings(string $actionType, int $connectionId): array
    {
        $results = [];

        foreach (AutomationRepository::all() as $automation) {
            if (($automation['trigger_event'] ?? '') !== self::STAGE_EVENT) {
                continue;
            }

            $definition = $automation['definition_json'] ?? [];
            $action = self::getActionConfig($definition, $actionType);

            if (!$action || absint($action['connection_id'] ?? 0) !== $connectionId) {
                continue;
            }

            if (self::getStageTrigger($definition) === '') {
                continue;
            }

            $results[] = $automation;
        }

        return $results;
    }

    private static function deleteMapping(int $automationId, string $actionType): bool
    {
        $automation = AutomationRepository::find($automationId);
        if (!$automation) {
            return false;
        }

        $definition = $automation['definition_json'] ?? [];
        $action = self::getActionConfig($definition, $actionType);

        if (!$action) {
            return false;
        }

        return AutomationRepository::delete($automationId);
    }

    private static function getActionConfig(array $definition, string $actionType): ?array
    {
        foreach ((array) ($definition['actions'] ?? []) as $action) {
            if (($action['type'] ?? '') === $actionType) {
                return is_array($action['config'] ?? null) ? $action['config'] : [];
            }
        }

        return null;
    }

    private static function getStageTrigger(array $definition): string
    {
        foreach ((array) ($definition['conditions'] ?? []) as $condition) {
            if (
                ($condition['field'] ?? '') === 'context.stage.to' &&
                ($condition['operator'] ?? '') === 'equals'
            ) {
                return (string) ($condition['value'] ?? '');
            }
        }

        return '';
    }

    private static function assertValidDefinition(array $definition): void
    {
        $validation = DefinitionValidator::validate($definition);
        if ($validation['valid']) {
            return;
        }

        throw new \InvalidArgumentException(implode(' ', $validation['errors']));
    }
}
