<?php
/**
 * Plugin Name: Fe Adulta — API subir audio TTS
 * Description: Endpoint REST acotado para publicar el MP3 TTS de un artículo.
 * Version: 1.0
 *
 * POST /wp-json/fea/v1/subir-audio
 * multipart/form-data: post_id=<id>, audio=<MP3>, voice_id=<opcional>
 *
 * Ver issue gitea.feadulta.com/rafa/feadulta#222.
 */

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

const FEA_AUDIO_MAX_BYTES = 26214400; // 25 MiB: límite operativo de producción.

add_action('rest_api_init', function () {
    register_rest_route('fea/v1', '/subir-audio', [
        'methods'             => WP_REST_Server::CREATABLE,
        'callback'            => 'fea_subir_audio_handle',
        'permission_callback' => 'fea_subir_audio_can_call',
        // La validación de post_id ocurre en el handler, después de autorizar:
        // una llamada anónima siempre recibe 401 aunque omita parámetros.
        'args'                => [
            'post_id' => [
                'sanitize_callback' => 'absint',
            ],
            'voice_id' => [
                'sanitize_callback' => 'sanitize_text_field',
            ],
        ],
    ]);
});

/** Editor o superior: mismo nivel que los endpoints de escritura existentes. */
function fea_subir_audio_can_call(WP_REST_Request $request) {
    if (!is_user_logged_in()) {
        return new WP_Error(
            'fea_subir_audio_not_authenticated',
            'Debes autenticarte para subir un audio.',
            ['status' => 401]
        );
    }
    if (!current_user_can('edit_others_posts')) {
        return new WP_Error(
            'fea_subir_audio_forbidden',
            'No tienes permiso para subir audios.',
            ['status' => 403]
        );
    }
    return true;
}

function fea_subir_audio_handle(WP_REST_Request $request) {
    $post_id = absint($request->get_param('post_id'));
    if (!$post_id) {
        return new WP_Error(
            'fea_subir_audio_invalid_post',
            'post_id es obligatorio.',
            ['status' => 400]
        );
    }
    if (!get_post($post_id)) {
        return new WP_Error(
            'fea_subir_audio_post_not_found',
            'No existe el artículo indicado.',
            ['status' => 404]
        );
    }

    $files = $request->get_file_params();
    $file = $files['audio'] ?? null;
    $validation = fea_subir_audio_validate_file($file);
    if (is_wp_error($validation)) return $validation;

    $previous = [
        'url'    => get_post_meta($post_id, 'fea_audio_url', true) ?: null,
        'voice'  => get_post_meta($post_id, 'fea_audio_voice', true) ?: null,
        'done'   => get_post_meta($post_id, 'fea_audio_done', true) ?: null,
        'sha256' => get_post_meta($post_id, 'fea_audio_sha256', true) ?: null,
        'error'  => get_post_meta($post_id, 'fea_audio_error', true) ?: null,
    ];

    require_once ABSPATH . 'wp-admin/includes/file.php';
    $upload_dir_filter = 'fea_subir_audio_upload_dir';
    add_filter('upload_dir', $upload_dir_filter);
    try {
        $uploaded = wp_handle_upload($file, [
            'test_form'                => false,
            'mimes'                    => ['mp3' => 'audio/mpeg'],
            'unique_filename_callback' => function ($dir, $name, $ext) use ($post_id) {
                return $post_id . '.mp3';
            },
        ]);
    } finally {
        remove_filter('upload_dir', $upload_dir_filter);
    }
    if (!empty($uploaded['error']) || empty($uploaded['file'])) {
        return new WP_Error(
            'fea_subir_audio_upload_failed',
            'No se pudo guardar el audio: ' . ($uploaded['error'] ?? 'error desconocido'),
            ['status' => 400]
        );
    }

    $sha256 = hash_file('sha256', $uploaded['file']);
    if ($sha256 === false) {
        wp_delete_file($uploaded['file']);
        return new WP_Error(
            'fea_subir_audio_hash_failed',
            'No se pudo calcular la huella del audio subido.',
            ['status' => 500]
        );
    }

    // El path es estable para que los players y el proceso de backfill no dependan
    // de las carpetas por mes que normalmente crea WordPress.
    $audio_url = home_url('/wp-content/uploads/tts/' . $post_id . '.mp3');
    update_post_meta($post_id, 'fea_audio_url', $audio_url);
    update_post_meta($post_id, 'fea_audio_voice', (string) $request->get_param('voice_id'));
    update_post_meta($post_id, 'fea_audio_done', '1');
    update_post_meta($post_id, 'fea_audio_sha256', $sha256);
    delete_post_meta($post_id, 'fea_audio_error');

    return new WP_REST_Response([
        'post_id'        => $post_id,
        'audio_url'      => $audio_url,
        'voice_id'       => (string) $request->get_param('voice_id'),
        'sha256'         => $sha256,
        'previous_audio' => $previous,
    ], 201);
}

/** Guarda el binario en uploads/tts sin cambiar globalmente la configuración. */
function fea_subir_audio_upload_dir($uploads) {
    $uploads['subdir'] = '/tts';
    $uploads['path'] = $uploads['basedir'] . $uploads['subdir'];
    $uploads['url'] = $uploads['baseurl'] . $uploads['subdir'];
    if (!wp_mkdir_p($uploads['path'])) {
        $uploads['error'] = 'No se pudo crear el directorio de audios.';
    }
    return $uploads;
}

function fea_subir_audio_validate_file($file) {
    if (!is_array($file) || empty($file['tmp_name']) || !isset($file['error'])) {
        return new WP_Error(
            'fea_subir_audio_missing_file',
            'audio es obligatorio.',
            ['status' => 400]
        );
    }
    if ((int) $file['error'] !== UPLOAD_ERR_OK) {
        return new WP_Error(
            'fea_subir_audio_file_error',
            'La subida de audio falló.',
            ['status' => 400]
        );
    }
    if ((int) $file['size'] <= 0 || (int) $file['size'] > FEA_AUDIO_MAX_BYTES) {
        return new WP_Error(
            'fea_subir_audio_file_too_large',
            'El audio debe ocupar entre 1 byte y 25 MiB.',
            ['status' => 400]
        );
    }

    $type = wp_check_filetype((string) ($file['name'] ?? ''), ['mp3' => 'audio/mpeg']);
    if (($type['ext'] ?? '') !== 'mp3' || ($type['type'] ?? '') !== 'audio/mpeg') {
        return new WP_Error(
            'fea_subir_audio_unsupported_type',
            'Solo se admiten archivos MP3 (audio/mpeg).',
            ['status' => 400]
        );
    }
    if (strtolower((string) ($file['type'] ?? '')) !== 'audio/mpeg') {
        return new WP_Error(
            'fea_subir_audio_unsupported_type',
            'audio debe declararse como audio/mpeg.',
            ['status' => 400]
        );
    }

    if (function_exists('finfo_open')) {
        $finfo = finfo_open(FILEINFO_MIME_TYPE);
        $mime = $finfo ? finfo_file($finfo, $file['tmp_name']) : false;
        if ($finfo) finfo_close($finfo);
        if ($mime !== false && $mime !== 'audio/mpeg') {
            return new WP_Error(
                'fea_subir_audio_invalid_audio',
                'audio debe contener datos MPEG válidos.',
                ['status' => 400]
            );
        }
    }
    return true;
}
