<?php

namespace Itmar\SNS\Twitter;

use WP_Error;


class MediaUploader
{
    private $access_token;

    public function __construct($access_token)
    {
        $this->access_token = $access_token;
    }

    public function upload_media_from_url($url, $type = 'image')
    {
        $response = wp_remote_get($url, ['sslverify' => true]);

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

        $body = wp_remote_retrieve_body($response);
        if (empty($body)) {
            return new WP_Error('empty_body', 'メディアデータが取得できませんでした。');
        }

        $tmp_file = wp_tempnam($url);
        if (!$tmp_file) {
            return new WP_Error('tmp_fail', '一時ファイルの作成に失敗しました。');
        }

        file_put_contents($tmp_file, $body);
        $media_id = $this->upload_media_from_file($tmp_file, $type);
        wp_delete_file($tmp_file);

        return $media_id;
    }


    public function upload_media_from_file($file_path, $type = 'image')
    {
        $media_type = ($type === 'video') ? 'video/mp4' : $this->get_mime_type($file_path);
        $media_category = ($type === 'video') ? 'tweet_video' : 'tweet_image';
        $total_bytes = filesize($file_path);

        $media_id = $this->upload_init($media_type, $total_bytes, $media_category);
        if (is_wp_error($media_id)) return $media_id;

        $result = $this->upload_append($media_id, $file_path, $media_type);
        if (is_wp_error($result)) return $result;

        $final = $this->upload_finalize($media_id);
        if (is_wp_error($final)) return $final;

        return $media_id;
    }

    private function upload_init($media_type, $total_bytes, $media_category)
    {
        $endpoint = 'https://api.x.com/2/media/upload/initialize';

        $args = [
            'headers' => [
                'Authorization' => 'Bearer ' . $this->access_token,   // ユーザーコンテキスト + media.write 必須
                'Content-Type'  => 'application/json',
            ],
            'body'    => wp_json_encode([
                'media_type'     => $media_type,      // e.g. "image/jpeg" / "video/mp4"
                'total_bytes'    => (int) $total_bytes,
                'media_category' => $media_category,  // "tweet_image" / "tweet_video" など
                // 'shared' => false, 'additional_owners' => [...] も必要に応じて
            ]),
            'timeout' => 30,
        ];

        $response = wp_remote_post($endpoint, $args);

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

        $body = json_decode(wp_remote_retrieve_body($response), true);
        if (empty($body['data']['id'])) {
            return new WP_Error('init_failed', 'INIT failed: ');
        }

        return $body['data']['id'];
    }


    private function upload_append($media_id, $file_path, $media_type = 'application/octet-stream')
    {
        $endpoint = "https://api.x.com/2/media/upload/{$media_id}/append";

        // --- WP_Filesystem 初期化 ---
        if (! function_exists('WP_Filesystem')) {
            require_once ABSPATH . 'wp-admin/includes/file.php';
        }
        WP_Filesystem();
        global $wp_filesystem;
        if (empty($wp_filesystem)) {
            return new \WP_Error('fs_init_failed', 'ファイルシステムの初期化に失敗しました。');
        }

        // ファイル全体を読み込み（WP_Filesystem）
        $bytes = $wp_filesystem->get_contents($file_path);
        if (false === $bytes) {
            return new \WP_Error('read_file_failed', 'ファイルの読み込みに失敗しました。');
        }

        $chunk_size = 4 * 1024 * 1024; // 4MB
        $length     = strlen($bytes);
        $segment    = 0;

        for ($offset = 0; $offset < $length; $offset += $chunk_size, $segment++) {
            $chunk = substr($bytes, $offset, $chunk_size);
            if ($chunk === '') {
                break; // 空チャンクは送らない
            }

            // --- multipart/form-data を手組み ---
            $boundary = wp_generate_password(24, false, false);
            $eol      = "\r\n";

            $body  = '';
            // 通常フィールド: segment_index
            $body .= '--' . $boundary . $eol;
            $body .= 'Content-Disposition: form-data; name="segment_index"' . $eol . $eol;
            $body .= (string) $segment . $eol;

            // ファイルパート: name="media"
            $filename = 'part' . $segment;
            $body    .= '--' . $boundary . $eol;
            $body    .= 'Content-Disposition: form-data; name="media"; filename="' . $filename . '"' . $eol;
            $body    .= 'Content-Type: ' . $media_type . $eol . $eol;
            $body    .= $chunk . $eol;
            $body    .= '--' . $boundary . '--' . $eol;

            // --- cURL → wp_remote_post に置き換え ---
            $response = wp_remote_post($endpoint, array(
                'timeout' => 60,
                'headers' => array(
                    'Authorization' => 'Bearer ' . $this->access_token,
                    'Content-Type'  => 'multipart/form-data; boundary=' . $boundary,
                ),
                'body'    => $body,
            ));

            if (is_wp_error($response)) {
                return new \WP_Error('append_failed', 'APPEND ' . $segment . ' failed: ' . $response->get_error_message());
            }

            $code = wp_remote_retrieve_response_code($response);
            $resp = wp_remote_retrieve_body($response);

            if ($code < 200 || $code >= 300) {
                return new \WP_Error('append_failed', "APPEND {$segment} failed ({$code}): {$resp}");
            }
        }

        return true;
    }


    private function upload_finalize($media_id, $max_wait_sec = 180)
    {
        // 1) FINALIZE: POST /2/media/upload/{id}/finalize
        $finalize = wp_remote_post("https://api.x.com/2/media/upload/{$media_id}/finalize", [
            'headers' => [
                'Authorization' => 'Bearer ' . $this->access_token,
                // Content-Type は付けない（空ボディでOK）
            ],
            'timeout' => 30,
        ]);
        if (is_wp_error($finalize)) return $finalize;

        $code = wp_remote_retrieve_response_code($finalize);
        $raw  = wp_remote_retrieve_body($finalize);
        $body = json_decode($raw, true);

        if ($code < 200 || $code >= 300) {
            return new WP_Error('finalize_failed', "FINALIZE failed ({$code}): " . $raw);
        }

        //状態取得
        $state = $body['data']['processing_state']
            ?? ($body['data']['processing_info']['state'] ?? 'succeeded');
        $check_after = $processing_info['check_after_secs'] ?? 5;

        // 2) 即完了ならここで終了
        if ($state === 'succeeded') {
            return true;
        } elseif ($state === 'failed') {
            return new WP_Error('media_processing_failed', 'Media processing failed: ' . $raw);
        } else {
            // 3) ジョブ情報を保存（例：オプション or post_meta）
            $job = [
                'media_id'      => $media_id,
                'state'         => $state,
                'last_checked'  => current_time('mysql'),
                'check_after'   => $check_after,
                'started_at'    => current_time('mysql'),
            ];
            update_option("x_upload_job_{$media_id}", $job);

            // 4) すぐにレスポンス返却
            return [
                'success'       => true,
                'media_id'      => $media_id,
                'state'         => $state,
                'next_check_in' => $check_after,
            ];
        }

        // 3) STATUS をポーリング: GET /2/media/upload/{id}
        // STATUS: GET /2/media/upload?media_id=... (&command=STATUS は任意)
        $status_url = add_query_arg(
            [
                'media_id' => $media_id,
                'command'  => 'STATUS', // 付けても付けなくても可
            ],
            'https://api.x.com/2/media/upload'
        );
        $deadline   = time() + (int)$max_wait_sec;
        $delay      = (int)($body['data']['processing_info']['check_after_secs'] ?? 2);
        $delay      = max(1, min(10, $delay));

        while (time() < $deadline) {
            sleep($delay);

            $status = wp_remote_get($status_url, [
                'headers' => ['Authorization' => 'Bearer ' . $this->access_token],
                'timeout' => 20,
            ]);
            if (is_wp_error($status)) return $status;

            $scode = wp_remote_retrieve_response_code($status);
            $sraw  = wp_remote_retrieve_body($status);
            $sbody = json_decode($sraw, true);

            if ($scode < 200 || $scode >= 300) {
                return new WP_Error('status_failed', "STATUS failed ({$scode}): " . $sraw);
            }

            $sbody = json_decode($sraw, true);
            $data  = $sbody['data'] ?? [];
            $info  = $data['processing_info'] ?? [];
            $state = $data['processing_state'] ?? ($info['state'] ?? 'succeeded');

            if ($state === 'succeeded') return true;
            if ($state === 'failed') {
                return new WP_Error('media_processing_failed', 'Media processing failed: ');
            }

            // 次回の待ち時間（サーバー推奨 or 上限10秒）
            $delay = (int)($info['check_after_secs'] ?? $delay);
            $delay = max(1, min(10, $delay));
        }

        return new WP_Error('media_processing_timeout', 'Media processing timed out.');
    }


    private function get_mime_type($file_path)
    {
        if (!function_exists('finfo_open')) return 'image/jpeg';
        $finfo = finfo_open(FILEINFO_MIME_TYPE);
        $type = finfo_file($finfo, $file_path);
        finfo_close($finfo);
        return $type ?: 'image/jpeg';
    }
}
