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

class Calliope_SP_Pinterest {

    private $api_base = 'https://api.pinterest.com/v5';

    public function __construct() {
        $env = (string) get_option('calliope_sp_pinterest_env', 'production');
        if ($env === 'sandbox') {
            $this->api_base = 'https://api-sandbox.pinterest.com/v5';
        } else {
            $this->api_base = 'https://api.pinterest.com/v5';
        }
    }

    /**
     * Basic request wrapper with logging.
     *
     * @param string $method
     * @param string $endpoint e.g. '/pins'
     * @param string $token
     * @param array|null $body
     * @return array|WP_Error { code:int, body_raw:string, body_json:array|null }
     */
    private function request($method, $endpoint, $token, $body = null) {
        $url = rtrim($this->api_base, '/') . '/' . ltrim($endpoint, '/');

        $args = [
            'method'  => strtoupper($method),
            'headers' => [
                'Authorization' => 'Bearer ' . $token,
                'Content-Type'  => 'application/json',
            ],
            'timeout' => 60,
        ];

        if ($body !== null) {
            $args['body'] = wp_json_encode($body);
        }

        $res = wp_remote_request($url, $args);

        if (is_wp_error($res)) {
            if (class_exists('Calliope_SP_Logger')) {
                Calliope_SP_Logger::log('pinterest', 'error', 'HTTP WP_Error: ' . $res->get_error_message());
            }
            return $res;
        }

        $code = (int) wp_remote_retrieve_response_code($res);
        $raw  = (string) wp_remote_retrieve_body($res);

        $json = null;
        $decoded = json_decode($raw, true);
        if (is_array($decoded)) {
            $json = $decoded;
        }

        if (class_exists('Calliope_SP_Logger')) {
            $lvl = ($code >= 200 && $code < 300) ? 'info' : 'error';
            $msg = 'HTTP ' . $code . ' | ' . mb_substr($raw, 0, 1200);
            Calliope_SP_Logger::log('pinterest', $lvl, $msg);
        }

        return [
            'code'     => $code,
            'body_raw' => $raw,
            'body_json'=> $json,
        ];
    }

    /**
     * Convert a base64 (data URL or raw b64) image into a public URL on this site.
     * Pinterest 'image_url' media source requires a publicly accessible URL.
     *
     * @param string $b64
     * @return string|WP_Error
     */
    private function image_url_from_b64($b64) {
        $asset = class_exists('Calliope_SP_Media_Helper')
            ? Calliope_SP_Media_Helper::create_public_image_from_b64($b64, [
                'context'         => 'pinterest',
                'filename_prefix' => 'calliope-social-publisher-pinterest',
                'network'         => 'pinterest',
                'max_attempts'    => 6,
            ])
            : new WP_Error('upload_failed', 'Pinterest could not prepare a public image URL.');

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

        return esc_url_raw((string) $asset['url']);
    }


    /**
     * List boards for the operation user (based on token scopes).
     *
     * @param string $token
     * @return array|WP_Error Array of boards: [ ['id'=>..,'name'=>..,'privacy'=>..], ... ]
     */
    public function list_boards($token) {
        $resp = $this->request('GET', '/boards?page_size=50', $token, null);
        if (is_wp_error($resp)) return $resp;

        if ($resp['code'] < 200 || $resp['code'] >= 300) {
            $msg = $resp['body_json']['message'] ?? $resp['body_raw'] ?? 'Desconocido';
            return new WP_Error('pinterest', 'Could not list boards (' . $resp['code'] . '): ' . $msg);
        }

        $items = $resp['body_json']['items'] ?? [];
        if (!is_array($items)) $items = [];

        $boards = [];
        foreach ($items as $b) {
            if (!is_array($b)) continue;
            $id = $b['id'] ?? '';
            if (!$id) continue;
            $boards[] = [
                'id'      => (string) $id,
                'name'    => (string) ($b['name'] ?? ''),
                'privacy' => (string) ($b['privacy'] ?? ''),
            ];
        }

        return $boards;
    }

    /**
     * If Board ID is missing, try to auto-detect it. If the account has exactly 1 board,
     * we auto-save it to options. Otherwise, we store the last fetched list for the admin UI.
     *
     * @param string $token
     * @return string|WP_Error
     */
    private function ensure_board_id($token) {
        $board_id = (string) get_option('calliope_sp_pinterest_board_id');
        if ($board_id) return $board_id;

        $boards = $this->list_boards($token);
        if (is_wp_error($boards)) return $boards;

        // Save list for UI help (even if empty)
        update_option('calliope_sp_pinterest_boards_last', $boards);

        if (count($boards) === 1) {
            $id = (string) ($boards[0]['id'] ?? '');
            if ($id) {
                update_option('calliope_sp_pinterest_board_id', sanitize_text_field($id));
                return $id;
            }
        }

        if (empty($boards)) {
            return new WP_Error('cfg', 'No boards were found. Create at least 1 Pinterest board and try again.');
        }

        return new WP_Error('cfg', 'Missing Board ID. You have ' . count($boards) . ' boards: copy the ID of one (it is listed below in Connections → Pinterest) and paste it into Board ID.');
    }

    /**
     * Admin helper: detect board and save it if possible.
     *
     * @return string|WP_Error
     */
    public function detect_board_id() {
        $token = (string) get_option('calliope_sp_pinterest_token');
        if (!$token) return new WP_Error('cfg', 'Missing token.');
        return $this->ensure_board_id($token);
    }



    /**
     * Simple connection check: verifies we can read the configured board.
     */
    public function test_connection() {
    $token = (string) get_option('calliope_sp_pinterest_token');
    $board_id = (string) get_option('calliope_sp_pinterest_board_id');

    if (!$token) {
        return new WP_Error('cfg', 'Missing Pinterest configuration (token).');
    }

    // 1) Validate token first (it does not depend on Board ID)
    $resp = $this->request('GET', '/user_account', $token, null);
    if (is_wp_error($resp)) return $resp;

    if ($resp['code'] < 200 || $resp['code'] >= 300) {
        $msg = $resp['body_json']['message'] ?? $resp['body_raw'] ?? 'Desconocido';
        if ((int)$resp['code'] === 401) {
            $hint = ' (401): Authentication failed. If your app is on Trial access, Pinterest often returns 401 when you use Production (api.pinterest.com). Change the Environment to Sandbox (api-sandbox.pinterest.com) and generate the token in Sandbox, or request a Standard upgrade to publish in production. Also check that the token is not expired/revoked.';
            return new WP_Error('pinterest', 'Error' . $hint);
        }
        return new WP_Error('pinterest', 'Error (' . $resp['code'] . '): ' . $msg);
    }

    // 2) If a Board ID exists, validate it as well
    if ($board_id) {
        $resp2 = $this->request('GET', '/boards/' . rawurlencode($board_id), $token, null);
        if (is_wp_error($resp2)) return $resp2;

        if ($resp2['code'] < 200 || $resp2['code'] >= 300) {
            $msg = $resp2['body_json']['message'] ?? $resp2['body_raw'] ?? 'Desconocido';
            return new WP_Error('pinterest', 'Invalid Board ID or insufficient permissions (' . $resp2['code'] . '): ' . $msg);
        }
    }

    return true;
}

    public function publish($data) {
        $token = (string) get_option('calliope_sp_pinterest_token');
        if (!$token) {
            return new WP_Error('cfg', 'Missing Pinterest configuration (token).');
        }

        $board_id = $this->ensure_board_id($token);
        if (is_wp_error($board_id)) {
            return $board_id;
        }

$title = mb_substr((string)($data['title'] ?? ''), 0, 100);
        $desc  = mb_substr(trim((string)($data['content'] ?? '') . "\n\n" . (string)($data['hashtags'] ?? '')), 0, 500);
        $link  = !empty($data['link']) ? esc_url_raw($data['link']) : null;
        $alt   = !empty($data['alt_text']) ? mb_substr((string)$data['alt_text'], 0, 500) : mb_substr($title, 0, 500);

        // Pinterest v5 Create Pin supports image_url media source (Pinterest fetches the image from a public URL).
        $image_url = '';
        if (!empty($data['image_url'])) {
            $image_url = esc_url_raw($data['image_url']);
        } elseif (!empty($data['image_b64'])) {
            $img_res = $this->image_url_from_b64($data['image_b64']);
            if (is_wp_error($img_res)) return $img_res;
            $image_url = $img_res;
        }

        if (empty($image_url)) {
            return new WP_Error('no_image_url', 'Pinterest requires an image.');
        }

        $payload = [
            'title'        => $title,
            'description'  => $desc,
            'board_id'     => $board_id,
            'alt_text'     => $alt,
            'media_source' => [
                'source_type' => 'image_url',
                'url'         => $image_url,
            ],
        ];

        // 'link' is optional in Pinterest API
        if (!empty($link)) {
            $payload['link'] = $link;
        }

        $resp = $this->request('POST', '/pins', $token, $payload);
        if (is_wp_error($resp)) return $resp;

        // Pinterest Create Pin typically returns 201, but accept any 2xx
        if ($resp['code'] < 200 || $resp['code'] >= 300) {
            $msg = $resp['body_json']['message'] ?? $resp['body_raw'] ?? 'Desconocido';
            if (
                (int) $resp['code'] === 429 &&
                stripos((string) $msg, 'blocked this link because it may lead to spam') !== false
            ) {
                return new WP_Error(
                    'pinterest_link_blocked',
                    'Pinterest rejected the link due to anti-spam protection.'
                );
            }
            if ((int)$resp['code'] === 401) {
                $hint = ' (401): Authentication failed. If your app is on Trial access, Pinterest often returns 401 when you use Production (api.pinterest.com). Change the Environment to Sandbox (api-sandbox.pinterest.com) and generate the token in Sandbox, or request a Standard upgrade to publish in production. Also check that the token is not expired/revoked.';
                return new WP_Error('pinterest', 'Error' . $hint);
            }
            return new WP_Error('pinterest', 'Error (' . $resp['code'] . '): ' . $msg);
        }

        return true;
    }
}
