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

abstract class VMO_Promotion_OAuth2_Connector extends VMO_Promotion_Configured_Connector {
    protected $store;
    protected $oauth_state_ttl = 1200;

    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) {
        $base = parent::connect_account($input);
        $settings = $this->get_settings();
        $missing = $this->get_missing_required_fields($settings);
        $user_id = (int) ($input['user_id'] ?? 0);

        if ($user_id <= 0) {
            return new WP_Error('missing_user', 'A valid user is required before starting provider authorization.');
        }

        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,
                )
            );
        }

        $scopes = $this->get_requested_scopes($settings);
        $state = $this->create_oauth_state($user_id, array(
            'platform_key' => $this->platform_key,
            'scopes' => $scopes,
        ));

        $base['requires_redirect'] = true;
        $base['scopes'] = $scopes;
        $base['auth_url'] = $this->build_authorization_url($settings, $scopes, $state);
        $base['message'] = sprintf('Redirect the user to %s to complete authorization.', $this->platform_label);

        if ($this->store) {
            $this->store->log_event(
                $user_id,
                'provider_authorization_started',
                sprintf('OAuth authorization started for %s.', $this->platform_label),
                array(
                    'platform_key' => $this->platform_key,
                    'scopes' => $scopes,
                )
            );
        }

        return $base;
    }

    public function handle_callback(array $request) {
        $state = sanitize_text_field($request['state'] ?? '');
        if ($state === '') {
            return new WP_Error('missing_state', 'Provider callback is missing the OAuth state token.');
        }

        $state_payload = $this->consume_oauth_state($state);
        if (empty($state_payload['user_id'])) {
            return new WP_Error('invalid_state', 'Provider callback state has expired or is invalid.');
        }

        $user_id = (int) $state_payload['user_id'];
        if (!empty($request['error'])) {
            $message = sanitize_text_field($request['error_description'] ?? $request['error']);
            if ($this->store) {
                $this->store->log_event(
                    $user_id,
                    'provider_authorization_failed',
                    sprintf('OAuth authorization failed for %s.', $this->platform_label),
                    array(
                        'platform_key' => $this->platform_key,
                        'error' => sanitize_key($request['error']),
                        'message' => $message,
                    )
                );
            }

            return new WP_Error('provider_authorization_failed', $message ?: 'Provider authorization failed.');
        }

        $code = sanitize_text_field($request['code'] ?? '');
        if ($code === '') {
            return new WP_Error('missing_code', 'Provider callback did not include an authorization code.');
        }

        $token_context = $this->exchange_authorization_code($code, $state_payload);
        if (is_wp_error($token_context)) {
            return $token_context;
        }

        $accounts = $this->sync_remote_accounts($user_id, $token_context, array(
            'oauth_state' => $state_payload,
        ));
        if (is_wp_error($accounts)) {
            return $accounts;
        }

        if (empty($accounts)) {
            $fallback = $this->build_fallback_connected_account($user_id, $token_context);
            if (!empty($fallback) && $this->store) {
                $stored = $this->store->upsert_connected_account($user_id, $fallback);
                if (!empty($stored)) {
                    $accounts = array($stored);
                }
            }
        }

        if ($this->store) {
            $this->store->log_event(
                $user_id,
                'provider_authorization_completed',
                sprintf('OAuth authorization completed for %s.', $this->platform_label),
                array(
                    'platform_key' => $this->platform_key,
                    'connected_accounts' => count($accounts),
                )
            );
        }

        return array(
            'success' => true,
            'platform_key' => $this->platform_key,
            'platform_label' => $this->platform_label,
            'user_id' => $user_id,
            'connected_accounts' => $accounts,
            'connected_accounts_count' => count($accounts),
            'message' => sprintf('%s authorization completed.', $this->platform_label),
        );
    }

    public function refresh_auth(array $input) {
        $user_id = (int) ($input['user_id'] ?? 0);
        if ($user_id <= 0) {
            return new WP_Error('missing_user', 'A valid user is required before refreshing provider authorization.');
        }

        $auth_row = $this->get_preferred_auth_row($user_id);
        if (empty($auth_row)) {
            return $this->connect_account($input);
        }

        $refresh_token = (string) ($auth_row['refresh_token'] ?? '');
        if ($refresh_token === '') {
            return $this->connect_account($input);
        }

        $settings = $this->get_settings();
        $response = $this->request_token($this->build_refresh_request_body($refresh_token, $settings));
        if (is_wp_error($response)) {
            return $response;
        }

        $token_context = array_merge($auth_row, $response);
        $accounts = $this->sync_remote_accounts($user_id, $token_context, array(
            'refresh' => true,
        ));
        if (is_wp_error($accounts)) {
            return $accounts;
        }

        return array(
            'success' => true,
            'platform_key' => $this->platform_key,
            'connected_accounts' => $accounts,
            'message' => sprintf('%s authorization refreshed.', $this->platform_label),
        );
    }

    public function list_accounts(array $input) {
        $user_id = (int) ($input['user_id'] ?? 0);
        if ($user_id <= 0) {
            return parent::list_accounts($input);
        }

        $auth_row = $this->get_preferred_auth_row($user_id);
        if (empty($auth_row['access_token'])) {
            return parent::list_accounts($input);
        }

        $accounts = $this->sync_remote_accounts($user_id, $auth_row, array(
            'sync_only' => true,
        ));
        if (is_wp_error($accounts)) {
            return $accounts;
        }

        return array(
            'platform_key' => $this->platform_key,
            'accounts' => $accounts,
        );
    }

    protected function sync_remote_accounts($user_id, array $token_context, array $request_context = array()) {
        if (!$this->store) {
            return array();
        }

        $accounts = $this->discover_remote_accounts($user_id, $token_context, $request_context);
        if (is_wp_error($accounts)) {
            return $accounts;
        }

        $stored = array();
        foreach ((array) $accounts as $account) {
            if (empty($account['platform_key'])) {
                $account['platform_key'] = $this->platform_key;
            }
            if (empty($account['platform_label'])) {
                $account['platform_label'] = $this->platform_label;
            }
            if (!isset($account['granted_scopes'])) {
                $account['granted_scopes'] = (array) ($token_context['granted_scopes'] ?? array());
            }
            if (!isset($account['access_token'])) {
                $account['access_token'] = (string) ($token_context['access_token'] ?? '');
            }
            if (!isset($account['refresh_token'])) {
                $account['refresh_token'] = (string) ($token_context['refresh_token'] ?? '');
            }
            if (!isset($account['token_expires_at'])) {
                $account['token_expires_at'] = sanitize_text_field($token_context['token_expires_at'] ?? '');
            }
            if (!isset($account['refresh_status'])) {
                $account['refresh_status'] = !empty($account['refresh_token']) ? 'available' : 'reconnect_required';
            }
            if (!isset($account['app_review_status'])) {
                $account['app_review_status'] = sanitize_key($this->get_settings()['app_review_status'] ?? 'approved');
            }
            if (!array_key_exists('publish_enabled', $account)) {
                $account['publish_enabled'] = !empty($this->get_settings()['publish_enabled']);
            }
            if (!array_key_exists('reply_enabled', $account)) {
                $account['reply_enabled'] = !empty($this->get_settings()['reply_enabled']);
            }
            if (!array_key_exists('metrics_enabled', $account)) {
                $account['metrics_enabled'] = !empty($this->get_settings()['metrics_enabled']);
            }

            $saved = $this->store->upsert_connected_account($user_id, $account);
            if (!empty($saved)) {
                $stored[] = $saved;
            }
        }

        return $stored;
    }

    protected function get_requested_scopes(array $settings) {
        $scopes = array_values(array_filter(array_map('sanitize_text_field', (array) ($settings['configured_scopes'] ?? array()))));
        if (empty($scopes)) {
            $scopes = array_values(array_filter(array_map('sanitize_text_field', (array) ($this->provider_profile['suggested_scopes'] ?? array()))));
        }

        return array_values(array_unique(array_filter($scopes)));
    }

    protected function build_refresh_request_body($refresh_token, array $settings) {
        return array(
            'grant_type' => 'refresh_token',
            'refresh_token' => (string) $refresh_token,
            'client_id' => $this->get_client_identifier($settings),
            'client_secret' => $this->get_client_secret($settings),
        );
    }

    protected function exchange_authorization_code($code, array $state_payload) {
        $settings = $this->get_settings();
        $response = $this->request_token($this->build_token_request_body($code, $state_payload, $settings));
        if (is_wp_error($response)) {
            if ($this->store) {
                $this->store->log_event(
                    (int) ($state_payload['user_id'] ?? 0),
                    'provider_token_exchange_failed',
                    sprintf('Token exchange failed for %s.', $this->platform_label),
                    array(
                        'platform_key' => $this->platform_key,
                        'error' => $response->get_error_code(),
                        'message' => $response->get_error_message(),
                    )
                );
            }
            return $response;
        }

        return $response;
    }

    protected function build_token_request_body($code, array $state_payload, array $settings) {
        return array(
            'grant_type' => 'authorization_code',
            'code' => (string) $code,
            'redirect_uri' => $this->get_callback_url(),
            'client_id' => $this->get_client_identifier($settings),
            'client_secret' => $this->get_client_secret($settings),
        );
    }

    protected function request_token(array $body) {
        $response = wp_remote_post($this->get_token_endpoint(), array(
            'timeout' => 20,
            'headers' => array(
                'Accept' => 'application/json',
                'Content-Type' => 'application/x-www-form-urlencoded',
            ),
            'body' => $body,
        ));

        return $this->parse_remote_response($response, true);
    }

    protected function api_get($url, $access_token, array $query = array(), array $headers = array()) {
        if (!empty($query)) {
            $url = add_query_arg($query, $url);
        }

        $headers = array_merge($headers, array(
            'Accept' => 'application/json',
            'Authorization' => 'Bearer ' . $access_token,
        ));

        return $this->parse_remote_response(wp_remote_get($url, array(
            'timeout' => 20,
            'headers' => $headers,
        )));
    }

    protected function api_post($url, $access_token, array $body = array(), array $headers = array(), $json = true) {
        $headers = array_merge($headers, array(
            'Accept' => 'application/json',
            'Authorization' => 'Bearer ' . $access_token,
        ));

        if ($json) {
            $headers['Content-Type'] = 'application/json';
            $body = wp_json_encode($body);
        }

        return $this->parse_remote_response(wp_remote_post($url, array(
            'timeout' => 30,
            'headers' => $headers,
            'body' => $body,
        )));
    }

    protected function api_put($url, $access_token, $body, array $headers = array()) {
        $headers = array_merge($headers, array(
            'Accept' => 'application/json',
            'Authorization' => 'Bearer ' . $access_token,
        ));

        return $this->parse_remote_response(wp_remote_request($url, array(
            'method' => 'PUT',
            'timeout' => 60,
            'headers' => $headers,
            'body' => $body,
        )));
    }

    protected function parse_remote_response($response, $token_response = false) {
        if (is_wp_error($response)) {
            return $response;
        }

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

        if ($status >= 400) {
            $error_message = '';
            if (!empty($body['error_description'])) {
                $error_message = sanitize_text_field($body['error_description']);
            } elseif (!empty($body['error']['message'])) {
                $error_message = sanitize_text_field($body['error']['message']);
            } elseif (!empty($body['message'])) {
                $error_message = sanitize_text_field($body['message']);
            } else {
                $error_message = 'The provider API request failed.';
            }

            $error_code = 'provider_api_error';
            if (!empty($body['error'])) {
                if (is_string($body['error'])) {
                    $error_code = sanitize_key($body['error']);
                } elseif (!empty($body['error']['code'])) {
                    $error_code = sanitize_key($body['error']['code']);
                }
            }

            return new WP_Error($error_code ?: 'provider_api_error', $error_message, array(
                'platform_key' => $this->platform_key,
                'status' => $status,
                'response' => $body,
                'headers' => $headers,
            ));
        }

        if ($token_response) {
            $expires_in = max(0, (int) ($body['expires_in'] ?? 0));
            return array(
                'access_token' => sanitize_text_field($body['access_token'] ?? ''),
                'refresh_token' => sanitize_text_field($body['refresh_token'] ?? ''),
                'token_type' => sanitize_text_field($body['token_type'] ?? ''),
                'expires_in' => $expires_in,
                'token_expires_at' => $expires_in > 0 ? gmdate('Y-m-d H:i:s', time() + $expires_in) : '',
                'granted_scopes' => $this->normalize_scope_value($body['scope'] ?? ($body['scopes'] ?? array())),
                'raw_response' => $body,
                'response_headers' => $headers,
            );
        }

        $body['_response_headers'] = $headers;
        $body['_status'] = $status;

        return $body;
    }

    protected function normalize_scope_value($scope_value) {
        if (is_string($scope_value)) {
            $parts = preg_split('/[\s,]+/', $scope_value);
            return array_values(array_filter(array_map('sanitize_text_field', (array) $parts)));
        }

        return array_values(array_filter(array_map('sanitize_text_field', (array) $scope_value)));
    }

    protected function create_oauth_state($user_id, array $payload = array()) {
        $state = wp_generate_password(40, false, false);
        set_transient($this->oauth_state_key($state), array_merge($payload, array(
            'user_id' => (int) $user_id,
            'created_at' => time(),
        )), $this->oauth_state_ttl);

        return $state;
    }

    protected function consume_oauth_state($state) {
        $key = $this->oauth_state_key($state);
        $payload = get_transient($key);
        delete_transient($key);

        return is_array($payload) ? $payload : array();
    }

    protected function oauth_state_key($state) {
        return 'vmo_promo_oauth_' . sanitize_key($this->platform_key) . '_' . md5((string) $state);
    }

    protected function get_client_identifier(array $settings) {
        if (!empty($settings['client_id'])) {
            return sanitize_text_field($settings['client_id']);
        }

        return sanitize_text_field($settings['app_id'] ?? '');
    }

    protected function get_client_secret(array $settings) {
        return sanitize_text_field($settings['client_secret'] ?? '');
    }

    protected function get_preferred_auth_row($user_id) {
        if (!$this->store || !method_exists($this->store, 'get_connected_account_auth_rows')) {
            return array();
        }

        $rows = $this->store->get_connected_account_auth_rows($user_id, $this->platform_key);
        if (empty($rows)) {
            return array();
        }

        foreach ($rows as $row) {
            $metadata = (array) ($row['connection_metadata'] ?? array());
            if (($metadata['connection_role'] ?? '') === 'oauth_user' && !empty($row['access_token'])) {
                return $row;
            }
        }

        foreach ($rows as $row) {
            if (!empty($row['access_token'])) {
                return $row;
            }
        }

        return array();
    }

    protected function build_fallback_connected_account($user_id, array $token_context) {
        return array(
            'platform_key' => $this->platform_key,
            'platform_label' => $this->platform_label,
            'external_account_id' => $this->platform_key . '_oauth_' . md5((string) $user_id . '|' . (string) ($token_context['access_token'] ?? '')),
            'display_name' => $this->platform_label . ' Connected Account',
            'auth_status' => 'connected',
            'granted_scopes' => (array) ($token_context['granted_scopes'] ?? array()),
            'access_token' => (string) ($token_context['access_token'] ?? ''),
            'refresh_token' => (string) ($token_context['refresh_token'] ?? ''),
            'token_expires_at' => sanitize_text_field($token_context['token_expires_at'] ?? ''),
            'refresh_status' => !empty($token_context['refresh_token']) ? 'available' : 'reconnect_required',
            'app_review_status' => sanitize_key($this->get_settings()['app_review_status'] ?? 'approved'),
            'publish_enabled' => !empty($this->get_settings()['publish_enabled']),
            'reply_enabled' => !empty($this->get_settings()['reply_enabled']),
            'metrics_enabled' => !empty($this->get_settings()['metrics_enabled']),
            'connection_metadata' => array(
                'connection_role' => 'oauth_user',
            ),
        );
    }

    protected function get_primary_publish_account(array $input) {
        $account = (array) ($input['connected_account'] ?? array());
        if (!empty($account['id'])) {
            return $account;
        }

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

        $accounts = $this->store->get_connected_accounts($user_id);
        foreach ($accounts as $candidate) {
            if (($candidate['platform_key'] ?? '') === $this->platform_key && !empty($candidate['publish_enabled'])) {
                return $candidate;
            }
        }

        return array();
    }

    protected function get_platform_account_record(array $input) {
        if (!$this->store || !method_exists($this->store, 'get_connected_account_auth_rows')) {
            return array();
        }

        $user_id = (int) ($input['user_id'] ?? 0);
        if ($user_id <= 0) {
            return array();
        }

        $target = (array) ($input['connected_account'] ?? array());
        $rows = $this->store->get_connected_account_auth_rows($user_id, $this->platform_key);
        if (empty($rows)) {
            return array();
        }

        foreach ($rows as $row) {
            if (!empty($target['id']) && (int) $row['id'] === (int) $target['id']) {
                return $row;
            }
            if (!empty($target['external_account_id']) && (string) $row['external_account_id'] === (string) $target['external_account_id']) {
                return $row;
            }
        }

        foreach ($rows as $row) {
            if (!empty($row['publish_enabled'])) {
                return $row;
            }
        }

        return $rows[0];
    }

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

        $asset = (array) $assets[0];
        return array(
            'id' => sanitize_text_field($asset['id'] ?? ''),
            'name' => sanitize_text_field($asset['name'] ?? ''),
            'url' => esc_url_raw($asset['url'] ?? ''),
            'kind' => sanitize_key($asset['kind'] ?? 'asset'),
            'type' => sanitize_text_field($asset['type'] ?? ''),
        );
    }

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

        return trim($caption);
    }

    abstract protected function build_authorization_url(array $settings, array $scopes, $state);
    abstract protected function get_token_endpoint();
    abstract protected function discover_remote_accounts($user_id, array $token_context, array $request_context = array());
}
