<?php
namespace BLR\Backline\Modules\Finances;

use BLR\Backline\Core\Access;
use BLR\Backline\Core\CrudHelpers;
use BLR\Backline\Core\Media;
use BLR\Backline\Core\Money;
use BLR\Backline\Core\Router;
use BLR\Backline\Core\Sanitizer;

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

final class Module {
    public const POST_TYPE = 'blr_finance_record';
    public const ATTACHMENT_META_KEY = 'blr_finance_attachment_id';

    private const OWNED_ATTACHMENT_POST_META = '_blr_ba_owner_post_id';
    private const OWNED_ATTACHMENT_CONTEXT_META = '_blr_ba_owner_context';
    private const OWNED_ATTACHMENT_CONTEXT = 'finance_attachment';

    public static function register_feature_only(): void {
        add_action('init', [self::class, 'register_cpt']);
        add_action('init', [self::class, 'register_meta'], 11);
        add_action('blr_backline_register_routes', [self::class, 'register_routes']);
        add_filter('blr_backline_post_type_caps', [self::class, 'filter_post_type_caps']);
        add_filter('blr_backline_dashboard_summary_cards', [self::class, 'register_dashboard_summary_card'], 10, 3);
        add_action('blr_backline_dashboard_after_summary_cards', [self::class, 'render_dashboard_panel'], 10, 2);
        add_action('before_delete_post', [self::class, 'handle_before_delete_post']);
    }

    public static function register_routes(): void {
        Router::register('finances', [Pages::class, 'render_list'], ['title' => __('Finances', 'blr-backline')]);
        Router::register('finances/view', [Pages::class, 'render_view'], ['title' => __('Finance Record', 'blr-backline')]);
        Router::register('finances/new', [Pages::class, 'render_form'], ['title' => __('New Finance Record', 'blr-backline')]);
        Router::register('finances/edit', [Pages::class, 'render_form'], ['title' => __('Edit Finance Record', 'blr-backline')]);
        Router::register('finances/delete', [Pages::class, 'handle_delete'], ['title' => __('Delete Finance Record', 'blr-backline')]);
    }

    public static function filter_post_type_caps(array $caps): array {
        $caps[self::POST_TYPE] = [
            'singular' => 'blr_finance_record',
            'plural'   => 'blr_finance_records',
        ];

        return $caps;
    }

    public static function register_nav_item(array $items, string $base, string $slug): array {
        unset($slug);

        $items[] = [
            'label'    => __('Finances', 'blr-backline'),
            'url'      => $base . 'finances/',
            'icon'     => 'dollar-sign',
            'route'    => 'finances',
            'priority' => 75,
        ];

        return $items;
    }

    public static function register_dashboard_summary_card(array $cards, array $dashboard_data, array $ctx): array {
        unset($dashboard_data, $ctx);

        $base = Router::base_url();
        if (substr($base, -1) !== '/') {
            $base .= '/';
        }

        $cards[] = [
            'count'        => self::finance_count(),
            'label'        => __('Finances', 'blr-backline'),
            'icon'         => 'dollar-sign',
            'archive_url'  => $base . 'finances/',
            'create_url'   => $base . 'finances/new/',
            'create_text'  => __('New Record', 'blr-backline'),
            'column_class' => 'col-12 col-md-6 col-xl-4 col-xxl-3',
            'priority'     => 75,
        ];

        return $cards;
    }

    public static function render_dashboard_panel(array $dashboard_data, array $ctx): void {
        unset($dashboard_data, $ctx);

        $totals = self::month_totals();
        $current_balance = self::current_balance();
        $watchlist = self::watchlist_invoices(5);
        $base = Router::base_url();
        if (substr($base, -1) !== '/') {
            $base .= '/';
        }
        $balance_value = (float) $current_balance;
        $balance_class = 'text-body';

        if ($balance_value > 0) {
            $balance_class = 'text-success';
        } elseif ($balance_value < 0) {
            $balance_class = 'text-danger';
        }
        ?>
        <div class="row g-3 mb-0">
            <div class="col-12">
                <div class="card shadow-sm border-0">
                    <div class="card-header d-flex flex-column flex-md-row align-items-md-center justify-content-between gap-2">
                        <div>
                            <h5 class="card-title mb-0"><?php echo esc_html__('Finances', 'blr-backline'); ?></h5>
                        </div>
                        <div class="d-flex flex-wrap gap-2">
                            <a class="btn btn-sm btn-outline-secondary" href="<?php echo esc_url($base . 'finances/'); ?>"><?php echo esc_html__('View finances', 'blr-backline'); ?></a>
                            <a class="btn btn-sm btn-outline-secondary" href="<?php echo esc_url($base . 'finances/new/'); ?>"><?php echo esc_html__('New finance record', 'blr-backline'); ?></a>
                        </div>
                    </div>
                    <div class="card-body">
                        <div class="row row-cols-1 row-cols-md-2 row-cols-xl-5 g-3 mb-4">
                            <div class="col">
                                <div class="border rounded p-3 h-100">
                                    <div class="small text-uppercase text-muted fw-semibold mb-2"><?php echo esc_html__('Current Balance', 'blr-backline'); ?></div>
                                    <div class="h2 lh-1 mb-2 <?php echo esc_attr($balance_class); ?>"><?php echo esc_html('$' . number_format_i18n((float) $current_balance, 2)); ?></div>
                                    <div class="small text-muted"><?php echo esc_html__('Settled income and invoices minus settled expenses.', 'blr-backline'); ?></div>
                                </div>
                            </div>
                            <div class="col">
                                <div class="border rounded p-3 h-100">
                                    <div class="small text-uppercase text-muted fw-semibold mb-2"><?php echo esc_html__('Income This Month', 'blr-backline'); ?></div>
                                    <div class="h2 lh-1 mb-2"><?php echo esc_html('$' . number_format_i18n((float) $totals['income'], 2)); ?></div>
                                    <div class="small text-muted"><?php echo esc_html__('Paid income recorded this month.', 'blr-backline'); ?></div>
                                </div>
                            </div>
                            <div class="col">
                                <div class="border rounded p-3 h-100">
                                    <div class="small text-uppercase text-muted fw-semibold mb-2"><?php echo esc_html__('Expenses This Month', 'blr-backline'); ?></div>
                                    <div class="h2 lh-1 mb-2"><?php echo esc_html('$' . number_format_i18n((float) $totals['expense'], 2)); ?></div>
                                    <div class="small text-muted"><?php echo esc_html__('Paid expenses recorded this month.', 'blr-backline'); ?></div>
                                </div>
                            </div>
                            <div class="col">
                                <div class="border rounded p-3 h-100">
                                    <div class="small text-uppercase text-muted fw-semibold mb-2"><?php echo esc_html__('Outstanding Invoices', 'blr-backline'); ?></div>
                                    <div class="h2 lh-1 mb-2"><?php echo esc_html('$' . number_format_i18n((float) $totals['outstanding_invoices'], 2)); ?></div>
                                    <div class="small text-muted"><?php echo esc_html__('Pending, partial, or overdue invoices.', 'blr-backline'); ?></div>
                                </div>
                            </div>
                            <div class="col">
                                <div class="border rounded p-3 h-100">
                                    <div class="small text-uppercase text-muted fw-semibold mb-2"><?php echo esc_html__('Overdue Invoices', 'blr-backline'); ?></div>
                                    <div class="h2 lh-1 mb-2"><?php echo esc_html(number_format_i18n(self::overdue_invoices_count())); ?></div>
                                    <div class="small text-muted"><?php echo esc_html__('Invoices past due and still not done.', 'blr-backline'); ?></div>
                                </div>
                            </div>
                        </div>

                        <div class="d-flex align-items-center justify-content-between mb-3">
                            <h6 class="mb-0"><?php echo esc_html__('Invoice Watchlist', 'blr-backline'); ?></h6>
                            <a class="small text-decoration-none" href="<?php echo esc_url($base . 'finances/'); ?>"><?php echo esc_html__('View all records', 'blr-backline'); ?></a>
                        </div>

                        <?php if (empty($watchlist)) : ?>
                            <div class="text-muted"><?php echo esc_html__('No pending invoices right now.', 'blr-backline'); ?></div>
                        <?php else : ?>
                            <div class="list-group list-group-flush">
                                <?php foreach ($watchlist as $record) : ?>
                                    <?php
                                    $record_id = (int) $record->ID;
                                    $due_date = (string) get_post_meta($record_id, 'blr_finance_due_date', true);
                                    $status = (string) get_post_meta($record_id, 'blr_finance_status', true);
                                    $amount = (string) get_post_meta($record_id, 'blr_finance_amount', true);
                                    $currency = (string) get_post_meta($record_id, 'blr_finance_currency', true);
                                    ?>
                                    <a class="list-group-item list-group-item-action py-2 px-3" href="<?php echo esc_url($base . 'finances/view/' . $record_id . '/'); ?>">
                                        <div class="d-flex align-items-start justify-content-between gap-3">
                                            <div>
                                                <div class="fw-semibold"><?php echo esc_html(get_the_title($record)); ?></div>
                                                <div class="small text-muted mb-1"><?php echo esc_html(self::contact_label((int) get_post_meta($record_id, 'blr_finance_contact_id', true)) ?: __('No contact linked', 'blr-backline')); ?></div>
                                                <div class="small text-muted"><?php echo esc_html__('Due', 'blr-backline'); ?> <?php echo esc_html($due_date !== '' ? $due_date : '—'); ?></div>
                                            </div>
                                            <div class="d-flex flex-column align-items-end gap-2 mt-2">
                                                <span><?php echo wp_kses_post(self::badge($status)); ?></span>
                                                <span class="small fw-semibold"><?php echo esc_html(Money::money_label($amount, $currency)); ?></span>
                                            </div>
                                        </div>
                                    </a>
                                <?php endforeach; ?>
                            </div>
                        <?php endif; ?>
                    </div>
                </div>
            </div>
        </div>
        <?php
    }

    public static function register_cpt(): void {
        register_post_type(self::POST_TYPE, array_merge([
            'labels' => [
                'name'          => __('Finances', 'blr-backline'),
                'singular_name' => __('Finance Record', 'blr-backline'),
            ],
            'public'       => false,
            'show_ui'      => false,
            'show_in_menu' => false,
            'show_in_rest' => false,
            'supports'     => ['title', 'editor', 'revisions', 'author'],
            'menu_icon'    => 'dashicons-chart-line',
        ], Access::post_type_registration_args(self::POST_TYPE)));
    }

    public static function register_meta(): void {
        self::register_meta_key('blr_finance_kind', 'string');
        self::register_meta_key('blr_finance_status', 'string');
        self::register_meta_key('blr_finance_amount', 'string');
        self::register_meta_key('blr_finance_currency', 'string');
        self::register_meta_key('blr_finance_date', 'string');
        self::register_meta_key('blr_finance_due_date', 'string');
        self::register_meta_key('blr_finance_category', 'string');
        self::register_meta_key('blr_finance_reference', 'string');
        self::register_meta_key('blr_finance_payment_method', 'string');
        self::register_meta_key('blr_finance_contact_id', 'integer');
        self::register_meta_key(self::ATTACHMENT_META_KEY, 'integer');
    }

    private static function register_meta_key(string $key, string $type): void {
        register_post_meta(self::POST_TYPE, $key, [
            'type'              => $type,
            'single'            => true,
            'show_in_rest'      => false,
            'sanitize_callback' => static function ($value) use ($key, $type) {
                if ($key === 'blr_finance_kind') {
                    return self::normalize_kind((string) $value);
                }

                if ($key === 'blr_finance_status') {
                    return self::normalize_status((string) $value);
                }

                if ($key === 'blr_finance_amount') {
                    return Money::normalize_amount($value);
                }

                if ($key === 'blr_finance_currency') {
                    $currency = strtoupper(sanitize_text_field((string) $value));
                    return preg_match('/^[A-Z]{3}$/', $currency) ? $currency : 'USD';
                }

                if (in_array($key, ['blr_finance_date', 'blr_finance_due_date'], true)) {
                    return Sanitizer::date_ymd($value);
                }

                if ($key === 'blr_finance_contact_id') {
                    return absint($value);
                }

                if ($key === self::ATTACHMENT_META_KEY) {
                    return self::validate_attachment_id(absint($value));
                }

                return Sanitizer::meta_value($key, $value, $type);
            },
            'auth_callback'     => Access::rest_meta_auth_callback(self::POST_TYPE),
        ]);
    }

    public static function kinds(): array {
        return ['Income', 'Expense', 'Invoice'];
    }

    public static function statuses(): array {
        return ['Draft', 'Pending', 'Paid', 'Partial', 'Overdue', 'Void'];
    }

    public static function normalize_kind(string $value): string {
        $value = strtolower(trim(sanitize_text_field($value)));
        $map = [
            'income'  => 'Income',
            'expense' => 'Expense',
            'invoice' => 'Invoice',
        ];

        return $map[$value] ?? '';
    }

    public static function normalize_status(string $value): string {
        $value = strtolower(trim(sanitize_text_field($value)));
        $map = [
            'draft'   => 'Draft',
            'pending' => 'Pending',
            'paid'    => 'Paid',
            'partial' => 'Partial',
            'overdue' => 'Overdue',
            'void'    => 'Void',
        ];

        return $map[$value] ?? '';
    }

    public static function kind_label(string $value): string {
        switch ($value) {
            case 'Income':
                return __('Income', 'blr-backline');
            case 'Expense':
                return __('Expense', 'blr-backline');
            case 'Invoice':
                return __('Invoice', 'blr-backline');
            default:
                return $value;
        }
    }

    public static function status_label(string $value): string {
        switch ($value) {
            case 'Draft':
                return __('Draft', 'blr-backline');
            case 'Pending':
                return __('Pending', 'blr-backline');
            case 'Paid':
                return __('Paid', 'blr-backline');
            case 'Partial':
                return __('Partial', 'blr-backline');
            case 'Overdue':
                return __('Overdue', 'blr-backline');
            case 'Void':
                return __('Void', 'blr-backline');
            default:
                return $value;
        }
    }

    public static function badge(string $value, string $context = 'status'): string {
        $map = $context === 'kind'
            ? [
                'Income'  => 'success',
                'Expense' => 'danger',
                'Invoice' => 'primary',
            ]
            : [
                'Draft'   => 'secondary',
                'Pending' => 'warning',
                'Paid'    => 'success',
                'Partial' => 'info',
                'Overdue' => 'danger',
                'Void'    => 'dark',
            ];

        $class = $map[$value] ?? 'secondary';
        $label = $context === 'kind' ? self::kind_label($value) : self::status_label($value);
        if ($label === '') {
            $label = __('—', 'blr-backline');
        }

        return '<span class="badge bg-' . esc_attr($class) . '">' . esc_html($label) . '</span>';
    }





    public static function finance_count(): int {
        $count = wp_count_posts(self::POST_TYPE);
        return (int) (($count->publish ?? 0) + ($count->draft ?? 0));
    }

    
    public static function sum_amount(array $meta_query = [], array $post_status = ['publish', 'draft']): float {
        $statuses = array_values(array_filter(array_map('sanitize_key', $post_status)));
        if (empty($statuses)) {
            return 0.0;
        }

        $query_args = [
            'post_type'              => self::POST_TYPE,
            'post_status'            => $statuses,
            'posts_per_page'         => 5000,
            'fields'                 => 'ids',
            'no_found_rows'          => true,
            'orderby'                => 'ID',
            'order'                  => 'DESC',
            'update_post_meta_cache' => true,
            'update_post_term_cache' => false,
        ];

        if (!empty($meta_query)) {
            $query_args['meta_query'] = $meta_query;
        }

        $ids = get_posts($query_args);
        if (empty($ids)) {
            return 0.0;
        }

        $total = 0.0;

        foreach ($ids as $id) {
            $total += (float) get_post_meta((int) $id, 'blr_finance_amount', true);
        }

        return $total;
    }

    public static function current_balance(): float {
        return self::sum_amount([
            [
                'key'     => 'blr_finance_kind',
                'value'   => ['Income', 'Invoice'],
                'compare' => 'IN',
            ],
            [
                'key'     => 'blr_finance_status',
                'value'   => ['Paid', 'Partial'],
                'compare' => 'IN',
            ],
        ]) - self::sum_amount([
            [
                'key'   => 'blr_finance_kind',
                'value' => 'Expense',
            ],
            [
                'key'     => 'blr_finance_status',
                'value'   => ['Paid', 'Partial'],
                'compare' => 'IN',
            ],
        ]);
    }

    public static function month_totals(): array {
        $start = current_time('Y-m-01');
        $end = current_time('Y-m-t');

        return [
            'income' => self::sum_amount([
                [
                    'key'   => 'blr_finance_kind',
                    'value' => 'Income',
                ],
                [
                    'key'     => 'blr_finance_date',
                    'value'   => [$start, $end],
                    'compare' => 'BETWEEN',
                    'type'    => 'DATE',
                ],
                [
                    'key'     => 'blr_finance_status',
                    'value'   => ['Paid', 'Partial'],
                    'compare' => 'IN',
                ],
            ]),
            'expense' => self::sum_amount([
                [
                    'key'   => 'blr_finance_kind',
                    'value' => 'Expense',
                ],
                [
                    'key'     => 'blr_finance_date',
                    'value'   => [$start, $end],
                    'compare' => 'BETWEEN',
                    'type'    => 'DATE',
                ],
                [
                    'key'     => 'blr_finance_status',
                    'value'   => ['Paid', 'Partial'],
                    'compare' => 'IN',
                ],
            ]),
            'outstanding_invoices' => self::sum_amount([
                [
                    'key'   => 'blr_finance_kind',
                    'value' => 'Invoice',
                ],
                [
                    'key'     => 'blr_finance_status',
                    'value'   => ['Pending', 'Partial', 'Overdue'],
                    'compare' => 'IN',
                ],
            ]),
        ];
    }

    public static function overdue_invoices_count(): int {
        $query = new \WP_Query([
            'post_type'      => self::POST_TYPE,
            'post_status'    => ['publish', 'draft'],
            'posts_per_page' => 1,
            'fields'         => 'ids',
            'meta_query'     => [
                [
                    'key'   => 'blr_finance_kind',
                    'value' => 'Invoice',
                ],
                [
                    'key'     => 'blr_finance_status',
                    'value'   => ['Pending', 'Partial', 'Overdue'],
                    'compare' => 'IN',
                ],
                [
                    'key'     => 'blr_finance_due_date',
                    'value'   => current_time('Y-m-d'),
                    'compare' => '<',
                    'type'    => 'DATE',
                ],
            ],
        ]);

        return (int) $query->found_posts;
    }

    public static function recent_records(int $limit = 6): array {
        return get_posts([
            'post_type'        => self::POST_TYPE,
            'post_status'      => ['publish', 'draft'],
            'numberposts'      => $limit,
            'meta_key'         => 'blr_finance_date',
            'orderby'          => ['meta_value' => 'DESC', 'date' => 'DESC'],
            'meta_type'        => 'DATE',
            'suppress_filters' => false,
        ]);
    }

    public static function watchlist_invoices(int $limit = 5): array {
        return get_posts([
            'post_type'        => self::POST_TYPE,
            'post_status'      => ['publish', 'draft'],
            'numberposts'      => $limit,
            'meta_key'         => 'blr_finance_due_date',
            'orderby'          => ['meta_value' => 'ASC', 'date' => 'DESC'],
            'meta_type'        => 'DATE',
            'meta_query'       => [
                [
                    'key'   => 'blr_finance_kind',
                    'value' => 'Invoice',
                ],
                [
                    'key'     => 'blr_finance_status',
                    'value'   => ['Pending', 'Partial', 'Overdue'],
                    'compare' => 'IN',
                ],
            ],
            'suppress_filters' => false,
        ]);
    }

    public static function contact_options(int $selected_contact_id = 0, int $limit = 500): array {
        $selected_contact_id = absint($selected_contact_id);
        $limit = max(25, min(1000, absint($limit)));

        $contacts = get_posts([
            'post_type'        => 'blr_contact',
            'post_status'      => ['publish', 'draft'],
            'posts_per_page'   => $limit,
            'orderby'          => 'title',
            'order'            => 'ASC',
            'no_found_rows'    => true,
            'suppress_filters' => false,
        ]);

        $options = [];
        foreach ($contacts as $contact) {
            if ($contact instanceof \WP_Post) {
                $options[(int) $contact->ID] = get_the_title($contact);
            }
        }

        if ($selected_contact_id > 0 && !isset($options[$selected_contact_id])) {
            $selected_contact = get_post($selected_contact_id);
            if ($selected_contact instanceof \WP_Post && $selected_contact->post_type === 'blr_contact') {
                $options[$selected_contact_id] = get_the_title($selected_contact);
            }
        }

        natcasesort($options);
        return $options;
    }

    public static function contact_label(int $contact_id): string {
        if ($contact_id <= 0) {
            return '';
        }

        $post = get_post($contact_id);
        if (!$post instanceof \WP_Post || $post->post_type !== 'blr_contact') {
            return '';
        }

        return get_the_title($post);
    }

    public static function attachment_upload_mimes(): array {
        return array_merge(
            CrudHelpers::image_mimes(),
            [
                'pdf' => 'application/pdf',
            ]
        );
    }

    public static function attachment_options(int $limit = 150, int $include_attachment_id = 0): array {
        $query_args = [
            'post_type'      => 'attachment',
            'post_status'    => 'inherit',
            'numberposts'    => $limit,
            'orderby'        => 'date',
            'order'          => 'DESC',
            'post_mime_type' => ['image', 'application/pdf'],
            'no_found_rows'  => true,
        ];

        $allow_global = (bool) apply_filters('blr_backline_allow_global_finance_attachment_picker', current_user_can('manage_options'));
        if (!$allow_global) {
            $query_args['meta_query'] = [
                [
                    'key' => self::OWNED_ATTACHMENT_POST_META,
                    'compare' => 'EXISTS',
                ],
                [
                    'key' => self::OWNED_ATTACHMENT_CONTEXT_META,
                    'value' => self::OWNED_ATTACHMENT_CONTEXT,
                    'compare' => '=',
                ],
            ];
        }

        $attachments = get_posts($query_args);

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

            $attachment_id = self::validate_attachment_id((int) $attachment->ID);
            if ($attachment_id <= 0) {
                continue;
            }

            $data = self::attachment_data($attachment_id);
            if (!$data) {
                continue;
            }

            $option_map[$attachment_id] = [
                'id'          => $attachment_id,
                'label'       => self::attachment_label($data),
                'filename'    => (string) ($data['filename'] ?? ''),
                'url'         => (string) ($data['url'] ?? ''),
                'mime_type'   => (string) ($data['mime_type'] ?? ''),
                'is_image'    => !empty($data['is_image']),
                'thumb_url'   => (string) ($data['thumb_url'] ?? ''),
                'preview_url' => (string) ($data['preview_url'] ?? ''),
            ];
        }

        $include_attachment_id = self::validate_attachment_id($include_attachment_id);
        if ($include_attachment_id > 0 && !isset($option_map[$include_attachment_id])) {
            $data = self::attachment_data($include_attachment_id);
            if ($data) {
                $option_map[$include_attachment_id] = [
                    'id'          => $include_attachment_id,
                    'label'       => self::attachment_label($data),
                    'filename'    => (string) ($data['filename'] ?? ''),
                    'url'         => (string) ($data['url'] ?? ''),
                    'mime_type'   => (string) ($data['mime_type'] ?? ''),
                    'is_image'    => !empty($data['is_image']),
                    'thumb_url'   => (string) ($data['thumb_url'] ?? ''),
                    'preview_url' => (string) ($data['preview_url'] ?? ''),
                ];
            }
        }

        return array_values($option_map);
    }

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

        echo '<div class="blr-ba-visual-picker">';
        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-file-url="" data-file-label="' . esc_attr($none_label) . '" data-file-name="" data-file-mime="" data-is-image="0" data-thumb-url="" data-preview-url="">';
            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">';

            if ($placeholder_url !== '') {
                echo '<img class="blr-ba-visual-choice-image" src="' . esc_url($placeholder_url) . '" alt="' . esc_attr($none_label) . '">';
            } else {
                echo '<span class="blr-ba-visual-choice-media blr-ba-visual-choice-media-empty"><span class="blr-ba-visual-choice-empty-label">' . esc_html($none_label) . '</span></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) {
                continue;
            }

            $label = (string) ($option['label'] ?? '');
            $filename = (string) ($option['filename'] ?? '');
            $mime_type = (string) ($option['mime_type'] ?? '');
            $is_image = !empty($option['is_image']);
            $preview_url = (string) ($option['preview_url'] ?? '');
            $search_text = strtolower(trim($label . ' ' . $filename . ' ' . $mime_type));

            echo '<label class="blr-ba-visual-choice" data-search-text="' . esc_attr($search_text) . '" data-file-url="' . esc_url((string) ($option['url'] ?? '')) . '" data-file-label="' . esc_attr($label) . '" data-file-name="' . esc_attr($filename) . '" data-file-mime="' . esc_attr($mime_type) . '" data-is-image="' . ($is_image ? '1' : '0') . '" data-thumb-url="' . esc_url((string) ($option['thumb_url'] ?? '')) . '" data-preview-url="' . esc_url($preview_url) . '">';
            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 ($preview_url !== '') {
                echo '<img class="blr-ba-visual-choice-image" src="' . esc_url($preview_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>';
            }

            if (!$is_image) {
                echo '<span class="blr-ba-visual-choice-badge">' . esc_html__('PDF', '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 attachments found.', 'blr-backline') . '</div>';
        echo '</div>';
    }

    public static function validate_attachment_id(int $attachment_id): int {
        if ($attachment_id <= 0) {
            return 0;
        }

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

        $mime_type = (string) get_post_mime_type($attachment_id);
        if ($mime_type === '') {
            return 0;
        }

        if ((strpos($mime_type, 'image/') === 0 || $mime_type === 'application/pdf') && Media::current_user_can_use_attachment($attachment_id)) {
            return $attachment_id;
        }

        return 0;
    }

    public static function attachment_data(int $attachment_id): array {
        $attachment_id = self::validate_attachment_id($attachment_id);
        if ($attachment_id <= 0) {
            return [];
        }

        $url = wp_get_attachment_url($attachment_id);
        if (!$url) {
            return [];
        }

        $mime_type = (string) get_post_mime_type($attachment_id);
        $is_image = strpos($mime_type, 'image/') === 0;
        $thumb_url = '';
        $preview_url = '';

        if ($is_image) {
            $thumb_url = (string) (wp_get_attachment_image_url($attachment_id, 'medium') ?: wp_get_attachment_image_url($attachment_id, 'thumbnail'));
            $preview_url = (string) (wp_get_attachment_image_url($attachment_id, 'large') ?: $thumb_url);
        } else {
            $preview_url = (string) wp_mime_type_icon($attachment_id);
        }

        return [
            'id'          => $attachment_id,
            'url'         => $url,
            'title'       => get_the_title($attachment_id),
            'filename'    => wp_basename((string) get_attached_file($attachment_id)),
            'mime_type'   => $mime_type,
            'is_image'    => $is_image,
            'thumb_url'   => $thumb_url,
            'preview_url' => $preview_url,
        ];
    }

    public static function attachment_label(array $attachment): string {
        $title = trim((string) ($attachment['title'] ?? ''));
        if ($title !== '') {
            return $title;
        }

        $filename = trim((string) ($attachment['filename'] ?? ''));
        if ($filename !== '') {
            return $filename;
        }

        return __('Attachment', 'blr-backline');
    }

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

        $post = get_post($post_id);
        if (!$post instanceof \WP_Post || $post->post_type !== self::POST_TYPE) {
            return;
        }

        $attachment_id = absint(get_post_meta($post_id, self::ATTACHMENT_META_KEY, true));
        if (self::attachment_owned_by_post($attachment_id, $post_id)) {
            CrudHelpers::delete_attachment($attachment_id);
        }
    }

    public static function cleanup_replaced_owned_attachment(int $post_id, int $previous_attachment_id, int $new_attachment_id): void {
        $post_id = absint($post_id);
        $previous_attachment_id = absint($previous_attachment_id);
        $new_attachment_id = absint($new_attachment_id);

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

        if (self::attachment_owned_by_post($previous_attachment_id, $post_id)) {
            CrudHelpers::delete_attachment($previous_attachment_id);
        }
    }

    public static function mark_uploaded_attachment_owned(int $post_id, int $attachment_id): void {
        $post_id = absint($post_id);
        $attachment_id = self::validate_attachment_id($attachment_id);
        if ($post_id <= 0 || $attachment_id <= 0) {
            return;
        }

        Media::mark_attachment_as_post_owned($attachment_id, $post_id, self::OWNED_ATTACHMENT_CONTEXT);
    }

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

        return (int) get_post_meta($attachment_id, self::OWNED_ATTACHMENT_POST_META, true) === $post_id
            && (string) get_post_meta($attachment_id, self::OWNED_ATTACHMENT_CONTEXT_META, true) === self::OWNED_ATTACHMENT_CONTEXT;
    }
}
