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

class VMO_Promotion_Discord_Connector extends VMO_Promotion_Configured_Connector {
    private $store;

    public function __construct($platform_key, $platform_label, array $capabilities = array(), array $provider_profile = array()) {
        parent::__construct($platform_key, $platform_label, $capabilities, $provider_profile);
        $this->store = class_exists('VMO_Promotion_Data_Store') ? VMO_Promotion_Data_Store::get_instance() : null;
    }

    public function connect_account(array $input) {
        $settings = $this->get_settings();
        $missing = $this->get_missing_required_fields($settings);
        if (!empty($missing)) {
            return new WP_Error(
                'missing_credentials',
                sprintf('Missing required credentials for %s: %s.', $this->platform_label, implode(', ', array_map(array('VMO_Promotion_Admin_Settings', 'label_for_field'), $missing))),
                array(
                    'platform_key' => $this->platform_key,
                    'missing_fields' => $missing,
                )
            );
        }

        $me = $this->discord_request('GET', '/users/@me', $settings['bot_token']);
        if (is_wp_error($me)) {
            return $me;
        }

        $channel = array();
        $target_channel_id = sanitize_text_field($settings['default_target_id'] ?? '');
        if ($target_channel_id !== '') {
            $channel = $this->discord_request('GET', '/channels/' . rawurlencode($target_channel_id), $settings['bot_token']);
            if (is_wp_error($channel)) {
                return $channel;
            }
        }

        $user_id = (int) ($input['user_id'] ?? 0);
        $account = array(
            'platform_key' => 'discord_community',
            'platform_label' => 'Discord Community',
            'external_account_id' => sanitize_text_field($me['id'] ?? ''),
            'display_name' => sanitize_text_field($me['global_name'] ?? $me['username'] ?? 'Discord Bot'),
            'auth_status' => 'connected',
            'granted_scopes' => array('bot', 'applications.commands'),
            'access_token' => sanitize_text_field($settings['bot_token'] ?? ''),
            'refresh_token' => '',
            'token_expires_at' => '',
            'refresh_status' => 'not_applicable',
            'app_review_status' => 'approved',
            'publish_enabled' => !empty($settings['publish_enabled']),
            'reply_enabled' => !empty($settings['reply_enabled']),
            'metrics_enabled' => !empty($settings['metrics_enabled']),
            'connection_metadata' => array(
                'connection_role' => 'discord_bot',
                'bot_username' => sanitize_text_field($me['username'] ?? ''),
                'bot_discriminator' => sanitize_text_field($me['discriminator'] ?? ''),
                'default_target_id' => $target_channel_id,
                'default_target_label' => sanitize_text_field($settings['default_target_label'] ?? ($channel['name'] ?? '')),
                'target_channel_type' => sanitize_text_field($channel['type'] ?? ''),
                'target_guild_id' => sanitize_text_field($channel['guild_id'] ?? ''),
            ),
        );

        $saved = $this->store ? $this->store->upsert_connected_account($user_id, $account) : $account;
        $response = array(
            'success' => true,
            'platform_key' => 'discord_community',
            'platform_label' => 'Discord Community',
            'requires_redirect' => false,
            'connected_accounts' => !empty($saved) ? array($saved) : array(),
            'connected_accounts_count' => !empty($saved) ? 1 : 0,
            'message' => 'Discord bot validated and connected.',
        );

        if (!empty($settings['client_id'])) {
            $response['invite_url'] = add_query_arg(array(
                'client_id' => sanitize_text_field($settings['client_id']),
                'scope' => 'bot applications.commands',
            ), 'https://discord.com/oauth2/authorize');
        }

        return $response;
    }

    public function handle_callback(array $request) {
        return new WP_Error('provider_not_ready', 'Discord publishing in this connector uses a bot token and target channel configuration, not a user OAuth callback.');
    }

    public function refresh_auth(array $input) {
        return $this->connect_account($input);
    }

    public function publish_text_post(array $input) {
        $account = $this->get_publish_account($input);
        if (empty($account['access_token'])) {
            return new WP_Error('missing_connected_account', 'A connected Discord bot is required before publishing.');
        }

        $target = $this->get_publish_target($account);
        if ($target === '') {
            return new WP_Error('missing_publish_target', 'Set a default Discord channel or thread ID in VMO Settings before publishing.');
        }

        $content_item = (array) ($input['content_item'] ?? array());
        $response = $this->discord_request('POST', '/channels/' . rawurlencode($target) . '/messages', $account['access_token'], array(
            'content' => $this->get_caption($content_item),
            'allowed_mentions' => array('parse' => array()),
        ));
        if (is_wp_error($response)) {
            return $response;
        }

        return array(
            'provider_post_id' => sanitize_text_field($response['id'] ?? ''),
            'message' => 'Published to Discord.',
            'response' => $response,
        );
    }

    public function publish_media_post(array $input) {
        $account = $this->get_publish_account($input);
        if (empty($account['access_token'])) {
            return new WP_Error('missing_connected_account', 'A connected Discord bot is required before publishing.');
        }

        $target = $this->get_publish_target($account);
        if ($target === '') {
            return new WP_Error('missing_publish_target', 'Set a default Discord channel or thread ID in VMO Settings before publishing.');
        }

        $content_item = (array) ($input['content_item'] ?? array());
        $asset = $this->extract_asset($content_item);
        if (empty($asset['url'])) {
            return $this->publish_text_post($input);
        }

        $asset_response = wp_remote_get($asset['url'], array(
            'timeout' => 180,
        ));
        if (is_wp_error($asset_response)) {
            return $asset_response;
        }

        $binary = wp_remote_retrieve_body($asset_response);
        if ($binary === '') {
            return new WP_Error('empty_asset', 'Discord upload could not read the selected media asset.');
        }

        $filename = sanitize_file_name($asset['name'] ?? basename(parse_url($asset['url'], PHP_URL_PATH) ?: 'discord-upload'));
        if ($filename === '') {
            $filename = 'discord-upload';
        }

        $mime = sanitize_text_field($asset['type'] ?? wp_remote_retrieve_header($asset_response, 'content-type') ?? 'application/octet-stream');
        $payload = array(
            'content' => $this->get_caption($content_item),
            'allowed_mentions' => array('parse' => array()),
        );

        $boundary = '--------------------------' . wp_generate_password(24, false, false);
        $body = $this->build_multipart_body($boundary, $payload, $filename, $mime, $binary);
        $response = $this->discord_request(
            'POST',
            '/channels/' . rawurlencode($target) . '/messages',
            $account['access_token'],
            $body,
            array(
                'Content-Type' => 'multipart/form-data; boundary=' . $boundary,
            ),
            false
        );
        if (is_wp_error($response)) {
            return $response;
        }

        return array(
            'provider_post_id' => sanitize_text_field($response['id'] ?? ''),
            'message' => 'Published media post to Discord.',
            'response' => $response,
        );
    }

    public function list_replies(array $input) {
        return new WP_Error('provider_not_ready', 'Discord reply ingestion is not normalized into a read API in this build.');
    }

    public function reply_to_comment(array $input) {
        $account = $this->get_publish_account($input);
        if (empty($account['access_token'])) {
            return new WP_Error('missing_connected_account', 'A connected Discord bot is required before replying.');
        }

        $target = sanitize_text_field($input['channel_id'] ?? $this->get_publish_target($account));
        $message = sanitize_textarea_field($input['message'] ?? '');
        $reply_to_message_id = sanitize_text_field($input['comment_id'] ?? $input['message_id'] ?? '');
        if ($target === '' || $message === '' || $reply_to_message_id === '') {
            return new WP_Error('missing_reply_context', 'Discord reply publishing requires a target channel, a message ID, and reply text.');
        }

        $response = $this->discord_request('POST', '/channels/' . rawurlencode($target) . '/messages', $account['access_token'], array(
            'content' => $this->truncate_text($message, 2000),
            'message_reference' => array(
                'message_id' => $reply_to_message_id,
                'channel_id' => $target,
                'fail_if_not_exists' => false,
            ),
            'allowed_mentions' => array('parse' => array()),
        ));
        if (is_wp_error($response)) {
            return $response;
        }

        return array(
            'platform_key' => 'discord_community',
            'provider_reply_id' => sanitize_text_field($response['id'] ?? ''),
            'message' => 'Reply sent to Discord.',
            'response' => $response,
        );
    }

    public function fetch_metrics(array $input) {
        return new WP_Error('provider_not_ready', 'Discord does not expose a normalized campaign metrics model in this build.');
    }

    private function discord_request($method, $path, $bot_token, $body = null, array $headers = array(), $json = true) {
        $request_headers = array_merge(array(
            'Authorization' => 'Bot ' . $bot_token,
            'Accept' => 'application/json',
        ), $headers);

        if ($json && $body !== null) {
            $request_headers['Content-Type'] = 'application/json; charset=UTF-8';
            $body = wp_json_encode($body);
        }

        $response = wp_remote_request('https://discord.com/api/v10' . $path, array(
            'method' => strtoupper($method),
            'timeout' => 60,
            'headers' => $request_headers,
            'body' => $body,
        ));

        if (is_wp_error($response)) {
            return $response;
        }

        $status = (int) wp_remote_retrieve_response_code($response);
        $data = json_decode((string) wp_remote_retrieve_body($response), true);
        if (!is_array($data)) {
            $data = array();
        }

        if ($status >= 400) {
            return new WP_Error(
                'provider_api_error',
                sanitize_text_field($data['message'] ?? 'Discord API request failed.'),
                array(
                    'platform_key' => 'discord_community',
                    'status' => $status,
                    'response' => $data,
                )
            );
        }

        return $data;
    }

    private function get_publish_account(array $input) {
        $account = (array) ($input['connected_account'] ?? array());
        if (!empty($account['access_token'])) {
            return $account;
        }

        $user_id = (int) ($input['user_id'] ?? 0);
        if ($user_id <= 0 || !$this->store || !method_exists($this->store, 'get_connected_account_auth_rows')) {
            return $account;
        }

        $rows = $this->store->get_connected_account_auth_rows($user_id, 'discord_community');
        if (!empty($rows)) {
            return $rows[0];
        }

        return $account;
    }

    private function get_publish_target(array $account) {
        $metadata = (array) ($account['connection_metadata'] ?? array());
        if (!empty($metadata['default_target_id'])) {
            return sanitize_text_field($metadata['default_target_id']);
        }

        $settings = $this->get_settings();
        return sanitize_text_field($settings['default_target_id'] ?? '');
    }

    private function extract_asset(array $content_item) {
        $bundle = (array) ($content_item['media_bundle'] ?? array());
        $assets = array_values((array) ($bundle['manual_assets'] ?? array()));
        if (empty($assets)) {
            return array();
        }

        return (array) $assets[0];
    }

    private function get_caption(array $content_item) {
        $caption = sanitize_textarea_field($content_item['caption_master'] ?? '');
        $cta = esc_url_raw($content_item['cta_url'] ?? '');
        if ($cta !== '' && strpos($caption, $cta) === false) {
            $caption = trim($caption . "\n\n" . $cta);
        }

        if ($caption === '') {
            $caption = sanitize_text_field($content_item['title'] ?? '');
        }

        return $this->truncate_text($caption, 2000);
    }

    private function build_multipart_body($boundary, array $payload, $filename, $mime, $binary) {
        $eol = "\r\n";
        $body = '--' . $boundary . $eol;
        $body .= 'Content-Disposition: form-data; name="payload_json"' . $eol;
        $body .= 'Content-Type: application/json' . $eol . $eol;
        $body .= wp_json_encode($payload) . $eol;
        $body .= '--' . $boundary . $eol;
        $body .= 'Content-Disposition: form-data; name="files[0]"; filename="' . addslashes($filename) . '"' . $eol;
        $body .= 'Content-Type: ' . $mime . $eol . $eol;
        $body .= $binary . $eol;
        $body .= '--' . $boundary . '--' . $eol;

        return $body;
    }

    private function truncate_text($value, $limit) {
        $value = trim((string) $value);
        if ($value === '') {
            return '';
        }

        if (function_exists('mb_strlen') && function_exists('mb_substr')) {
            return mb_strlen($value) > $limit ? rtrim(mb_substr($value, 0, $limit - 1)) : $value;
        }

        return strlen($value) > $limit ? rtrim(substr($value, 0, $limit - 1)) : $value;
    }
}
