<?php
/**
 * Secret-free local destination registry used by extensions that need to inspect
 * the configured publishing endpoints without performing provider API requests.
 *
 * @package CalliopeSocialPublisher
 */

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

final class Calliope_SP_Destination_Registry {
    /**
     * Canonical publishing choices. Pro extends this list through the existing filter.
     *
     * @return array<int,array{key:string,label:string}>
     */
    public static function get_choices(): array {
        $labels = [];
        if (class_exists('Calliope_SP_Platform_Preview')) {
            $labels = Calliope_SP_Platform_Preview::network_labels();
        } elseif (class_exists('Calliope_SP_Social_Templates')) {
            $labels = Calliope_SP_Social_Templates::network_labels();
        }

        $choices = [];
        foreach ((array) $labels as $key => $label) {
            $key = sanitize_key((string) $key);
            $label = sanitize_text_field((string) $label);
            if ($key !== '' && $label !== '') {
                $choices[] = ['key' => $key, 'label' => $label];
            }
        }

        // Preserve the existing extension point used by Pro for additional destinations.
        $choices = apply_filters('calliope_sp_destination_choices', $choices);
        if (!is_array($choices)) {
            return [];
        }

        $normalized = [];
        $seen = [];
        foreach ($choices as $choice) {
            if (!is_array($choice)) {
                continue;
            }
            $key = sanitize_key((string) ($choice['key'] ?? ''));
            $label = sanitize_text_field((string) ($choice['label'] ?? ''));
            if ($key === '' || $label === '' || isset($seen[$key])) {
                continue;
            }
            $seen[$key] = true;
            $normalized[] = ['key' => $key, 'label' => $label];
        }
        return $normalized;
    }

    /**
     * Return the locally-known Free destinations only. No provider request is made.
     *
     * @return array<int,array<string,mixed>>
     */
    public static function get_local_destinations(): array {
        $out = [];
        foreach (self::get_choices() as $choice) {
            $network = self::normalize_network((string) $choice['key']);
            if (!in_array($network, self::free_networks(), true)) {
                continue;
            }
            $destination = self::build_free_destination($network, (string) $choice['label']);
            if ($destination !== null) {
                $out[] = $destination;
            }
        }
        return $out;
    }

    /** @return string[] */
    private static function free_networks(): array {
        if (class_exists('Calliope_SP_Platform_Preview')) {
            return array_values(array_unique(array_map([__CLASS__, 'normalize_network'], array_keys(Calliope_SP_Platform_Preview::network_labels()))));
        }
        if (class_exists('Calliope_SP_Social_Templates')) {
            return array_values(array_unique(array_map([__CLASS__, 'normalize_network'], array_keys(Calliope_SP_Social_Templates::network_labels()))));
        }
        return [];
    }

    private static function normalize_network(string $network): string {
        $network = sanitize_key($network);
        return $network === 'bsky' ? 'bluesky' : $network;
    }

    /** @return array<string,mixed>|null */
    private static function build_free_destination(string $network, string $fallback_label): ?array {
        $suffix = $network === 'linkedin' ? 'LinkedIn' : ucfirst($network);
        $class = 'Calliope_SP_' . $suffix;
        if ($class === '' || !class_exists($class)) {
            return null;
        }

        $provider = new $class();
        if (!method_exists($provider, 'get_connection_state')) {
            return null;
        }
        $state_object = $provider->get_connection_state();
        if (is_wp_error($state_object) || !($state_object instanceof Calliope_SP_Connection_State)) {
            return null;
        }
        $state = $state_object->to_array();
        $reference = self::reference_for_network($network);
        $configured = !empty($state['is_configured']) || !empty($state['is_authenticated']) || $reference['target_key'] !== '';
        if (!$configured) {
            return null;
        }

        $label = self::label_for_network($network, $reference['target_key'], $fallback_label);
        return [
            'network' => $network,
            'network_label' => sanitize_text_field($fallback_label),
            'connection_key' => 'primary',
            'target_key' => $reference['target_key'] !== '' ? $reference['target_key'] : 'primary',
            'profile_uuid' => '',
            'label' => $label,
            'status' => sanitize_key((string) ($state['status'] ?? 'not_configured')),
            'health' => self::health_from_state($state),
            'available' => true,
            'source' => 'free',
        ];
    }

    /** @return array{target_key:string} */
    private static function reference_for_network(string $network): array {
        switch ($network) {
            case 'bluesky':
                return ['target_key' => self::clean_key_value((string) get_option('calliope_sp_bsky_handle', ''))];
            case 'mastodon':
                return ['target_key' => self::clean_key_value((string) get_option('calliope_sp_mstdn_instance', ''))];
            case 'tumblr':
                return ['target_key' => self::clean_key_value((string) get_option('calliope_sp_tumblr_blog_identifier', ''))];
            case 'pinterest':
                return ['target_key' => self::clean_key_value((string) get_option('calliope_sp_pinterest_board_id', ''))];
            case 'threads':
                return ['target_key' => self::clean_key_value((string) get_option('calliope_sp_threads_user_id', ''))];
            case 'instagram':
                return ['target_key' => self::clean_key_value((string) get_option('calliope_sp_ig_user_id', ''))];
            case 'linkedin':
                $urn = (string) get_option('calliope_sp_li_person_id', '');
                return ['target_key' => self::clean_key_value($urn)];
            case 'blogger':
                return ['target_key' => self::clean_key_value((string) get_option('calliope_sp_blogger_blog_id', ''))];
            case 'telegram':
                $chat = self::clean_key_value((string) get_option('calliope_sp_telegram_chat_id', ''));
                $thread = absint(get_option('calliope_sp_telegram_message_thread_id', 0));
                return ['target_key' => $thread > 0 && $chat !== '' ? $chat . ':thread:' . $thread : $chat];
            case 'facebook':
                return ['target_key' => self::clean_key_value((string) get_option('calliope_sp_fb_page_id', ''))];
        }
        return ['target_key' => ''];
    }

    private static function label_for_network(string $network, string $target_key, string $fallback): string {
        if ($network === 'bluesky' && $target_key !== '') {
            return '@' . ltrim($target_key, '@');
        }
        if ($network === 'mastodon' && $target_key !== '') {
            return $target_key;
        }
        if ($network === 'tumblr' && $target_key !== '') {
            return $target_key;
        }
        if ($network === 'telegram' && $target_key !== '') {
            return str_starts_with($target_key, '@') ? $target_key : $fallback;
        }
        if ($network === 'facebook') {
            $label = self::lookup_cached_label(get_option('calliope_sp_fb_pages_last', []), (string) get_option('calliope_sp_fb_page_id', ''), ['id','page_id'], ['name','label','title']);
            if ($label !== '') { return $label; }
        }
        if ($network === 'pinterest') {
            $cache = get_option('calliope_sp_pinterest_boards_last', []);
            $label = self::lookup_cached_label($cache, (string) get_option('calliope_sp_pinterest_board_id', ''), ['id','board_id'], ['name','label','title']);
            if ($label !== '') { return $label; }
        }
        if ($network === 'blogger') {
            $label = self::lookup_cached_label(get_option('calliope_sp_blogger_blogs_last', []), (string) get_option('calliope_sp_blogger_blog_id', ''), ['id','blog_id'], ['name','label','title']);
            if ($label !== '') { return $label; }
        }
        return sanitize_text_field($fallback);
    }

    /**
     * Search a small local cache for a human label. The cache may be JSON or an array.
     */
    private static function lookup_cached_label($raw, string $wanted, array $id_keys, array $label_keys): string {
        if (is_string($raw)) {
            $decoded = json_decode($raw, true);
            $raw = is_array($decoded) ? $decoded : [];
        }
        if (!is_array($raw) || $wanted === '') {
            return '';
        }
        $queue = array_values($raw);
        $visited = 0;
        while ($queue !== [] && $visited < 250) {
            $node = array_shift($queue);
            $visited++;
            if (!is_array($node)) { continue; }
            $candidate_id = '';
            foreach ($id_keys as $key) {
                if (isset($node[$key]) && is_scalar($node[$key])) { $candidate_id = trim((string) $node[$key]); break; }
            }
            if ($candidate_id !== '' && hash_equals($wanted, $candidate_id)) {
                foreach ($label_keys as $key) {
                    if (isset($node[$key]) && is_scalar($node[$key])) {
                        $label = sanitize_text_field((string) $node[$key]);
                        if ($label !== '') { return $label; }
                    }
                }
            }
            foreach ($node as $value) {
                if (is_array($value)) { $queue[] = $value; }
            }
        }
        return '';
    }

    /** @param array<string,mixed> $state */
    private static function health_from_state(array $state): string {
        if (!empty($state['is_ready'])) {
            return 'ready';
        }
        $status = sanitize_key((string) ($state['status'] ?? ''));
        if (in_array($status, ['refresh_required','token_expired','token_invalid','permission_missing','environment_mismatch'], true)) {
            return 'needs_reconnection';
        }
        if (in_array($status, ['not_configured','credentials_saved'], true)) {
            return 'disconnected';
        }
        return 'unavailable';
    }

    private static function clean_key_value(string $value): string {
        $value = trim(wp_strip_all_tags($value, true));
        $value = preg_replace('/[\x00-\x1F\x7F]/u', '', (string) $value);
        if (function_exists('mb_substr')) {
            return mb_substr($value, 0, 190, 'UTF-8');
        }
        return substr($value, 0, 190);
    }
}
