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

/**
 * Handles custom meta boxes for the file post type.
 */
class NNAXFUF_Meta_Boxes {
    private function to_lower( $value ) {
        $value = (string) $value;
        if ( function_exists( 'mb_strtolower' ) ) {
            return mb_strtolower( $value );
        }
        return strtolower( $value );
    }

    private function get_user_share_sort_key( $user ) {
        $last_name  = trim( (string) get_user_meta( (int) $user->ID, 'last_name', true ) );
        $first_name = trim( (string) get_user_meta( (int) $user->ID, 'first_name', true ) );
        $user_login = isset( $user->user_login ) ? (string) $user->user_login : '';
        $user_email = isset( $user->user_email ) ? (string) $user->user_email : '';
        $display    = isset( $user->display_name ) ? (string) $user->display_name : '';

        if ( $last_name !== '' || $first_name !== '' ) {
            return $this->to_lower( trim( $last_name . ' ' . $first_name . ' ' . $user_login . ' ' . $user_email ) );
        }

        if ( $display !== '' ) {
            return $this->to_lower( trim( $display . ' ' . $user_login . ' ' . $user_email ) );
        }

        return $this->to_lower( trim( $user_login . ' ' . $user_email ) );
    }

    private function get_user_share_label( $user ) {
        $last_name  = trim( (string) get_user_meta( (int) $user->ID, 'last_name', true ) );
        $first_name = trim( (string) get_user_meta( (int) $user->ID, 'first_name', true ) );
        $user_login = isset( $user->user_login ) ? (string) $user->user_login : '';
        $user_email = isset( $user->user_email ) ? (string) $user->user_email : '';
        $display    = isset( $user->display_name ) ? trim( (string) $user->display_name ) : '';

        $identity_parts = array();
        if ( $user_login !== '' ) {
            $identity_parts[] = '@' . $user_login;
        }
        if ( $user_email !== '' ) {
            $identity_parts[] = $user_email;
        }
        $identity = implode( ' | ', $identity_parts );

        if ( $last_name !== '' && $first_name !== '' ) {
            return $last_name . ', ' . $first_name . ( $identity !== '' ? ' (' . $identity . ')' : '' );
        }
        if ( $last_name !== '' ) {
            return $last_name . ( $identity !== '' ? ' (' . $identity . ')' : '' );
        }
        if ( $first_name !== '' ) {
            return $first_name . ( $identity !== '' ? ' (' . $identity . ')' : '' );
        }
        if ( $display !== '' ) {
            return $display . ( $identity !== '' ? ' (' . $identity . ')' : '' );
        }

        return $identity;
    }

    private function get_share_option_flags() {
        $settings      = NNAXFUF_Helpers::get_settings();
        $share_options = isset( $settings['share_options'] ) && is_array( $settings['share_options'] )
            ? $settings['share_options']
            : array();

        return array(
            'allow_time_limit'   => ! empty( $share_options['allow_time_limit'] ),
            'allow_owner_rights' => ! empty( $share_options['allow_owner_rights'] ),
            'allow_shared_folder_file_delete' => ! empty( $share_options['allow_shared_folder_file_delete'] ),
        );
    }

    private function get_users_for_sharing() {
        if ( ! current_user_can( 'manage_options' ) ) {
            return array();
        }

        $users = get_users(
            array(
                'fields'  => array( 'ID', 'display_name', 'user_login', 'user_email' ),
                'orderby' => 'ID',
                'order'   => 'ASC',
            )
        );

        if ( empty( $users ) ) {
            return array();
        }

        usort(
            $users,
            function ( $a, $b ) {
                $a_key = $this->get_user_share_sort_key( $a );
                $b_key = $this->get_user_share_sort_key( $b );
                return strcmp( $a_key, $b_key );
            }
        );

        return $users;
    }

    private function render_share_users_field( $owner_id, $current_share_rules, $field_name = 'nnaxfuf_share_user_ids[]', $config_name = 'nnaxfuf_share_user_configs', $is_folder_share = false ) {
        $users_for_sharing = $this->get_users_for_sharing();
        if ( empty( $users_for_sharing ) ) {
            return;
        }
        $current_share_rules = is_array( $current_share_rules ) ? $current_share_rules : array();
        $shared_user_ids     = array_map( 'intval', array_keys( $current_share_rules ) );
        $share_flags         = $this->get_share_option_flags();
        $allow_owner_rights  = ! empty( $share_flags['allow_owner_rights'] );
        $allow_time_limit    = ! empty( $share_flags['allow_time_limit'] );
        $allow_shared_folder_file_delete = ! empty( $share_flags['allow_shared_folder_file_delete'] );
        ?>
        <div style="max-height: 260px; overflow-y: auto; border: 1px solid #ccd0d4; background: #fff;">
            <?php foreach ( $users_for_sharing as $u ) :
                $uid = (int) $u->ID;
                if ( $uid <= 0 || $uid === (int) $owner_id ) {
                    continue;
                }
                $rule = isset( $current_share_rules[ $uid ] ) && is_array( $current_share_rules[ $uid ] )
                    ? $current_share_rules[ $uid ]
                    : array();
                $role_value = 'normal';
                if ( ! empty( $rule['can_delete'] ) && $is_folder_share && $allow_shared_folder_file_delete ) {
                    $role_value = 'admin';
                } elseif ( ! empty( $rule['owner_rights'] ) ) {
                    $role_value = 'manager';
                }
                $starts_on  = ! empty( $rule['starts_ts'] ) ? wp_date( 'Y-m-d', (int) $rule['starts_ts'], wp_timezone() ) : '';
                $expires_on = ! empty( $rule['expires_ts'] ) ? wp_date( 'Y-m-d', (int) $rule['expires_ts'], wp_timezone() ) : '';
                $display = $this->get_user_share_label( $u );
                ?>
                <div style="padding: 8px 10px; border-bottom: 1px solid #eceff1;">
                    <label style="display: block; margin: 0 0 6px;">
                        <input
                            type="checkbox"
                            name="<?php echo esc_attr( $field_name ); ?>"
                            value="<?php echo esc_attr( (string) $uid ); ?>"
                            <?php checked( in_array( $uid, $shared_user_ids, true ) ); ?> />
                        <span><?php echo esc_html( $display ); ?></span>
                    </label>
                    <?php if ( $allow_owner_rights || $allow_time_limit ) : ?>
                        <div style="margin: 0 0 2px 22px; display: flex; flex-wrap: wrap; gap: 10px 14px; align-items: center;">
                            <?php if ( $allow_owner_rights ) : ?>
                                <label>
                                    <span style="margin-right: 6px;"><?php esc_html_e( 'Rights', 'nnax-frontend-user-files' ); ?></span>
                                    <select name="<?php echo esc_attr( $config_name ); ?>[<?php echo esc_attr( (string) $uid ); ?>][role]">
                                        <option value="normal" <?php selected( $role_value, 'normal' ); ?>><?php esc_html_e( 'Normal', 'nnax-frontend-user-files' ); ?></option>
                                        <option value="manager" <?php selected( $role_value, 'manager' ); ?>><?php esc_html_e( 'Manager', 'nnax-frontend-user-files' ); ?></option>
                                        <?php if ( $is_folder_share && $allow_shared_folder_file_delete ) : ?>
                                            <option value="admin" <?php selected( $role_value, 'admin' ); ?>><?php esc_html_e( 'Admin', 'nnax-frontend-user-files' ); ?></option>
                                        <?php endif; ?>
                                    </select>
                                </label>
                            <?php endif; ?>
                            <?php if ( $allow_time_limit ) : ?>
                                <label>
                                    <span style="margin-right: 6px;"><?php esc_html_e( 'From', 'nnax-frontend-user-files' ); ?></span>
                                    <input type="date" name="<?php echo esc_attr( $config_name ); ?>[<?php echo esc_attr( (string) $uid ); ?>][starts_on]" value="<?php echo esc_attr( $starts_on ); ?>">
                                </label>
                                <label>
                                    <span style="margin-right: 6px;"><?php esc_html_e( 'Until', 'nnax-frontend-user-files' ); ?></span>
                                    <input type="date" name="<?php echo esc_attr( $config_name ); ?>[<?php echo esc_attr( (string) $uid ); ?>][expires_on]" value="<?php echo esc_attr( $expires_on ); ?>">
                                </label>
                            <?php endif; ?>
                        </div>
                    <?php endif; ?>
                </div>
            <?php endforeach; ?>
        </div>
        <?php
    }

    private function get_share_rules_from_posted_ids( $post_id, $owner_id, $field_name = 'nnaxfuf_share_user_ids', $config_field_name = 'nnaxfuf_share_user_configs' ) {
        $share_flags         = $this->get_share_option_flags();
        $allow_owner_rights  = ! empty( $share_flags['allow_owner_rights'] );
        $allow_time_limit    = ! empty( $share_flags['allow_time_limit'] );
        $allow_shared_folder_file_delete = ! empty( $share_flags['allow_shared_folder_file_delete'] );
        // phpcs:ignore WordPress.Security.NonceVerification.Missing -- Nonce is verified in the calling save_post action.
        $raw_post = wp_unslash( $_POST );
        $posted_ids = isset( $raw_post[ $field_name ] ) && is_array( $raw_post[ $field_name ] )
            ? array_map( 'intval', $raw_post[ $field_name ] )
            : array();
        $posted_ids = array_values(
            array_unique(
                array_filter(
                    $posted_ids,
                    function ( $uid ) use ( $owner_id ) {
                        $uid = (int) $uid;
                        return $uid > 0 && $uid !== (int) $owner_id;
                    }
                )
            )
        );

        $raw_configs = isset( $raw_post[ $config_field_name ] ) && is_array( $raw_post[ $config_field_name ] )
            ? $raw_post[ $config_field_name ]
            : array();

        $new_rules      = array();
        foreach ( $posted_ids as $uid ) {
            $uid = (int) $uid;
            $config = ( isset( $raw_configs[ $uid ] ) && is_array( $raw_configs[ $uid ] ) )
                ? $raw_configs[ $uid ]
                : array();

            $owner_rights = false;
            $role = 'normal';
            if ( $allow_owner_rights ) {
                $role = isset( $config['role'] ) ? sanitize_key( (string) $config['role'] ) : 'normal';
                $owner_rights = in_array( $role, array( 'manager', 'admin' ), true );
            }

            $can_delete = false;
            if ( $is_folder_share && $allow_shared_folder_file_delete ) {
                $can_delete = ( $role === 'admin' );
            }

            $starts_ts  = 0;
            $expires_ts = 0;
            if ( $allow_time_limit ) {
                $starts_raw = isset( $config['starts_on'] ) ? (string) $config['starts_on'] : '';
                $expires_raw = isset( $config['expires_on'] ) ? (string) $config['expires_on'] : '';
                $starts_ts   = NNAXFUF_Helpers::parse_share_start_date( $starts_raw );
                $expires_ts  = NNAXFUF_Helpers::parse_share_expiry_date( $expires_raw );

                if ( $starts_ts > 0 && $expires_ts > 0 && $starts_ts > $expires_ts ) {
                    $tmp = $starts_ts;
                    $starts_ts = $expires_ts;
                    $expires_ts = $tmp;
                }
            }

            $new_rules[ $uid ] = array(
                'starts_ts'    => $starts_ts,
                'expires_ts'   => $expires_ts,
                'owner_rights' => $owner_rights,
                'can_delete'   => $can_delete,
            );
        }

        return $new_rules;
    }

    public function __construct() {
        add_action( 'add_meta_boxes', array( $this, 'add_file_meta_box' ) );
        add_action( 'add_meta_boxes', array( $this, 'add_folder_meta_box' ) );
        add_action( 'save_post_' . NNAXFUF_Plugin::CPT, array( $this, 'save_file_meta' ), 10, 2 );
        add_action( 'save_post_' . NNAXFUF_Plugin::CPT_FOLDER, array( $this, 'save_folder_meta' ), 10, 2 );
        add_action( 'post_updated', array( $this, 'log_backend_title_change' ), 10, 3 );
    }

    public function log_backend_title_change( $post_id, $post_after, $post_before ) {
        $post_id = (int) $post_id;
        if ( $post_id <= 0 ) {
            return;
        }

        if ( wp_is_post_revision( $post_id ) ) {
            return;
        }

        if ( ! $post_after || ! $post_before ) {
            return;
        }

        $post_type = isset( $post_after->post_type ) ? (string) $post_after->post_type : '';
        if ( $post_type !== NNAXFUF_Plugin::CPT && $post_type !== NNAXFUF_Plugin::CPT_FOLDER ) {
            return;
        }

        // Avoid duplicate rename logs when the dedicated frontend AJAX rename action already logs it.
        // phpcs:ignore WordPress.Security.NonceVerification.Missing -- Read-only check to prevent duplicate logs in ajax rename flow.
        $raw_post = wp_unslash( $_POST );
        if ( wp_doing_ajax() && isset( $raw_post['action'] ) && sanitize_key( (string) $raw_post['action'] ) === 'nnaxfuf_rename_item' ) {
            return;
        }

        $old_title = isset( $post_before->post_title ) ? (string) $post_before->post_title : '';
        $new_title = isset( $post_after->post_title ) ? (string) $post_after->post_title : '';
        if ( $old_title === $new_title ) {
            return;
        }

        NNAXFUF_Helpers::log_activity( $post_id, 'rename', array(
            'old_title' => $old_title,
            'new_title' => $new_title,
            'source'    => 'backend',
        ) );
    }

    public function add_file_meta_box() {
        add_meta_box(
            'nnaxfuf_file_meta',
            __( 'Additional File Info', 'nnax-frontend-user-files' ),
            array( $this, 'render_file_meta_box' ),
            NNAXFUF_Plugin::CPT,
            'normal',
            'default'
        );
    }

    public function add_folder_meta_box() {
        add_meta_box(
            'nnaxfuf_folder_meta',
            __( 'Folder Ownership', 'nnax-frontend-user-files' ),
            array( $this, 'render_folder_meta_box' ),
            NNAXFUF_Plugin::CPT_FOLDER,
            'normal',
            'default'
        );
    }

    public function render_file_meta_box( $post ) {
        $settings = NNAXFUF_Helpers::get_settings();
        $fields   = isset( $settings['custom_meta_fields'] ) && is_array( $settings['custom_meta_fields'] )
            ? $settings['custom_meta_fields']
            : array();
        $owner_id = (int) get_post_meta( $post->ID, NNAXFUF_Plugin::META_OWNER, true );
        if ( $owner_id <= 0 ) {
            $owner_id = (int) $post->post_author;
        }
        $parent_folder_id = (int) wp_get_post_parent_id( $post->ID );
        $current_share_rules = NNAXFUF_Helpers::get_item_share_rules( $post->ID );

        wp_nonce_field( 'nnaxfuf_file_meta_save', 'nnaxfuf_file_meta_nonce' );
        ?>

        <table class="form-table">
            <tr>
                <th><label for="nnaxfuf_owner_id"><?php esc_html_e( 'Owner', 'nnax-frontend-user-files' ); ?></label></th>
                <td>
                    <?php
                    wp_dropdown_users(
                        array(
                            'name'             => 'nnaxfuf_owner_id',
                            'id'               => 'nnaxfuf_owner_id',
                            'selected'         => $owner_id,
                            'show_option_none' => false,
                            'option_none_value'=> 0,
                            'class'            => 'regular-text',
                        )
                    );
                    ?>
                </td>
            </tr>
            <tr>
                <th><label for="nnaxfuf_parent_folder_id"><?php esc_html_e( 'Folder', 'nnax-frontend-user-files' ); ?></label></th>
                <td>
                    <?php
                    // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- Core function outputs escaped markup for select options.
                    wp_dropdown_pages(
                        array(
                            // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- Passed to core dropdown API, not direct output.
                            'post_type'         => NNAXFUF_Plugin::CPT_FOLDER,
                            'name'              => 'nnaxfuf_parent_folder_id',
                            'id'                => 'nnaxfuf_parent_folder_id',
                            // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- Value is cast to int and rendered by core.
                            'selected'          => $parent_folder_id,
                            'show_option_none'  => esc_html__( 'Root', 'nnax-frontend-user-files' ),
                            'option_none_value' => '0',
                            'sort_column'       => 'menu_order,post_title',
                            'hierarchical'      => 1,
                            'depth'             => 8,
                        )
                    );
                    ?>
                </td>
            </tr>
            <?php if ( current_user_can( 'manage_options' ) ) : ?>
                <tr>
                    <th><?php esc_html_e( 'Shared with users', 'nnax-frontend-user-files' ); ?></th>
                    <td>
                        <p class="description" style="margin-top:0;">
                            <?php esc_html_e( 'Rights: "Normal" allows viewing/downloading. "Manager" additionally allows editing/uploading/subfolder management. "Admin" is available for folders and additionally allows deleting files inside the shared folder.', 'nnax-frontend-user-files' ); ?>
                        </p>
                        <?php $this->render_share_users_field( $owner_id, $current_share_rules, 'nnaxfuf_share_user_ids[]', 'nnaxfuf_share_user_configs', false ); ?>
                        <p class="description"><?php esc_html_e( 'Admins can adjust who this file is shared with.', 'nnax-frontend-user-files' ); ?></p>
                    </td>
                </tr>
            <?php endif; ?>
            <?php if ( empty( $fields ) ) : ?>
                <tr>
                    <td>
                        <em><?php esc_html_e( 'No meta fields configured. You can manage them in the plugin settings.', 'nnax-frontend-user-files' ); ?></em>
                    </td>
                </tr>
            <?php else : ?>
                <?php foreach ( $fields as $field ) :
                    $meta_key = isset( $field['key'] ) ? (string) $field['key'] : '';
                    $label    = isset( $field['label'] ) ? (string) $field['label'] : '';
                    $type     = isset( $field['type'] ) ? (string) $field['type'] : 'text';
                    if ( $meta_key === '' || $label === '' ) {
                        continue;
                    }
                    $value = get_post_meta( $post->ID, $meta_key, true );
                    $id    = 'nnaxfuf_meta_' . sanitize_html_class( ltrim( $meta_key, '_' ) );
                    ?>
                    <tr>
                        <th><label for="<?php echo esc_attr( $id ); ?>"><?php echo esc_html( $label ); ?></label></th>
                        <td>
                            <?php if ( $type === 'textarea' ) : ?>
                                <textarea
                                    name="nnaxfuf_custom_meta[<?php echo esc_attr( $meta_key ); ?>]"
                                    id="<?php echo esc_attr( $id ); ?>"
                                    class="large-text"
                                    rows="3"><?php echo esc_textarea( (string) $value ); ?></textarea>
                            <?php else : ?>
                                <input
                                    type="<?php echo esc_attr( $type ); ?>"
                                    name="nnaxfuf_custom_meta[<?php echo esc_attr( $meta_key ); ?>]"
                                    id="<?php echo esc_attr( $id ); ?>"
                                    class="regular-text"
                                    value="<?php echo esc_attr( (string) $value ); ?>" />
                            <?php endif; ?>
                        </td>
                    </tr>
                <?php endforeach; ?>
            <?php endif; ?>
        </table>
        <?php
    }

    public function render_folder_meta_box( $post ) {
        $owner_id = (int) get_post_meta( $post->ID, NNAXFUF_Plugin::META_OWNER, true );
        if ( $owner_id <= 0 ) {
            $owner_id = (int) $post->post_author;
        }
        $parent_folder_id   = (int) wp_get_post_parent_id( $post->ID );
        $current_share_rules = NNAXFUF_Helpers::get_item_share_rules( $post->ID );

        wp_nonce_field( 'nnaxfuf_folder_meta_save', 'nnaxfuf_folder_meta_nonce' );
        ?>
        <p>
            <label for="nnaxfuf_folder_owner_id"><strong><?php esc_html_e( 'Owner', 'nnax-frontend-user-files' ); ?></strong></label>
        </p>
        <p>
            <?php
            wp_dropdown_users(
                array(
                    'name'             => 'nnaxfuf_folder_owner_id',
                    'id'               => 'nnaxfuf_folder_owner_id',
                    'selected'         => $owner_id,
                    'show_option_none' => false,
                    'option_none_value'=> 0,
                    'class'            => 'widefat',
                )
            );
            ?>
        </p>
        <p>
            <label for="nnaxfuf_folder_parent_id"><strong><?php esc_html_e( 'Parent folder', 'nnax-frontend-user-files' ); ?></strong></label>
        </p>
        <p>
            <?php
            $all_folder_targets = get_posts(
                array(
                    'post_type'      => NNAXFUF_Plugin::CPT_FOLDER,
                    'post_status'    => 'any',
                    'posts_per_page' => -1,
                    'orderby'        => 'title',
                    'order'          => 'ASC',
                )
            );
            ?>
            <select name="nnaxfuf_folder_parent_id" id="nnaxfuf_folder_parent_id" class="widefat">
                <option value="0"><?php esc_html_e( 'Root', 'nnax-frontend-user-files' ); ?></option>
                <?php foreach ( $all_folder_targets as $folder_target ) : ?>
                    <?php
                    $target_id = (int) $folder_target->ID;
                    if ( $target_id === (int) $post->ID ) {
                        continue;
                    }

                    $target_ancestors = array_map( 'intval', get_post_ancestors( $target_id ) );
                    if ( in_array( (int) $post->ID, $target_ancestors, true ) ) {
                        continue;
                    }

                    $depth = count( $target_ancestors );
                    $label = str_repeat( '— ', max( 0, $depth ) ) . NNAXFUF_Helpers::get_raw_item_title( $target_id );
                    ?>
                    <option value="<?php echo esc_attr( $target_id ); ?>" <?php selected( $parent_folder_id, $target_id ); ?>>
                        <?php echo esc_html( $label ); ?>
                    </option>
                <?php endforeach; ?>
            </select>
            <?php
            ?>
        </p>
        <?php if ( current_user_can( 'manage_options' ) ) : ?>
            <p>
                <strong><?php esc_html_e( 'Shared with users', 'nnax-frontend-user-files' ); ?></strong>
            </p>
            <p class="description" style="margin-top:0;">
                <?php esc_html_e( 'Rights: "Normal" allows viewing/downloading. "Manager" additionally allows editing/uploading/subfolder management. "Admin" is available for folders and additionally allows deleting files inside the shared folder.', 'nnax-frontend-user-files' ); ?>
            </p>
            <?php $this->render_share_users_field( $owner_id, $current_share_rules, 'nnaxfuf_folder_share_user_ids[]', 'nnaxfuf_folder_share_user_configs', true ); ?>
            <p class="description"><?php esc_html_e( 'Admins can adjust who this folder is shared with.', 'nnax-frontend-user-files' ); ?></p>
        <?php endif; ?>
        <?php
    }

    public function save_file_meta( $post_id, $post ) {
        if ( ! isset( $_POST['nnaxfuf_file_meta_nonce'] ) ) {
            return;
        }

        check_admin_referer( 'nnaxfuf_file_meta_save', 'nnaxfuf_file_meta_nonce' );

        if ( defined( 'DOING_AUTOSAVE' ) && DOING_AUTOSAVE ) {
            return;
        }

        if ( ! current_user_can( 'edit_post', $post_id ) ) {
            return;
        }

        if ( isset( $_POST['nnaxfuf_owner_id'] ) ) {
            $owner_id = absint( wp_unslash( $_POST['nnaxfuf_owner_id'] ) );
            if ( $owner_id > 0 ) {
                update_post_meta( $post_id, NNAXFUF_Plugin::META_OWNER, $owner_id );
            } else {
                delete_post_meta( $post_id, NNAXFUF_Plugin::META_OWNER );
            }
        }

        // Admin-only share management from the file edit screen.
        if ( current_user_can( 'manage_options' ) ) {
            $owner_id = (int) get_post_meta( $post_id, NNAXFUF_Plugin::META_OWNER, true );
            if ( $owner_id <= 0 ) {
                $owner_id = (int) $post->post_author;
            }

            $new_rules = $this->get_share_rules_from_posted_ids( $post_id, $owner_id, 'nnaxfuf_share_user_ids', 'nnaxfuf_share_user_configs' );
            NNAXFUF_Helpers::set_item_share_rules( $post_id, $new_rules );
        }

        if ( isset( $_POST['nnaxfuf_parent_folder_id'] ) ) {
            $target_parent = absint( wp_unslash( $_POST['nnaxfuf_parent_folder_id'] ) );
            $target_parent = max( 0, $target_parent );

            if ( $target_parent > 0 ) {
                $parent_post = get_post( $target_parent );
                if ( ! $parent_post || $parent_post->post_type !== NNAXFUF_Plugin::CPT_FOLDER ) {
                    $target_parent = 0;
                }
            }

            if ( (int) $post->post_parent !== $target_parent ) {
                remove_action( 'save_post_' . NNAXFUF_Plugin::CPT, array( $this, 'save_file_meta' ), 10 );
                wp_update_post(
                    array(
                        'ID'          => $post_id,
                        'post_parent' => $target_parent,
                    )
                );
                add_action( 'save_post_' . NNAXFUF_Plugin::CPT, array( $this, 'save_file_meta' ), 10, 2 );
            }
        }

        $settings = NNAXFUF_Helpers::get_settings();
        $fields   = isset( $settings['custom_meta_fields'] ) && is_array( $settings['custom_meta_fields'] )
            ? $settings['custom_meta_fields']
            : array();
        $raw_post = wp_unslash( $_POST );
        $posted_meta = isset( $raw_post['nnaxfuf_custom_meta'] ) && is_array( $raw_post['nnaxfuf_custom_meta'] )
            ? $raw_post['nnaxfuf_custom_meta']
            : array();

        foreach ( $fields as $field ) {
            $meta_key = isset( $field['key'] ) ? (string) $field['key'] : '';
            $type     = isset( $field['type'] ) ? (string) $field['type'] : 'text';
            if ( $meta_key === '' ) {
                continue;
            }

            $raw_value = isset( $posted_meta[ $meta_key ] ) ? (string) $posted_meta[ $meta_key ] : '';
            $value = '';

            switch ( $type ) {
                case 'textarea':
                    $value = sanitize_textarea_field( $raw_value );
                    break;
                case 'date':
                    $value = sanitize_text_field( $raw_value );
                    if ( $value !== '' && ! preg_match( '/^\d{4}-\d{2}-\d{2}$/', $value ) ) {
                        $value = '';
                    }
                    break;
                case 'number':
                    $value = trim( str_replace( ',', '.', $raw_value ) );
                    if ( $value === '' || ! is_numeric( $value ) ) {
                        $value = '';
                    }
                    break;
                case 'email':
                    $value = sanitize_email( $raw_value );
                    break;
                case 'url':
                    $value = esc_url_raw( $raw_value );
                    break;
                case 'text':
                default:
                    $value = sanitize_text_field( $raw_value );
                    break;
            }

            if ( $value === '' ) {
                delete_post_meta( $post_id, $meta_key );
            } else {
                update_post_meta( $post_id, $meta_key, $value );
            }
        }
    }

    public function save_folder_meta( $post_id, $post ) {
        if ( ! isset( $_POST['nnaxfuf_folder_meta_nonce'] ) ) {
            return;
        }

        check_admin_referer( 'nnaxfuf_folder_meta_save', 'nnaxfuf_folder_meta_nonce' );

        if ( defined( 'DOING_AUTOSAVE' ) && DOING_AUTOSAVE ) {
            return;
        }

        if ( ! current_user_can( 'edit_post', $post_id ) ) {
            return;
        }

        $owner_id = 0;
        if ( isset( $_POST['nnaxfuf_folder_owner_id'] ) ) {
            $owner_id = absint( wp_unslash( $_POST['nnaxfuf_folder_owner_id'] ) );
            if ( $owner_id > 0 ) {
                update_post_meta( $post_id, NNAXFUF_Plugin::META_OWNER, $owner_id );

                if ( (int) $post->post_author !== $owner_id ) {
                    remove_action( 'save_post_' . NNAXFUF_Plugin::CPT_FOLDER, array( $this, 'save_folder_meta' ), 10 );
                    wp_update_post(
                        array(
                            'ID'          => $post_id,
                            'post_author' => $owner_id,
                        )
                    );
                    add_action( 'save_post_' . NNAXFUF_Plugin::CPT_FOLDER, array( $this, 'save_folder_meta' ), 10, 2 );
                }
            } else {
                delete_post_meta( $post_id, NNAXFUF_Plugin::META_OWNER );
            }
        }

        if ( isset( $_POST['nnaxfuf_folder_parent_id'] ) ) {
            $target_parent = max( 0, absint( wp_unslash( $_POST['nnaxfuf_folder_parent_id'] ) ) );

            if ( $target_parent > 0 ) {
                $parent_post = get_post( $target_parent );
                $invalid_parent = ( ! $parent_post || $parent_post->post_type !== NNAXFUF_Plugin::CPT_FOLDER );
                $would_create_cycle = in_array( (int) $post_id, array_map( 'intval', get_post_ancestors( $target_parent ) ), true );
                if ( $invalid_parent || $would_create_cycle || $target_parent === (int) $post_id ) {
                    $target_parent = (int) $post->post_parent;
                }
            }

            if ( (int) $post->post_parent !== $target_parent ) {
                remove_action( 'save_post_' . NNAXFUF_Plugin::CPT_FOLDER, array( $this, 'save_folder_meta' ), 10 );
                wp_update_post(
                    array(
                        'ID'          => $post_id,
                        'post_parent' => $target_parent,
                    )
                );
                add_action( 'save_post_' . NNAXFUF_Plugin::CPT_FOLDER, array( $this, 'save_folder_meta' ), 10, 2 );
            }
        }

        if ( current_user_can( 'manage_options' ) ) {
            if ( $owner_id <= 0 ) {
                $owner_id = (int) get_post_field( 'post_author', $post_id );
            }
            $new_rules = $this->get_share_rules_from_posted_ids( $post_id, $owner_id, 'nnaxfuf_folder_share_user_ids', 'nnaxfuf_folder_share_user_configs' );
            NNAXFUF_Helpers::set_item_share_rules( $post_id, $new_rules );
        }
    }
}
