<?php
if (!defined('ABSPATH')) exit;

class Calliope_SP_Publish_Helper {
    public static function normalize_payload(array $payload, $network = '') {
        $network = sanitize_key((string) $network);

        $payload['title'] = isset($payload['title']) ? sanitize_text_field((string) $payload['title']) : '';
        $payload['content'] = isset($payload['content']) ? self::normalize_multiline_text($payload['content']) : '';
        $payload['link'] = isset($payload['link']) ? esc_url_raw((string) $payload['link']) : '';
        $payload['alt_text'] = isset($payload['alt_text']) ? sanitize_text_field((string) $payload['alt_text']) : '';
        $payload['hashtags_list'] = self::normalize_hashtags_list($payload['hashtags'] ?? '', $network);
        $payload['hashtags'] = self::format_hashtags($payload['hashtags'] ?? '', $network);

        return $payload;
    }

    public static function normalize_hashtags_list($raw_hashtags, $network = '') {
        $raw_hashtags = wp_strip_all_tags((string) $raw_hashtags);
        $raw_hashtags = preg_replace('/[\r\n\t,;]+/u', ' ', $raw_hashtags);
        $raw_hashtags = preg_replace('/\s+/u', ' ', (string) $raw_hashtags);
        $raw_hashtags = trim((string) $raw_hashtags);

        if ($raw_hashtags === '') {
            return [];
        }

        $parts = preg_split('/\s+/u', $raw_hashtags);
        if (!is_array($parts)) {
            return [];
        }

        $normalized = [];
        $seen = [];

        foreach ($parts as $part) {
            $tag_body = self::sanitize_hashtag_body($part, $network);
            if ($tag_body === '') {
                continue;
            }

            $dedupe_key = function_exists('mb_strtolower')
                ? mb_strtolower($tag_body, 'UTF-8')
                : strtolower($tag_body);

            if (isset($seen[$dedupe_key])) {
                continue;
            }

            $normalized[] = '#' . $tag_body;
            $seen[$dedupe_key] = true;
        }

        return $normalized;
    }

    public static function format_hashtags($raw_hashtags, $network = '') {
        $tags = self::normalize_hashtags_list($raw_hashtags, $network);
        if (empty($tags)) {
            return '';
        }

        switch (sanitize_key((string) $network)) {
            case 'tumblr':
                $tumblr_tags = array_map(static function ($tag) {
                    return ltrim((string) $tag, '#');
                }, $tags);
                return implode(',', array_filter($tumblr_tags));

            default:
                return implode(' ', $tags);
        }
    }

    public static function classify_result(array $result) {
        $network = sanitize_key((string) ($result['network'] ?? ''));
        $error_code = strtolower((string) ($result['error_code'] ?? ''));
        $technical_message = trim((string) ($result['error'] ?? ''));
        $classification = [
            'network'           => $network,
            'label'             => self::get_network_label($network),
            'ok'                => !empty($result['ok']),
            'log_status'        => !empty($result['ok']) ? 'success' : 'error',
            'summary_status'    => !empty($result['ok']) ? 'success' : 'error',
            'classification'    => '',
            'retryable'         => true,
            'short_message'     => !empty($result['ok']) ? __('sent', 'calliope-social-publisher') : ($technical_message !== '' ? $technical_message : __('Unknown error', 'calliope-social-publisher')),
            'user_message'      => !empty($result['ok']) ? __('Published successfully', 'calliope-social-publisher') : ($technical_message !== '' ? $technical_message : __('Unknown error', 'calliope-social-publisher')),
            'technical_message' => $technical_message,
        ];

        if (!empty($classification['ok']) && $error_code === 'duplicate_skipped') {
            $classification['log_status'] = 'warning';
            $classification['summary_status'] = 'warning';
            $classification['classification'] = 'DUPLICATE_SKIPPED';
            $classification['retryable'] = false;
            $classification['short_message'] = __('skipped duplicate', 'calliope-social-publisher');
            $classification['user_message'] = __('This network was skipped because the same publication was already sent or started recently. This prevents duplicate social posts.', 'calliope-social-publisher');
            return $classification;
        }

        if (!empty($classification['ok'])) {
            return $classification;
        }

        if (
            $error_code === 'pinterest_link_blocked' ||
            self::contains_phrase($technical_message, 'blocked this link because it may lead to spam')
        ) {
            $classification['classification'] = 'PINTEREST_LINK_BLOCKED';
            $classification['log_status'] = 'warning';
            $classification['summary_status'] = 'warning';
            $classification['retryable'] = false;
            $classification['short_message'] = __('rejected by anti-spam protection', 'calliope-social-publisher');
            $classification['user_message'] = __('Pinterest rejected this URL because it may have triggered its anti-spam protection. Other selected networks may have published successfully. Try again later with a different URL, image, title, or description. Avoid posting the same link repeatedly.', 'calliope-social-publisher');
            return $classification;
        }

        if (
            $error_code === 'instagram_unsupported_aspect_ratio' ||
            ($network === 'instagram' && self::contains_phrase($technical_message, 'aspect ratio is not supported'))
        ) {
            $classification['classification'] = 'INSTAGRAM_UNSUPPORTED_ASPECT_RATIO';
            $classification['retryable'] = true;
            $classification['short_message'] = __('image aspect ratio was rejected', 'calliope-social-publisher');
            $classification['user_message'] = __('Instagram rejected the image aspect ratio even after the plugin tried to prepare a compatible crop. Please try a square, 4:5 portrait, or 1.91:1 landscape image.', 'calliope-social-publisher');
            return $classification;
        }

        if (
            $error_code === 'instagram_invalid_media_url' ||
            ($network === 'instagram' && (
                self::contains_phrase($technical_message, 'only photo or video can be accepted as media type') ||
                self::contains_phrase($technical_message, '2207052') ||
                self::contains_phrase($technical_message, 'could not download a valid image')
            ))
        ) {
            $classification['classification'] = 'INSTAGRAM_INVALID_MEDIA_URL';
            $classification['retryable'] = false;
            $classification['short_message'] = __('could not fetch or accept the media URL', 'calliope-social-publisher');
            $classification['user_message'] = __('Instagram rejected the media URL because it could not download a valid image. Please try another image or use Original format.', 'calliope-social-publisher');
            return $classification;
        }

        if (
            $error_code === 'threads_invalid_media_url' ||
            ($network === 'threads' && (
                self::contains_phrase($technical_message, '2207052') ||
                self::contains_phrase($technical_message, 'could not download a valid image') ||
                self::contains_phrase($technical_message, 'public media url is not accessible')
            ))
        ) {
            $classification['classification'] = 'THREADS_INVALID_MEDIA_URL';
            $classification['retryable'] = false;
            $classification['short_message'] = __('could not fetch or accept the media URL', 'calliope-social-publisher');
            $classification['user_message'] = __('Threads rejected the media URL because it could not download a valid image. Please try another image or use Original format.', 'calliope-social-publisher');
            return $classification;
        }

        if (
            $error_code === 'blogger_invalid_client' ||
            self::contains_phrase($technical_message, 'invalid_client') ||
            self::contains_phrase($technical_message, 'provided client secret is invalid')
        ) {
            $classification['classification'] = 'BLOGGER_INVALID_CLIENT';
            $classification['retryable'] = false;
            $classification['short_message'] = __('invalid OAuth client secret', 'calliope-social-publisher');
            $classification['user_message'] = __('Blogger rejected the saved OAuth client credentials. Update the Client Secret in Google Cloud and reconnect Blogger.', 'calliope-social-publisher');
            return $classification;
        }

        if (
            $error_code === 'facebook_identity_confirmation' ||
            self::contains_phrase($technical_message, 'confirm your identity') ||
            self::contains_phrase($technical_message, 'con' . 'firma tu iden' . 'tidad') ||
            self::contains_phrase($technical_message, 'identity confirmation') ||
            self::contains_phrase($technical_message, 'checkpoint') ||
            self::contains_phrase($technical_message, 'security check') ||
            self::contains_phrase($technical_message, 'page publishing authorization') ||
            self::contains_phrase($technical_message, 'account verification') ||
            self::contains_phrase($technical_message, 'verify your identity') ||
            self::contains_phrase($technical_message, 'veri' . 'fica tu iden' . 'tidad') ||
            self::contains_phrase($technical_message, 'facebook mobile app') ||
            self::contains_phrase($technical_message, 'app mo' . 'vil de face' . 'book') ||
            self::contains_phrase($technical_message, 'confirm your account or phone') ||
            self::contains_phrase($technical_message, 'con' . 'firmar tu cue' . 'nta o tu cel' . 'ular') ||
            self::contains_phrase($technical_message, 'con' . 'firma tu cue' . 'nta o tu cel' . 'ular')
        ) {
            $classification['classification'] = 'FACEBOOK_IDENTITY_CONFIRMATION';
            $classification['retryable'] = false;
            $classification['short_message'] = __('identity confirmation required', 'calliope-social-publisher');
            $classification['user_message'] = __('Facebook requires identity confirmation before publishing to this Page. Open the Facebook mobile app with the connected account and follow the instructions to confirm your account or phone. After completing it, disconnect and reconnect Facebook in the plugin, then try again.', 'calliope-social-publisher');
            return $classification;
        }

        if (
            $error_code === 'facebook_missing_publish_permission' ||
            self::contains_phrase($technical_message, 'pages_manage_posts') ||
            (self::contains_phrase($technical_message, 'permission') && self::contains_phrase($technical_message, 'publish'))
        ) {
            $classification['classification'] = 'FACEBOOK_MISSING_PUBLISH_PERMISSION';
            $classification['retryable'] = false;
            $classification['short_message'] = __('missing publish permission', 'calliope-social-publisher');
            $classification['user_message'] = __('Facebook is connected, but the Page token does not include permission to publish posts. Please disconnect and reconnect Facebook, making sure the selected Page is allowed and pages_manage_posts is granted.', 'calliope-social-publisher');
            return $classification;
        }

        if ($error_code === 'facebook_media_error') {
            $classification['classification'] = 'FACEBOOK_MEDIA_ERROR';
            $classification['retryable'] = true;
            $classification['short_message'] = __('rejected the image/media', 'calliope-social-publisher');
            $classification['user_message'] = __('Facebook rejected the image/media for this post. The Page is connected, but Meta could not process the image URL or media payload. Please try a different image or publish a text-only test.', 'calliope-social-publisher');
            return $classification;
        }

        if (
            $error_code === 'facebook_action_restricted' ||
            $error_code === 'facebook_publish_restricted'
        ) {
            $classification['classification'] = 'FACEBOOK_ACTION_RESTRICTED';
            $classification['log_status'] = 'warning';
            $classification['summary_status'] = 'warning';
            $classification['retryable'] = true;
            $classification['short_message'] = __('publishing restricted by Facebook', 'calliope-social-publisher');
            $classification['user_message'] = __('Facebook rejected this publish request due to a publishing restriction or temporary action block. The Page is connected, but Meta did not allow this specific post to be published. Please try a simpler text-only post, wait a while, or review the app/Page permissions.', 'calliope-social-publisher');
            return $classification;
        }

        if (
            $error_code === 'facebook_missing_page_token' ||
            self::contains_phrase($technical_message, 'no publishable page token')
        ) {
            $classification['classification'] = 'FACEBOOK_MISSING_PAGE_TOKEN';
            $classification['retryable'] = false;
            $classification['short_message'] = __('no publishable Page token found', 'calliope-social-publisher');
            $classification['user_message'] = __('Facebook is connected, but no publishable Page token was found.', 'calliope-social-publisher');
            return $classification;
        }

        return $classification;
    }

    public static function build_report(array $results) {
        $classified_results = [];
        $lines = [];
        $success_count = 0;
        $failed_count = 0;

        foreach ($results as $key => $result) {
            $classified = self::classify_result(is_array($result) ? $result : []);
            $classified_results[$key] = array_merge(
                is_array($result) ? $result : [],
                [
                    'ui_status'      => $classified['summary_status'],
                    'classification' => $classified['classification'],
                    'retryable'      => $classified['retryable'],
                    'user_message'   => $classified['user_message'],
                    'short_message'  => $classified['short_message'],
                ]
            );

            $lines[] = $classified['label'] . ': ' . $classified['short_message'];

            if (!empty($classified['ok'])) {
                $success_count++;
            } else {
                $failed_count++;
            }
        }

        $all_ok = $failed_count === 0;
        $partial = $success_count > 0 && $failed_count > 0;

        if ($all_ok) {
            $headline = __('Published successfully', 'calliope-social-publisher');
            $notice_type = 'success';
        } elseif ($partial) {
            $headline = __('Published partially', 'calliope-social-publisher');
            $notice_type = 'warning';
        } else {
            $headline = __('Publishing failed', 'calliope-social-publisher');
            $notice_type = 'error';
        }

        $summary = $headline;
        if (!empty($lines)) {
            $summary .= ' | ' . implode(' | ', $lines);
        }

        return [
            'all_ok'         => $all_ok,
            'partial'        => $partial,
            'notice_type'    => $notice_type,
            'headline'       => $headline,
            'summary'        => $summary,
            'lines'          => $lines,
            'results'        => $classified_results,
            'success_count'  => $success_count,
            'failed_count'   => $failed_count,
        ];
    }

    public static function get_network_label($network) {
        $labels = [
            'bsky'      => __('Bluesky', 'calliope-social-publisher'),
            'bluesky'   => __('Bluesky', 'calliope-social-publisher'),
            'mastodon'  => __('Mastodon', 'calliope-social-publisher'),
            'tumblr'    => __('Tumblr', 'calliope-social-publisher'),
            'pinterest' => __('Pinterest', 'calliope-social-publisher'),
            'threads'   => __('Threads', 'calliope-social-publisher'),
            'instagram' => __('Instagram', 'calliope-social-publisher'),
            'linkedin'  => __('LinkedIn', 'calliope-social-publisher'),
            'blogger'   => __('Blogger', 'calliope-social-publisher'),
            'telegram'  => __('Telegram', 'calliope-social-publisher'),
            'facebook'  => __('Facebook', 'calliope-social-publisher'),
        ];

        $network = sanitize_key((string) $network);
        return $labels[$network] ?? ucfirst($network);
    }

    private static function normalize_multiline_text($value) {
        $value = (string) $value;
        $value = preg_replace("/\r\n?/", "\n", $value);
        $value = preg_replace("/[ \t]+\n/", "\n", (string) $value);
        return trim((string) $value);
    }

    private static function sanitize_hashtag_body($tag, $network = '') {
        $tag = trim((string) $tag);
        $tag = preg_replace('/^[#\s,;]+/u', '', $tag);
        $tag = preg_replace('/[#\s,;]+$/u', '', (string) $tag);
        $tag = preg_replace('/[^\p{L}\p{M}\p{N}_]+/u', '', (string) $tag);

        if ($tag === '') {
            return '';
        }

        if (in_array(sanitize_key((string) $network), ['bluesky'], true) && function_exists('remove_accents')) {
            $ascii_fallback = remove_accents($tag);
            $ascii_fallback = preg_replace('/[^\p{L}\p{M}\p{N}_]+/u', '', (string) $ascii_fallback);
            if ($ascii_fallback !== '') {
                return $tag;
            }
        }

        return $tag;
    }

    private static function contains_phrase($haystack, $needle) {
        return $haystack !== '' && stripos((string) $haystack, (string) $needle) !== false;
    }
}
