<?php
namespace BLR\Backline\Core;

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

final class Media {
    private const DEFAULT_ATTACHMENT_QUERY_LIMIT = 500;
    private const MIN_ATTACHMENT_QUERY_LIMIT = 50;
    private const ATTACHMENT_META_MANAGED = '_blr_ba_managed_attachment';
    private const ATTACHMENT_META_OWNER_POST_ID = '_blr_ba_owner_post_id';
    private const ATTACHMENT_META_OWNER_CONTEXT = '_blr_ba_owner_context';

    public static function boot(): void {
    }

    public static function attachment_query_limit(): int {
        $limit = (int) apply_filters('blr_backline_media_picker_limit', self::DEFAULT_ATTACHMENT_QUERY_LIMIT);
        if ($limit < self::MIN_ATTACHMENT_QUERY_LIMIT) {
            $limit = self::MIN_ATTACHMENT_QUERY_LIMIT;
        }

        return $limit;
    }

    /**
     * Build attachment picker queries without exposing the whole site media library
     * to non-admin Backline users. Admins keep normal media-library access; other
     * users only see Backline-managed attachments unless an install explicitly
     * opts into global media selection with the filter below.
     */
    private static function attachment_query_args(string $mime_type): array {
        $args = [
            'post_type'      => 'attachment',
            'post_status'    => 'inherit',
            'post_mime_type' => $mime_type,
            'numberposts'    => self::attachment_query_limit(),
            'orderby'        => 'date',
            'order'          => 'DESC',
            'no_found_rows'  => true,
        ];

        $allow_global = current_user_can('manage_options');
        /**
         * Filters whether Backline media pickers may show global media-library
         * attachments to the current user.
         *
         * @param bool   $allow_global Whether global media-library attachments are allowed.
         * @param string $mime_type    Requested attachment mime group, such as image or audio.
         */
        $allow_global = (bool) apply_filters('blr_backline_allow_global_media_picker_attachments', $allow_global, $mime_type);

        if (!$allow_global) {
            $args['meta_query'] = [
                [
                    'key' => self::ATTACHMENT_META_MANAGED,
                    'value' => '1',
                    'compare' => '=',
                ],
            ];
        }

        return $args;
    }

    /**
     * @param array{include_attachment_id?:int} $args
     * @return list<array{id:int,label:string,thumb:string}>
     */
    public static function options(array $args = []): array {
        $attachments = get_posts(self::attachment_query_args('image'));

        $options = [];
        foreach ($attachments as $attachment) {
            if (!$attachment instanceof \WP_Post) {
                continue;
            }

            $option = self::build_option((int) $attachment->ID);
            if ($option !== null) {
                $options[$option['id']] = $option;
            }
        }

        $include_attachment_id = absint($args['include_attachment_id'] ?? 0);
        if ($include_attachment_id > 0 && !isset($options[$include_attachment_id])) {
            $option = self::build_option($include_attachment_id);
            if ($option !== null) {
                $options[$option['id']] = $option;
            }
        }

        return array_values($options);
    }

    /**
     * @param array{include_attachment_id?:int} $args
     * @return list<array{id:int,label:string,thumb:string,preview:string,mime:string,filename:string}>
     */
    public static function audio_options(array $args = []): array {
        $attachments = get_posts(self::attachment_query_args('audio'));

        $options = [];
        foreach ($attachments as $attachment) {
            if (!$attachment instanceof \WP_Post) {
                continue;
            }

            $option = self::build_audio_option((int) $attachment->ID);
            if ($option !== null) {
                $options[$option['id']] = $option;
            }
        }

        $include_attachment_id = absint($args['include_attachment_id'] ?? 0);
        if ($include_attachment_id > 0 && !isset($options[$include_attachment_id])) {
            $option = self::build_audio_option($include_attachment_id);
            if ($option !== null) {
                $options[$option['id']] = $option;
            }
        }

        return array_values($options);
    }

    public static function render_picker(string $field_name, array $args = []): void {
        $selected = absint($args['selected'] ?? 0);
        $options = isset($args['options']) && is_array($args['options']) ? $args['options'] : self::options([
            'include_attachment_id' => $selected,
        ]);
        $allow_none = !empty($args['allow_none']);
        $none_label = isset($args['none_label']) ? (string) $args['none_label'] : __('No image selected', 'blr-backline');

        echo '<select class="form-select blr-ba-select2" name="' . esc_attr($field_name) . '" data-placeholder="' . esc_attr__('Choose image...', 'blr-backline') . '" data-allow-clear="1">';
        if ($allow_none) {
            echo '<option value="">' . esc_html($none_label) . '</option>';
        }

        foreach ($options as $option) {
            $attachment_id = absint($option['id'] ?? 0);
            if ($attachment_id <= 0 || !self::is_valid_image_attachment_id($attachment_id)) {
                continue;
            }

            $label = (string) ($option['label'] ?? '');
            if ($label === '') {
                $label = (string) get_the_title($attachment_id);
            }

            echo '<option value="' . esc_attr((string) $attachment_id) . '" ' . selected($selected, $attachment_id, false) . '>' . esc_html($label) . '</option>';
        }

        echo '</select>';
    }

    public static function render_visual_picker(string $field_name, array $args = []): void {
        $selected = absint($args['selected'] ?? 0);
        $options = isset($args['options']) && is_array($args['options']) ? $args['options'] : self::options([
            'include_attachment_id' => $selected,
        ]);
        $allow_none = !empty($args['allow_none']);
        $none_label = isset($args['none_label']) ? (string) $args['none_label'] : __('No image selected', 'blr-backline');
        $search_placeholder = isset($args['search_placeholder']) ? (string) $args['search_placeholder'] : __('Search images...', 'blr-backline');
        $preview_target = isset($args['preview_target']) ? (string) $args['preview_target'] : '';
        $placeholder_url = BLR_BA_URL . 'assets/blr-ba/img/placeholder-image.png';

        echo '<div class="blr-ba-visual-picker"' . ($preview_target !== '' ? ' data-preview-target="' . esc_attr($preview_target) . '"' : '') . '>';
        echo '<input type="search" class="form-control mb-3 blr-ba-visual-picker-search" placeholder="' . esc_attr($search_placeholder) . '">';
        echo '<div class="blr-ba-visual-picker-grid">';

        if ($allow_none) {
            echo '<label class="blr-ba-visual-choice" data-search-text="' . esc_attr(strtolower($none_label)) . '" data-preview-url="' . esc_url($placeholder_url) . '" data-preview-label="' . esc_attr($none_label) . '">';
            echo '<input class="blr-ba-visual-choice-input" type="radio" name="' . esc_attr($field_name) . '" value="" ' . checked($selected, 0, false) . '>';
            echo '<span class="blr-ba-visual-choice-card">';
            echo '<span class="blr-ba-visual-choice-media">';
            echo '<img class="blr-ba-visual-choice-image" src="' . esc_url($placeholder_url) . '" alt="' . esc_attr($none_label) . '">';
            echo '</span>';
            echo '<span class="blr-ba-visual-choice-title">' . esc_html($none_label) . '</span>';
            echo '</span>';
            echo '</label>';
        }

        foreach ($options as $option) {
            $attachment_id = absint($option['id'] ?? 0);
            if ($attachment_id <= 0 || !self::is_valid_image_attachment_id($attachment_id)) {
                continue;
            }

            $label = (string) ($option['label'] ?? '');
            if ($label === '') {
                $label = (string) get_the_title($attachment_id);
            }

            $thumb_url = (string) ($option['thumb'] ?? '');
            if ($thumb_url === '') {
                $thumb_url = (string) wp_get_attachment_image_url($attachment_id, 'thumbnail');
            }

            $preview_url = (string) wp_get_attachment_image_url($attachment_id, 'large');
            if ($preview_url === '') {
                $preview_url = $thumb_url;
            }

            echo '<label class="blr-ba-visual-choice" data-search-text="' . esc_attr(strtolower($label)) . '" data-preview-url="' . esc_url($preview_url) . '" data-preview-label="' . esc_attr($label) . '">';
            echo '<input class="blr-ba-visual-choice-input" type="radio" name="' . esc_attr($field_name) . '" value="' . esc_attr((string) $attachment_id) . '" ' . checked($selected, $attachment_id, false) . '>';
            echo '<span class="blr-ba-visual-choice-card">';
            echo '<span class="blr-ba-visual-choice-media">';

            if ($thumb_url !== '') {
                echo '<img class="blr-ba-visual-choice-image" src="' . esc_url($thumb_url) . '" alt="' . esc_attr($label) . '">';
            } else {
                echo '<span class="blr-ba-visual-choice-media blr-ba-visual-choice-media-empty">';
                echo '<span class="blr-ba-visual-choice-empty-label">' . esc_html__('No preview', 'blr-backline') . '</span>';
                echo '</span>';
            }

            echo '</span>';
            echo '<span class="blr-ba-visual-choice-title">' . esc_html($label) . '</span>';
            echo '</span>';
            echo '</label>';
        }

        echo '</div>';
        echo '<div class="blr-ba-visual-picker-empty text-muted mt-2" hidden>' . esc_html__('No matching images found.', 'blr-backline') . '</div>';
        echo '</div>';
    }


    public static function render_audio_visual_picker(string $field_name, array $args = []): void {
        $selected = absint($args['selected'] ?? 0);
        $options = isset($args['options']) && is_array($args['options']) ? $args['options'] : self::audio_options([
            'include_attachment_id' => $selected,
        ]);
        $allow_none = !empty($args['allow_none']);
        $none_label = isset($args['none_label']) ? (string) $args['none_label'] : __('No audio file selected', 'blr-backline');
        $search_placeholder = isset($args['search_placeholder']) ? (string) $args['search_placeholder'] : __('Search audio files...', 'blr-backline');
        $preview_target = isset($args['preview_target']) ? (string) $args['preview_target'] : '';
        $placeholder_url = BLR_BA_URL . 'assets/blr-ba/img/placeholder-image.png';

        echo '<div class="blr-ba-visual-picker" data-preview-kind="audio"' . ($preview_target !== '' ? ' data-preview-target="' . esc_attr($preview_target) . '"' : '') . '>';
        echo '<input type="search" class="form-control mb-3 blr-ba-visual-picker-search" placeholder="' . esc_attr($search_placeholder) . '">';
        echo '<div class="blr-ba-visual-picker-grid">';

        if ($allow_none) {
            echo '<label class="blr-ba-visual-choice" data-search-text="' . esc_attr(strtolower($none_label)) . '" data-preview-kind="audio" data-preview-url="" data-preview-label="' . esc_attr($none_label) . '">';
            echo '<input class="blr-ba-visual-choice-input" type="radio" name="' . esc_attr($field_name) . '" value="" ' . checked($selected, 0, false) . '>';
            echo '<span class="blr-ba-visual-choice-card">';
            echo '<span class="blr-ba-visual-choice-media">';
            echo '<img class="blr-ba-visual-choice-image blr-ba-visual-choice-image--audio" src="' . esc_url($placeholder_url) . '" alt="' . esc_attr($none_label) . '">';
            echo '<span class="blr-ba-visual-choice-badge">' . esc_html__('Audio', 'blr-backline') . '</span>';
            echo '</span>';
            echo '<span class="blr-ba-visual-choice-title">' . esc_html($none_label) . '</span>';
            echo '</span>';
            echo '</label>';
        }

        foreach ($options as $option) {
            $attachment_id = absint($option['id'] ?? 0);
            if ($attachment_id <= 0 || !self::is_valid_audio_attachment_id($attachment_id)) {
                continue;
            }

            $label = (string) ($option['label'] ?? '');
            $filename = (string) ($option['filename'] ?? '');
            $mime_type = (string) ($option['mime'] ?? '');
            $search_text = strtolower(trim($label . ' ' . $filename . ' ' . $mime_type));
            $thumb_url = (string) ($option['thumb'] ?? '');
            $preview_url = (string) ($option['preview'] ?? '');
            if ($preview_url === '') {
                $preview_url = (string) wp_get_attachment_url($attachment_id);
            }

            echo '<label class="blr-ba-visual-choice" data-search-text="' . esc_attr($search_text) . '" data-preview-kind="audio" data-preview-url="' . esc_url($preview_url) . '" data-preview-label="' . esc_attr($label) . '">';
            echo '<input class="blr-ba-visual-choice-input" type="radio" name="' . esc_attr($field_name) . '" value="' . esc_attr((string) $attachment_id) . '" ' . checked($selected, $attachment_id, false) . '>';
            echo '<span class="blr-ba-visual-choice-card">';
            echo '<span class="blr-ba-visual-choice-media">';

            if ($thumb_url !== '') {
                echo '<img class="blr-ba-visual-choice-image blr-ba-visual-choice-image--audio" src="' . esc_url($thumb_url) . '" alt="' . esc_attr($label) . '">';
            } else {
                echo '<span class="blr-ba-visual-choice-media blr-ba-visual-choice-media-empty">';
                echo '<span class="blr-ba-visual-choice-empty-label">' . esc_html__('No preview', 'blr-backline') . '</span>';
                echo '</span>';
            }

            echo '<span class="blr-ba-visual-choice-badge">' . esc_html__('Audio', 'blr-backline') . '</span>';
            echo '</span>';
            echo '<span class="blr-ba-visual-choice-title">' . esc_html($label) . '</span>';

            if ($filename !== '' && $filename !== $label) {
                echo '<span class="blr-ba-visual-choice-subtitle">' . esc_html($filename) . '</span>';
            }

            echo '</span>';
            echo '</label>';
        }

        echo '</div>';
        echo '<div class="blr-ba-visual-picker-empty text-muted mt-2" hidden>' . esc_html__('No matching audio files found.', 'blr-backline') . '</div>';
        echo '</div>';
    }

    public static function preview_markup(int $attachment_id, string $size = 'large'): string {
        $attachment_id = absint($attachment_id);

        if ($attachment_id <= 0 || !self::is_valid_image_attachment_id($attachment_id)) {
            return '<img class="img-fluid rounded border blr-ba-preview-image" src="' . esc_url(BLR_BA_URL . 'assets/blr-ba/img/placeholder-image.png') . '" alt="' . esc_attr__('Placeholder image', 'blr-backline') . '">';
        }

        return (string) wp_get_attachment_image(
            $attachment_id,
            $size,
            false,
            [
                'class' => 'img-fluid rounded border blr-ba-preview-image',
            ]
        );
    }

    public static function audio_preview_markup(int $attachment_id = 0, string $audio_url = '', string $label = ''): string {
        $attachment_id = absint($attachment_id);
        $resolved_url = '';
        $resolved_label = trim($label);

        if ($attachment_id > 0 && self::is_valid_audio_attachment_id($attachment_id)) {
            $resolved_url = (string) wp_get_attachment_url($attachment_id);
            if ($resolved_label === '') {
                $resolved_label = self::attachment_label($attachment_id, __('Audio file', 'blr-backline'));
            }
        } elseif ($audio_url !== '') {
            $resolved_url = esc_url($audio_url);
            if ($resolved_label === '') {
                $resolved_label = __('Current audio file', 'blr-backline');
            }
        }

        if ($resolved_url === '') {
            return '<div class="text-muted">' . esc_html__('No audio file selected.', 'blr-backline') . '</div>';
        }

        return '<div class="blr-ba-audio-preview-card"><div class="blr-ba-audio-preview-title">' . esc_html($resolved_label) . '</div><audio class="w-100" controls preload="none" src="' . esc_url($resolved_url) . '"></audio></div>';
    }

    public static function is_valid_image_attachment_id(int $attachment_id): bool {
        $attachment_id = absint($attachment_id);
        if ($attachment_id <= 0) {
            return false;
        }

        $attachment = get_post($attachment_id);
        return $attachment instanceof \WP_Post
            && $attachment->post_type === 'attachment'
            && wp_attachment_is_image($attachment_id);
    }

    public static function is_valid_audio_attachment_id(int $attachment_id): bool {
        $attachment_id = absint($attachment_id);
        if ($attachment_id <= 0) {
            return false;
        }

        $attachment = get_post($attachment_id);
        if (!$attachment instanceof \WP_Post || $attachment->post_type !== 'attachment') {
            return false;
        }

        $mime_type = (string) get_post_mime_type($attachment_id);
        return strpos($mime_type, 'audio/') === 0;
    }

    public static function current_user_can_use_attachment(int $attachment_id, ?int $post_id = null): bool {
        $attachment_id = absint($attachment_id);
        if ($attachment_id <= 0 || get_post_type($attachment_id) !== 'attachment') {
            return false;
        }

        if (current_user_can('manage_options')) {
            return true;
        }

        if ($post_id !== null && self::is_post_owned_attachment($attachment_id, $post_id)) {
            return true;
        }

        if (self::is_post_owned_attachment($attachment_id)) {
            return true;
        }

        $attachment = get_post($attachment_id);
        if (
            $attachment instanceof \WP_Post
            && (int) $attachment->post_author === get_current_user_id()
            && Access::user_can_access()
        ) {
            return true;
        }

        return current_user_can('edit_post', $attachment_id);
    }

    public static function assign_featured_image_result(int $post_id, int $selected_attachment_id, string $upload_field): array {
        $errors = [];
        $created_attachment_ids = [];
        $post_id = absint($post_id);
        $previous_attachment_id = $post_id > 0 ? absint(get_post_thumbnail_id($post_id)) : 0;

        $selected_attachment_id = absint($selected_attachment_id);
        if (
            $selected_attachment_id > 0
            && (
                !self::is_valid_image_attachment_id($selected_attachment_id)
                || !self::current_user_can_use_attachment($selected_attachment_id, $post_id)
            )
        ) {
            return [
                'attachment_id' => 0,
                'created_attachment_ids' => [],
                'errors' => [__('The selected image is invalid or not available to your Backline account.', 'blr-backline')],
            ];
        }

        $upload = CrudHelpers::handle_upload($upload_field, $post_id, CrudHelpers::image_mimes(), CrudHelpers::visual_max_file_size());
        $upload_errors = CrudHelpers::get_last_upload_errors();
        foreach ($upload_errors as $upload_error) {
            $errors[] = (string) $upload_error;
        }

        if (is_array($upload) && !empty($upload)) {
            $uploaded_attachment_id = absint($upload['attachment_id'] ?? 0);
            if ($uploaded_attachment_id > 0 && self::is_valid_image_attachment_id($uploaded_attachment_id)) {
                $selected_attachment_id = $uploaded_attachment_id;
                $created_attachment_ids[] = $uploaded_attachment_id;
                self::mark_attachment_as_post_owned($uploaded_attachment_id, $post_id, 'featured_image');
            }
        }

        if (!empty($errors)) {
            return [
                'attachment_id' => $selected_attachment_id,
                'created_attachment_ids' => $created_attachment_ids,
                'errors' => array_values(array_unique(array_filter(array_map('strval', $errors), 'strlen'))),
            ];
        }

        if ($selected_attachment_id > 0) {
            set_post_thumbnail($post_id, $selected_attachment_id);
        } else {
            delete_post_thumbnail($post_id);
        }

        if ($previous_attachment_id > 0 && $previous_attachment_id !== $selected_attachment_id) {
            self::cleanup_replaced_post_owned_attachment($post_id, $previous_attachment_id);
        }

        return [
            'attachment_id' => $selected_attachment_id,
            'created_attachment_ids' => $created_attachment_ids,
            'errors' => [],
        ];
    }

    public static function cleanup_created_attachments(array $attachment_ids): void {
        foreach (array_values(array_unique(array_map('absint', $attachment_ids))) as $attachment_id) {
            if ($attachment_id > 0) {
                CrudHelpers::delete_attachment($attachment_id);
            }
        }
    }

    public static function cleanup_post_owned_featured_image(int $post_id): void {
        $post_id = absint($post_id);
        if ($post_id <= 0) {
            return;
        }

        $attachment_id = absint(get_post_thumbnail_id($post_id));
        if ($attachment_id <= 0 || !self::is_post_owned_attachment($attachment_id, $post_id)) {
            return;
        }

        if (self::attachment_is_featured_elsewhere($attachment_id, $post_id)) {
            self::clear_attachment_ownership($attachment_id);
            return;
        }

        CrudHelpers::delete_attachment($attachment_id);
    }

    public static function mark_attachment_as_post_owned(int $attachment_id, int $post_id, string $context = 'featured_image'): void {
        $attachment_id = absint($attachment_id);
        $post_id = absint($post_id);

        if ($attachment_id <= 0 || $post_id <= 0) {
            return;
        }

        update_post_meta($attachment_id, self::ATTACHMENT_META_MANAGED, '1');
        update_post_meta($attachment_id, self::ATTACHMENT_META_OWNER_POST_ID, $post_id);
        update_post_meta($attachment_id, self::ATTACHMENT_META_OWNER_CONTEXT, sanitize_key($context));
    }

    public static function is_post_owned_attachment(int $attachment_id, ?int $post_id = null): bool {
        $attachment_id = absint($attachment_id);
        if ($attachment_id <= 0) {
            return false;
        }

        $managed = (string) get_post_meta($attachment_id, self::ATTACHMENT_META_MANAGED, true) === '1';
        if (!$managed) {
            return false;
        }

        if ($post_id === null) {
            return true;
        }

        return absint(get_post_meta($attachment_id, self::ATTACHMENT_META_OWNER_POST_ID, true)) === absint($post_id);
    }

    private static function build_option(int $attachment_id): ?array {
        $attachment_id = absint($attachment_id);
        if (
            $attachment_id <= 0
            || !self::is_valid_image_attachment_id($attachment_id)
            || !self::current_user_can_use_attachment($attachment_id)
        ) {
            return null;
        }

        return [
            'id'    => $attachment_id,
            'label' => self::attachment_label($attachment_id, sprintf(
                /* translators: %d: attachment ID. */
                __('Image %d', 'blr-backline'),
                $attachment_id
            )),
            'thumb' => (string) wp_get_attachment_image_url($attachment_id, 'thumbnail'),
        ];
    }

    private static function build_audio_option(int $attachment_id): ?array {
        $attachment_id = absint($attachment_id);
        if (
            $attachment_id <= 0
            || !self::is_valid_audio_attachment_id($attachment_id)
            || !self::current_user_can_use_attachment($attachment_id)
        ) {
            return null;
        }

        $label = self::attachment_label($attachment_id, sprintf(
            /* translators: %d: attachment ID. */
            __('Audio %d', 'blr-backline'),
            $attachment_id
        ));
        $filename = wp_basename((string) get_attached_file($attachment_id));

        return [
            'id'       => $attachment_id,
            'label'    => $label,
            'thumb'    => (string) wp_mime_type_icon($attachment_id),
            'preview'  => (string) wp_get_attachment_url($attachment_id),
            'mime'     => (string) get_post_mime_type($attachment_id),
            'filename' => $filename,
        ];
    }

    private static function attachment_label(int $attachment_id, string $fallback): string {
        $title = trim((string) get_the_title($attachment_id));
        if ($title !== '') {
            return $title;
        }

        $file = get_attached_file($attachment_id);
        if (is_string($file) && $file !== '') {
            return wp_basename($file);
        }

        return $fallback;
    }

    private static function cleanup_replaced_post_owned_attachment(int $post_id, int $attachment_id): void {
        $post_id = absint($post_id);
        $attachment_id = absint($attachment_id);
        if ($post_id <= 0 || $attachment_id <= 0 || !self::is_post_owned_attachment($attachment_id, $post_id)) {
            return;
        }

        if (self::attachment_is_featured_elsewhere($attachment_id, $post_id)) {
            self::clear_attachment_ownership($attachment_id);
            return;
        }

        CrudHelpers::delete_attachment($attachment_id);
    }

    private static function attachment_is_featured_elsewhere(int $attachment_id, int $excluded_post_id): bool {
        $attachment_id = absint($attachment_id);
        $excluded_post_id = absint($excluded_post_id);
        if ($attachment_id <= 0) {
            return false;
        }

        $post_ids = get_posts([
            'post_type' => 'any',
            'post_status' => ['publish', 'draft', 'pending', 'future', 'private', 'trash'],
            'fields' => 'ids',
            'numberposts' => 2,
            'post__not_in' => $excluded_post_id > 0 ? [$excluded_post_id] : [],
            'no_found_rows' => true,
            'meta_query' => [[
                'key' => '_thumbnail_id',
                'value' => (string) $attachment_id,
                'compare' => '=',
            ]],
        ]);

        return !empty($post_ids);
    }

    private static function clear_attachment_ownership(int $attachment_id): void {
        $attachment_id = absint($attachment_id);
        if ($attachment_id <= 0) {
            return;
        }

        delete_post_meta($attachment_id, self::ATTACHMENT_META_MANAGED);
        delete_post_meta($attachment_id, self::ATTACHMENT_META_OWNER_POST_ID);
        delete_post_meta($attachment_id, self::ATTACHMENT_META_OWNER_CONTEXT);
    }
}
