<?php

namespace Formatrica\Submission\Integrations\Dispatchers;

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

use Formatrica\Submission\Integrations\Integration_Dispatcher_Interface;
use WP_Error;

/**
 * Create a WordPress post from a form submission
 */
final class WordPress_Post_Dispatcher implements Integration_Dispatcher_Interface
{
    /**
     * @param array<string,mixed> $settings
     * @param array<string,mixed> $form
     * @param array<string,mixed> $payload
     * @param array<string,mixed> $context
     * @param array<int, array<string,mixed>> $field_meta
     */
    public function dispatch(array $settings, array $form, array $payload, array $context, array $field_meta): void
    {
        if (empty($settings['enabled'])) {
            return;
        }

        $post_type = isset($settings['post_type']) ? sanitize_text_field((string) $settings['post_type']) : 'post';
        $post_status = isset($settings['post_status']) ? sanitize_text_field((string) $settings['post_status']) : 'pending';
        $map = isset($settings['map']) && is_array($settings['map']) ? $settings['map'] : [];

        $author_mode = isset($settings['author_mode']) ? sanitize_text_field((string) $settings['author_mode']) : 'current_user';
        $fixed_author_id = isset($settings['fixed_author_id']) ? absint((int) $settings['fixed_author_id']) : 0;
        $taxonomies = isset($settings['taxonomies']) && is_array($settings['taxonomies']) ? $settings['taxonomies'] : [];
        $meta_mappings = isset($settings['meta']) && is_array($settings['meta']) ? $settings['meta'] : [];

        $title_key = isset($map['title']) ? (string) $map['title'] : '';
        $content_key = isset($map['content']) ? (string) $map['content'] : '';
        $excerpt_key = isset($map['excerpt']) ? (string) $map['excerpt'] : '';
        $featured_key = isset($map['featured_image']) ? (string) $map['featured_image'] : '';

        // Resolve author according to mode
        $post_author = 0;
        switch ($author_mode) {
            case 'fixed':
                $post_author = $fixed_author_id > 0 ? $fixed_author_id : 0;
                break;
            case 'anonymous':
                $post_author = 0;
                break;
            case 'current_user':
            default:
                $post_author = get_current_user_id();
                break;
        }

        $postarr = [
            'post_type' => $post_type,
            'post_status' => $this->maybe_downgrade_status($post_status, $post_type, $post_author),
            'post_title' => $title_key !== '' && isset($payload[$title_key])
                ? sanitize_text_field((string) $payload[$title_key])
                : '',
            'post_content' => $content_key !== '' && isset($payload[$content_key])
                ? wp_kses_post((string) $payload[$content_key])
                : '',
            'post_excerpt' => $excerpt_key !== '' && isset($payload[$excerpt_key])
                ? sanitize_text_field((string) $payload[$excerpt_key])
                : '',
        ];

        if ($post_author) {
            $postarr['post_author'] = absint($post_author);
        }

        $postarr = apply_filters('formatrica_wp_post_args', $postarr, $form, $payload, $settings, $context);

        $post_id = wp_insert_post($postarr, true);

        if ($post_id instanceof WP_Error || !$post_id) {
            return;
        }

        if ($featured_key !== '') {
            $this->maybe_set_featured_image($post_id, $featured_key, $field_meta);
        }

        // Apply taxonomy mappings
        if (!empty($taxonomies)) {
            $this->apply_taxonomies((int) $post_id, $taxonomies, $payload);
        }

        // Apply post meta mappings
        if (!empty($meta_mappings)) {
            $this->apply_meta((int) $post_id, $meta_mappings, $payload);
        }

        do_action('formatrica_wp_post_created', (int) $post_id, $form, $payload);
    }

    /**
     * If the desired status is publish but the chosen author can't publish for this post type,
     * downgrade to pending. Otherwise return the desired status.
     */
    private function maybe_downgrade_status(string $status, string $post_type, int $author_id): string
    {
        if ($status !== 'publish') {
            return $status;
        }

        // Resolve capability to check
        $cap = $post_type === 'post' ? 'publish_posts' : ('publish_' . $post_type . 's');

        // If there's a logged-in author set, check their caps; else check current user
        $user_id = $author_id > 0 ? $author_id : get_current_user_id();
        if ($user_id > 0) {
            $user = get_user_by('id', $user_id);
            if ($user && user_can($user, $cap)) {
                return 'publish';
            }
        }

        return 'pending';
    }

    /**
     * @param int $post_id
     * @param string $field_name
     * @param array<int, array<string,mixed>> $field_meta
     */
    private function maybe_set_featured_image(int $post_id, string $field_name, array $field_meta): void
    {
        $meta = null;
        foreach ($field_meta as $fm) {
            if (($fm['name'] ?? '') === $field_name && !empty($fm['meta'])) {
                $meta = $fm['meta'];
                break;
            }
        }

        if (!$meta || empty($meta['url'])) {
            return;
        }

        $url = (string) $meta['url'];

        if (!function_exists('media_sideload_image')) {
            require_once ABSPATH . 'wp-admin/includes/media.php';
            require_once ABSPATH . 'wp-admin/includes/file.php';
            require_once ABSPATH . 'wp-admin/includes/image.php';
        }

        $attachment_id = media_sideload_image($url, $post_id, null, 'id');

        if (!is_wp_error($attachment_id) && $attachment_id) {
            set_post_thumbnail($post_id, (int) $attachment_id);
        }
    }

    /**
     * @param int $post_id
     * @param array<int, array<string,mixed>> $taxonomies
     * @param array<string,mixed> $payload
     */
    private function apply_taxonomies(int $post_id, array $taxonomies, array $payload): void
    {
        foreach ($taxonomies as $row) {
            $taxonomy = isset($row['taxonomy']) ? sanitize_text_field((string) $row['taxonomy']) : '';
            $field = isset($row['field']) ? (string) $row['field'] : '';
            $mode = isset($row['mode']) && $row['mode'] === 'ids' ? 'ids' : 'names';

            if ($taxonomy === '' || $field === '' || !isset($payload[$field])) {
                continue;
            }

            $raw = $payload[$field];
            $items = [];

            if (is_array($raw)) {
                $items = $raw;
            } else {
                $items = array_map('trim', explode(',', (string) $raw));
            }

            if ($mode === 'ids') {
                $term_ids = array_values(array_filter(array_map('intval', $items), fn($n) => $n > 0));
                if (!empty($term_ids)) {
                    wp_set_post_terms($post_id, $term_ids, $taxonomy, false);
                }
            } else {
                $term_ids = [];
                foreach ($items as $name) {
                    if ($name === '') {
                        continue;
                    }
                    $exists = term_exists($name, $taxonomy);
                    if ($exists && !is_wp_error($exists)) {
                        $term_ids[] = is_array($exists) ? (int) ($exists['term_id'] ?? 0) : (int) $exists;
                        continue;
                    }
                    // Attempt to create term if it doesn't exist
                    $created = wp_insert_term($name, $taxonomy);
                    if (!is_wp_error($created)) {
                        $term_ids[] = (int) ($created['term_id'] ?? 0);
                    }
                }

                if (!empty($term_ids)) {
                    wp_set_post_terms($post_id, $term_ids, $taxonomy, false);
                }
            }
        }
    }

    /**
     * @param int $post_id
     * @param array<int, array<string,mixed>> $meta_mappings
     * @param array<string,mixed> $payload
     */
    private function apply_meta(int $post_id, array $meta_mappings, array $payload): void
    {
        $is_acf_active = function_exists('get_field_object');

        foreach ($meta_mappings as $row) {
            $key = isset($row['key']) ? sanitize_text_field((string) $row['key']) : '';
            $field = isset($row['field']) ? (string) $row['field'] : '';
            if ($key === '' || $field === '' || !isset($payload[$field])) {
                continue;
            }

            $value = $payload[$field];

            // Handle ACF fields if ACF is active
            if ($is_acf_active) {
                $acf_field = get_field_object($key, $post_id);
                if ($acf_field && !empty($acf_field['type'])) {
                    $value = $this->format_acf_value($acf_field, $value);
                    update_field($key, $value, $post_id);
                    continue;
                }
            }

            // Standard post meta handling
            if (is_array($value)) {
                update_post_meta($post_id, $key, $value);
            } else {
                update_post_meta($post_id, $key, sanitize_text_field((string) $value));
            }
        }
    }

    /**
     * Format value according to ACF field type
     *
     * @param array<string,mixed> $acf_field
     * @param mixed $value
     * @return mixed
     */
    private function format_acf_value(array $acf_field, $value)
    {
        $field_type = $acf_field['type'] ?? '';

        switch ($field_type) {
            case 'text':
            case 'textarea':
            case 'email':
            case 'url':
            case 'password':
                return is_string($value) ? sanitize_text_field($value) : '';

            case 'number':
            case 'range':
                return is_numeric($value) ? (float) $value : 0;

            case 'true_false':
                return (bool) $value;

            case 'select':
            case 'radio':
            case 'button_group':
                // Single selection - return as is if valid
                return is_string($value) || is_numeric($value) ? $value : '';

            case 'checkbox':
                // Multiple selection - ensure array format
                if (is_array($value)) {
                    return array_map('sanitize_text_field', $value);
                }
                return is_string($value) ? [sanitize_text_field($value)] : [];

            case 'post_object':
            case 'page_link':
                // Post ID(s)
                if (is_array($value)) {
                    return array_map('absint', array_filter($value, 'is_numeric'));
                }
                return is_numeric($value) ? absint($value) : 0;

            case 'relationship':
                // Array of post IDs
                if (is_array($value)) {
                    return array_map('absint', array_filter($value, 'is_numeric'));
                }
                return is_numeric($value) ? [absint($value)] : [];

            case 'taxonomy':
                // Term ID(s)
                if (is_array($value)) {
                    return array_map('absint', array_filter($value, 'is_numeric'));
                }
                return is_numeric($value) ? absint($value) : 0;

            case 'user':
                // User ID(s)
                if (is_array($value)) {
                    return array_map('absint', array_filter($value, 'is_numeric'));
                }
                return is_numeric($value) ? absint($value) : 0;

            case 'image':
            case 'file':
                // Attachment ID or URL
                return is_numeric($value) ? absint($value) : (is_string($value) ? esc_url_raw($value) : '');

            case 'gallery':
                // Array of attachment IDs
                if (is_array($value)) {
                    return array_map('absint', array_filter($value, 'is_numeric'));
                }
                return is_numeric($value) ? [absint($value)] : [];

            case 'wysiwyg':
                return is_string($value) ? wp_kses_post($value) : '';

            case 'oembed':
                return is_string($value) ? esc_url_raw($value) : '';

            case 'date_picker':
            case 'date_time_picker':
            case 'time_picker':
                // Date/time values - return as string
                return is_string($value) ? sanitize_text_field($value) : '';

            case 'color_picker':
                // Hex color code
                return is_string($value) ? sanitize_hex_color($value) : '';

            case 'link':
                // ACF link field expects array with url, title, target
                if (is_array($value)) {
                    return [
                        'url' => isset($value['url']) ? esc_url_raw($value['url']) : '',
                        'title' => isset($value['title']) ? sanitize_text_field($value['title']) : '',
                        'target' => isset($value['target']) ? sanitize_text_field($value['target']) : '',
                    ];
                }
                // If just a URL string
                return is_string($value) ? ['url' => esc_url_raw($value), 'title' => '', 'target' => ''] : [];

            case 'google_map':
                // Google Map field expects array with lat, lng, address
                if (is_array($value)) {
                    return [
                        'lat' => isset($value['lat']) ? (float) $value['lat'] : 0,
                        'lng' => isset($value['lng']) ? (float) $value['lng'] : 0,
                        'address' => isset($value['address']) ? sanitize_text_field($value['address']) : '',
                    ];
                }
                return [];

            case 'repeater':
            case 'group':
            case 'flexible_content':
                // Complex fields - return as array
                return is_array($value) ? $value : [];

            default:
                // For unknown field types, sanitize based on value type
                if (is_array($value)) {
                    return $value;
                }
                return is_string($value) ? sanitize_text_field($value) : $value;
        }
    }
}
