<?php

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

trait Niwa_AI_Tools_Taxonomy {

    /* ==================================================================
     *  TAXONOMY MANAGEMENT — Shared helpers
     * ================================================================== */

    /**
     * Format a WP_Term object into a consistent data array.
     *
     * @param WP_Term $term Term object.
     * @return array
     */
    private function format_term_data( $term ) {
        $data = array(
            'id'          => intval( $term->term_id ),
            'name'        => $term->name,
            'slug'        => $term->slug,
            'description' => $term->description,
            'parent'      => intval( $term->parent ),
            'count'       => intval( $term->count ),
        );

        $link = get_term_link( $term );
        if ( ! is_wp_error( $link ) ) {
            $data['link'] = $link;
        }

        return $data;
    }

    /**
     * Get the human-readable taxonomy label.
     *
     * @param string $taxonomy Taxonomy slug.
     * @return string
     */
    private function get_taxonomy_label( $taxonomy ) {
        return 'category' === $taxonomy ? 'kategorija' : 'tag';
    }

    /**
     * Get the plural human-readable taxonomy label.
     *
     * @param string $taxonomy Taxonomy slug.
     * @return string
     */
    private function get_taxonomy_label_plural( $taxonomy ) {
        return 'category' === $taxonomy ? 'kategorije' : 'tagovi';
    }

    /* ------------------------------------------------------------------
     *  Shared: list_terms
     * ------------------------------------------------------------------ */

    private function preview_list_terms( $args, $taxonomy, $tool_name ) {
        $per_page   = isset( $args['per_page'] ) ? max( 1, min( 100, intval( $args['per_page'] ) ) ) : 20;
        $page       = isset( $args['page'] ) ? max( 1, intval( $args['page'] ) ) : 1;
        $search     = isset( $args['search'] ) ? sanitize_text_field( trim( (string) $args['search'] ) ) : '';
        $orderby    = isset( $args['orderby'] ) && in_array( (string) $args['orderby'], array( 'name', 'id', 'count', 'slug' ), true )
            ? (string) $args['orderby']
            : 'name';
        $order      = isset( $args['order'] ) && in_array( strtolower( (string) $args['order'] ), array( 'asc', 'desc' ), true )
            ? strtolower( (string) $args['order'] )
            : 'asc';
        $hide_empty = isset( $args['hide_empty'] ) ? (bool) $args['hide_empty'] : false;

        $query_args = array(
            'taxonomy'   => $taxonomy,
            'number'     => $per_page,
            'offset'     => ( $page - 1 ) * $per_page,
            'orderby'    => 'id' === $orderby ? 'term_id' : $orderby,
            'order'      => strtoupper( $order ),
            'hide_empty' => $hide_empty,
        );

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

        if ( 'category' === $taxonomy && isset( $args['parent'] ) ) {
            $parent_id = intval( $args['parent'] );
            if ( $parent_id >= 0 ) {
                $query_args['parent'] = $parent_id;
            }
        }

        $terms = get_terms( $query_args );
        if ( is_wp_error( $terms ) ) {
            return array(
                'success' => false,
                'error'   => 'Greska pri preuzimanju ' . $this->get_taxonomy_label_plural( $taxonomy ) . ': ' . $terms->get_error_message(),
            );
        }

        $result = array();
        foreach ( $terms as $term ) {
            $data = array(
                'id'          => intval( $term->term_id ),
                'name'        => $term->name,
                'slug'        => $term->slug,
                'description' => $term->description,
                'parent'      => intval( $term->parent ),
                'count'       => intval( $term->count ),
            );
            $result[] = $data;
        }

        /* Count total for pagination info. */
        $total_args = $query_args;
        unset( $total_args['number'], $total_args['offset'] );
        $total_args['fields'] = 'count';
        $total = (int) get_terms( $total_args );

        return array(
            'success' => true,
            'action'  => array(
                'type'             => $tool_name,
                'label'            => 'Prikaz ' . $this->get_taxonomy_label_plural( $taxonomy ),
                'summary'          => 'Prikazano ' . count( $result ) . ' od ' . $total . ' ' . $this->get_taxonomy_label_plural( $taxonomy ) . ' (stranica ' . $page . ').',
                'rollback_plan'    => 'Citanje — nema potrebe za rollback.',
                'rollback_support' => 'none',
                'targets'          => array(),
                'args'             => array(),
                'read_only'        => true,
                'data'             => array(
                    'terms'    => $result,
                    'total'    => $total,
                    'page'     => $page,
                    'per_page' => $per_page,
                ),
            ),
        );
    }

    /* ------------------------------------------------------------------
     *  Shared: get_term
     * ------------------------------------------------------------------ */

    private function preview_get_term( $args, $taxonomy, $tool_name ) {
        $id_key  = 'category' === $taxonomy ? 'category_id' : 'tag_id';
        $term_id = isset( $args[ $id_key ] ) ? intval( $args[ $id_key ] ) : 0;

        if ( $term_id <= 0 ) {
            return array(
                'success' => false,
                'error'   => ucfirst( $this->get_taxonomy_label( $taxonomy ) ) . ' ID je obavezan.',
            );
        }

        $term = get_term( $term_id, $taxonomy );
        if ( ! $term || is_wp_error( $term ) ) {
            return array(
                'success' => false,
                'error'   => ucfirst( $this->get_taxonomy_label( $taxonomy ) ) . ' (ID: ' . $term_id . ') nije pronadjena.',
            );
        }

        $data = $this->format_term_data( $term );

        return array(
            'success' => true,
            'action'  => array(
                'type'             => $tool_name,
                'label'            => 'Prikaz ' . $this->get_taxonomy_label( $taxonomy ),
                'summary'          => ucfirst( $this->get_taxonomy_label( $taxonomy ) ) . ': "' . $term->name . '" (ID: ' . $term_id . ', postova: ' . $term->count . ').',
                'rollback_plan'    => 'Citanje — nema potrebe za rollback.',
                'rollback_support' => 'none',
                'targets'          => array(),
                'args'             => array(),
                'read_only'        => true,
                'data'             => array(
                    'term' => $data,
                ),
            ),
        );
    }

    /* ------------------------------------------------------------------
     *  Shared: create_term
     * ------------------------------------------------------------------ */

    private function preview_create_term( $args, $taxonomy, $tool_name ) {
        $name = isset( $args['name'] ) ? sanitize_text_field( trim( (string) $args['name'] ) ) : '';
        if ( '' === $name ) {
            return array(
                'success' => false,
                'error'   => 'Naziv ' . $this->get_taxonomy_label( $taxonomy ) . ' (name) je obavezan.',
            );
        }

        /* Check if term with same name already exists. */
        $existing = get_term_by( 'name', $name, $taxonomy );
        if ( $existing && ! is_wp_error( $existing ) ) {
            return array(
                'success' => false,
                'error'   => ucfirst( $this->get_taxonomy_label( $taxonomy ) ) . ' sa nazivom "' . $name . '" vec postoji (ID: ' . $existing->term_id . ').',
            );
        }

        $slug        = isset( $args['slug'] ) ? sanitize_title( (string) $args['slug'] ) : '';
        $description = isset( $args['description'] ) ? sanitize_textarea_field( (string) $args['description'] ) : '';
        $parent      = 0;

        if ( 'category' === $taxonomy && isset( $args['parent'] ) ) {
            $parent = intval( $args['parent'] );
            if ( $parent > 0 ) {
                $parent_term = get_term( $parent, 'category' );
                if ( ! $parent_term || is_wp_error( $parent_term ) ) {
                    return array(
                        'success' => false,
                        'error'   => 'Roditeljska kategorija (ID: ' . $parent . ') ne postoji.',
                    );
                }
            }
        }

        $summary_parts   = array();
        $summary_parts[] = 'Kreiranje ' . $this->get_taxonomy_label( $taxonomy ) . ': "' . $name . '"';
        if ( '' !== $slug ) {
            $summary_parts[] = 'Slug: ' . $slug;
        }
        if ( '' !== $description ) {
            $summary_parts[] = 'Opis: ' . mb_substr( $description, 0, 60 ) . ( mb_strlen( $description ) > 60 ? '...' : '' );
        }
        if ( $parent > 0 ) {
            $parent_term = get_term( $parent, 'category' );
            $summary_parts[] = 'Roditelj: "' . ( $parent_term && ! is_wp_error( $parent_term ) ? $parent_term->name : 'ID ' . $parent ) . '"';
        }

        $action_args = array(
            'name'        => $name,
            'slug'        => $slug,
            'description' => $description,
            'taxonomy'    => $taxonomy,
        );

        if ( 'category' === $taxonomy ) {
            $action_args['parent'] = $parent;
        }

        return array(
            'success' => true,
            'action'  => array(
                'type'             => $tool_name,
                'label'            => 'Kreiraj ' . $this->get_taxonomy_label( $taxonomy ),
                'summary'          => implode( ' | ', $summary_parts ),
                'rollback_plan'    => 'Brise kreirani term iz taksonomije.',
                'rollback_support' => 'full',
                'targets'          => array(),
                'args'             => $action_args,
            ),
        );
    }

    private function validate_create_term_action_args( $args ) {
        if ( empty( $args['name'] ) || '' === trim( (string) $args['name'] ) ) {
            return array(
                'success' => false,
                'error'   => 'Naziv terma (name) nedostaje.',
            );
        }

        if ( empty( $args['taxonomy'] ) || ! in_array( (string) $args['taxonomy'], array( 'category', 'post_tag' ), true ) ) {
            return array(
                'success' => false,
                'error'   => 'Nevalidna taksonomija za kreiranje terma.',
            );
        }

        return array( 'success' => true );
    }

    private function execute_create_term_action( $action ) {
        $args     = isset( $action['args'] ) && is_array( $action['args'] ) ? $action['args'] : array();
        $name     = sanitize_text_field( (string) $args['name'] );
        $taxonomy = (string) $args['taxonomy'];

        $term_args = array();
        if ( ! empty( $args['slug'] ) ) {
            $term_args['slug'] = sanitize_title( (string) $args['slug'] );
        }
        if ( ! empty( $args['description'] ) ) {
            $term_args['description'] = sanitize_textarea_field( (string) $args['description'] );
        }
        if ( 'category' === $taxonomy && isset( $args['parent'] ) && intval( $args['parent'] ) > 0 ) {
            $term_args['parent'] = intval( $args['parent'] );
        }

        $result = wp_insert_term( $name, $taxonomy, $term_args );
        if ( is_wp_error( $result ) ) {
            return array(
                'success' => false,
                'error'   => ucfirst( $this->get_taxonomy_label( $taxonomy ) ) . ' nije kreirana: ' . $result->get_error_message(),
            );
        }

        $term_id = intval( $result['term_id'] );
        $term    = get_term( $term_id, $taxonomy );
        $label   = ( $term && ! is_wp_error( $term ) ) ? $term->name : $name;

        return array(
            'success'          => true,
            'summary'          => 'Kreirana ' . $this->get_taxonomy_label( $taxonomy ) . ' "' . $label . '" (ID: ' . $term_id . ').',
            'executed_actions' => array(
                array(
                    'type'    => isset( $action['type'] ) ? (string) $action['type'] : 'create_term',
                    'term_id' => $term_id,
                ),
            ),
            'rollback_entries' => array(
                array(
                    'type'     => 'term_delete',
                    'term_id'  => $term_id,
                    'taxonomy' => $taxonomy,
                ),
            ),
        );
    }

    /* ------------------------------------------------------------------
     *  Shared: update_term
     * ------------------------------------------------------------------ */

    private function preview_update_term( $args, $taxonomy, $tool_name ) {
        $id_key  = 'category' === $taxonomy ? 'category_id' : 'tag_id';
        $term_id = isset( $args[ $id_key ] ) ? intval( $args[ $id_key ] ) : 0;

        if ( $term_id <= 0 ) {
            return array(
                'success' => false,
                'error'   => ucfirst( $this->get_taxonomy_label( $taxonomy ) ) . ' ID je obavezan za update.',
            );
        }

        $term = get_term( $term_id, $taxonomy );
        if ( ! $term || is_wp_error( $term ) ) {
            return array(
                'success' => false,
                'error'   => ucfirst( $this->get_taxonomy_label( $taxonomy ) ) . ' (ID: ' . $term_id . ') nije pronadjena.',
            );
        }

        /* Capture previous values for rollback. */
        $rollback_snapshot = array(
            'type'                 => 'term_restore',
            'term_id'              => $term_id,
            'taxonomy'             => $taxonomy,
            'previous_name'        => $term->name,
            'previous_slug'        => $term->slug,
            'previous_description' => $term->description,
            'previous_parent'      => intval( $term->parent ),
        );

        /* Detect what is changing. */
        $changes     = array();
        $update_args = array();

        if ( isset( $args['name'] ) && '' !== trim( (string) $args['name'] ) ) {
            $update_args['name'] = sanitize_text_field( (string) $args['name'] );
            $changes[] = 'naziv: "' . $term->name . '" → "' . $update_args['name'] . '"';
        }
        if ( isset( $args['slug'] ) ) {
            $update_args['slug'] = sanitize_title( (string) $args['slug'] );
            $changes[] = 'slug: "' . $term->slug . '" → "' . $update_args['slug'] . '"';
        }
        if ( isset( $args['description'] ) ) {
            $update_args['description'] = sanitize_textarea_field( (string) $args['description'] );
            $desc_preview = mb_substr( $update_args['description'], 0, 40 );
            $changes[] = 'opis: "' . $desc_preview . ( mb_strlen( $update_args['description'] ) > 40 ? '...' : '' ) . '"';
        }
        if ( 'category' === $taxonomy && isset( $args['parent'] ) ) {
            $new_parent = intval( $args['parent'] );
            if ( $new_parent > 0 ) {
                $parent_term = get_term( $new_parent, 'category' );
                if ( ! $parent_term || is_wp_error( $parent_term ) ) {
                    return array(
                        'success' => false,
                        'error'   => 'Roditeljska kategorija (ID: ' . $new_parent . ') ne postoji.',
                    );
                }
            }
            $update_args['parent'] = $new_parent;
            $changes[] = 'roditelj: ' . intval( $term->parent ) . ' → ' . $new_parent;
        }

        if ( empty( $changes ) ) {
            return array(
                'success' => false,
                'error'   => 'Nijedna promena nije navedena za ' . $this->get_taxonomy_label( $taxonomy ) . ' (ID: ' . $term_id . ').',
            );
        }

        $summary = 'Azuriranje ' . $this->get_taxonomy_label( $taxonomy ) . ' "' . $term->name . '" (ID: ' . $term_id . '). Promene: ' . implode( ', ', $changes );

        $action_args = array_merge(
            $update_args,
            array(
                'term_id'           => $term_id,
                'taxonomy'          => $taxonomy,
                'rollback_snapshot' => $rollback_snapshot,
            )
        );

        return array(
            'success' => true,
            'action'  => array(
                'type'             => $tool_name,
                'label'            => 'Azuriraj ' . $this->get_taxonomy_label( $taxonomy ),
                'summary'          => $summary,
                'rollback_plan'    => 'Vraca prethodne vrednosti (naziv, slug, opis, roditelj).',
                'rollback_support' => 'full',
                'targets'          => array(
                    array(
                        'term_id' => $term_id,
                        'name'    => $term->name,
                        'slug'    => $term->slug,
                    ),
                ),
                'args'             => $action_args,
            ),
        );
    }

    private function validate_update_term_action_args( $args ) {
        if ( empty( $args['term_id'] ) || intval( $args['term_id'] ) <= 0 ) {
            return array(
                'success' => false,
                'error'   => 'Term ID nedostaje za update.',
            );
        }

        if ( empty( $args['taxonomy'] ) || ! in_array( (string) $args['taxonomy'], array( 'category', 'post_tag' ), true ) ) {
            return array(
                'success' => false,
                'error'   => 'Nevalidna taksonomija za update terma.',
            );
        }

        return array( 'success' => true );
    }

    private function execute_update_term_action( $action ) {
        $args     = isset( $action['args'] ) && is_array( $action['args'] ) ? $action['args'] : array();
        $term_id  = intval( $args['term_id'] );
        $taxonomy = (string) $args['taxonomy'];

        $term = get_term( $term_id, $taxonomy );
        if ( ! $term || is_wp_error( $term ) ) {
            return array(
                'success' => false,
                'error'   => ucfirst( $this->get_taxonomy_label( $taxonomy ) ) . ' (ID: ' . $term_id . ') ne postoji.',
            );
        }

        $update_data = array();
        if ( isset( $args['name'] ) )        { $update_data['name']        = sanitize_text_field( (string) $args['name'] ); }
        if ( isset( $args['slug'] ) )        { $update_data['slug']        = sanitize_title( (string) $args['slug'] ); }
        if ( isset( $args['description'] ) ) { $update_data['description'] = sanitize_textarea_field( (string) $args['description'] ); }
        if ( 'category' === $taxonomy && isset( $args['parent'] ) ) {
            $update_data['parent'] = intval( $args['parent'] );
        }

        if ( empty( $update_data ) ) {
            return array(
                'success' => false,
                'error'   => 'Nema podataka za update terma.',
            );
        }

        $result = wp_update_term( $term_id, $taxonomy, $update_data );
        if ( is_wp_error( $result ) ) {
            return array(
                'success' => false,
                'error'   => ucfirst( $this->get_taxonomy_label( $taxonomy ) ) . ' update nije uspeo: ' . $result->get_error_message(),
            );
        }

        $updated_term = get_term( $term_id, $taxonomy );
        $label = ( $updated_term && ! is_wp_error( $updated_term ) ) ? $updated_term->name : $term->name;

        $rollback_entries = array();
        if ( ! empty( $args['rollback_snapshot'] ) && is_array( $args['rollback_snapshot'] ) ) {
            $rollback_entries[] = $args['rollback_snapshot'];
        }

        return array(
            'success'          => true,
            'summary'          => 'Azurirana ' . $this->get_taxonomy_label( $taxonomy ) . ' "' . $label . '" (ID: ' . $term_id . ').',
            'executed_actions' => array(
                array(
                    'type'    => isset( $action['type'] ) ? (string) $action['type'] : 'update_term',
                    'term_id' => $term_id,
                ),
            ),
            'rollback_entries' => $rollback_entries,
        );
    }

    /* ------------------------------------------------------------------
     *  Shared: delete_term
     * ------------------------------------------------------------------ */

    private function preview_delete_term( $args, $taxonomy, $tool_name ) {
        $id_key  = 'category' === $taxonomy ? 'category_id' : 'tag_id';
        $term_id = isset( $args[ $id_key ] ) ? intval( $args[ $id_key ] ) : 0;
        $force   = isset( $args['force'] ) ? (bool) $args['force'] : false;

        if ( $term_id <= 0 ) {
            return array(
                'success' => false,
                'error'   => ucfirst( $this->get_taxonomy_label( $taxonomy ) ) . ' ID je obavezan za brisanje.',
            );
        }

        $term = get_term( $term_id, $taxonomy );
        if ( ! $term || is_wp_error( $term ) ) {
            return array(
                'success' => false,
                'error'   => ucfirst( $this->get_taxonomy_label( $taxonomy ) ) . ' (ID: ' . $term_id . ') nije pronadjena.',
            );
        }

        /* Safety: cannot delete the default category. */
        if ( 'category' === $taxonomy ) {
            $default_cat = intval( get_option( 'default_category', 0 ) );
            if ( $term_id === $default_cat ) {
                return array(
                    'success' => false,
                    'error'   => 'Ne moze se obrisati podrazumevana kategorija (ID: ' . $term_id . ').',
                );
            }
        }

        if ( ! $force ) {
            return array(
                'success' => false,
                'error'   => 'Brisanje ' . $this->get_taxonomy_label( $taxonomy ) . ' "' . $term->name . '" zahteva force=true. Ova akcija je destruktivna i ne moze se ponistiti.',
            );
        }

        $summary_parts   = array();
        $summary_parts[] = 'Brisanje ' . $this->get_taxonomy_label( $taxonomy ) . ': "' . $term->name . '" (ID: ' . $term_id . ')';
        if ( $term->count > 0 ) {
            $summary_parts[] = 'UPOZORENJE: ' . $term->count . ' postova koristi ovu ' . $this->get_taxonomy_label( $taxonomy );
        }

        return array(
            'success' => true,
            'action'  => array(
                'type'             => $tool_name,
                'label'            => 'Obrisi ' . $this->get_taxonomy_label( $taxonomy ),
                'summary'          => implode( ' | ', $summary_parts ),
                'rollback_plan'    => 'Brisanje terma je trajno — rollback nije moguc.',
                'rollback_support' => 'none',
                'targets'          => array(
                    array(
                        'term_id' => $term_id,
                        'name'    => $term->name,
                        'slug'    => $term->slug,
                    ),
                ),
                'args'             => array(
                    'term_id'  => $term_id,
                    'taxonomy' => $taxonomy,
                    'force'    => true,
                ),
            ),
        );
    }

    private function validate_delete_term_action_args( $args ) {
        if ( empty( $args['term_id'] ) || intval( $args['term_id'] ) <= 0 ) {
            return array(
                'success' => false,
                'error'   => 'Term ID nedostaje za brisanje.',
            );
        }

        if ( empty( $args['taxonomy'] ) || ! in_array( (string) $args['taxonomy'], array( 'category', 'post_tag' ), true ) ) {
            return array(
                'success' => false,
                'error'   => 'Nevalidna taksonomija za brisanje terma.',
            );
        }

        return array( 'success' => true );
    }

    private function execute_delete_term_action( $action ) {
        $args     = isset( $action['args'] ) && is_array( $action['args'] ) ? $action['args'] : array();
        $term_id  = intval( $args['term_id'] );
        $taxonomy = (string) $args['taxonomy'];

        $term = get_term( $term_id, $taxonomy );
        if ( ! $term || is_wp_error( $term ) ) {
            return array(
                'success' => false,
                'error'   => ucfirst( $this->get_taxonomy_label( $taxonomy ) ) . ' (ID: ' . $term_id . ') ne postoji.',
            );
        }

        /* Safety: cannot delete the default category. */
        if ( 'category' === $taxonomy ) {
            $default_cat = intval( get_option( 'default_category', 0 ) );
            if ( $term_id === $default_cat ) {
                return array(
                    'success' => false,
                    'error'   => 'Ne moze se obrisati podrazumevana kategorija (ID: ' . $term_id . ').',
                );
            }
        }

        $term_name = $term->name;

        $result = wp_delete_term( $term_id, $taxonomy );
        if ( is_wp_error( $result ) ) {
            return array(
                'success' => false,
                'error'   => ucfirst( $this->get_taxonomy_label( $taxonomy ) ) . ' brisanje nije uspelo: ' . $result->get_error_message(),
            );
        }

        if ( false === $result ) {
            return array(
                'success' => false,
                'error'   => ucfirst( $this->get_taxonomy_label( $taxonomy ) ) . ' (ID: ' . $term_id . ') nije mogla biti obrisana.',
            );
        }

        return array(
            'success'          => true,
            'summary'          => 'Obrisana ' . $this->get_taxonomy_label( $taxonomy ) . ' "' . $term_name . '" (ID: ' . $term_id . ').',
            'executed_actions' => array(
                array(
                    'type'    => isset( $action['type'] ) ? (string) $action['type'] : 'delete_term',
                    'term_id' => $term_id,
                ),
            ),
            'rollback_entries' => array(),
        );
    }

    /* ==================================================================
     *  CATEGORIES — Public-facing delegation methods
     * ================================================================== */

    /* --- list_categories --- */

    private function preview_list_categories( $args ) {
        return $this->preview_list_terms( $args, 'category', 'list_categories' );
    }

    public function execute_list_categories( $args ) {
        $preview = $this->preview_list_categories( $args );
        if ( empty( $preview['success'] ) ) {
            return $preview;
        }

        $data = isset( $preview['action']['data'] ) ? $preview['action']['data'] : array();

        return array(
            'ok'       => true,
            'terms'    => isset( $data['terms'] ) ? $data['terms'] : array(),
            'total'    => isset( $data['total'] ) ? intval( $data['total'] ) : 0,
            'page'     => isset( $data['page'] ) ? intval( $data['page'] ) : 1,
            'per_page' => isset( $data['per_page'] ) ? intval( $data['per_page'] ) : 20,
        );
    }

    /* --- get_category --- */

    private function preview_get_category( $args ) {
        return $this->preview_get_term( $args, 'category', 'get_category' );
    }

    public function execute_get_category( $args ) {
        $preview = $this->preview_get_category( $args );
        if ( empty( $preview['success'] ) ) {
            return $preview;
        }

        return array(
            'ok'   => true,
            'term' => isset( $preview['action']['data']['term'] ) ? $preview['action']['data']['term'] : array(),
        );
    }

    /* --- create_category --- */

    private function preview_create_category( $args ) {
        return $this->preview_create_term( $args, 'category', 'create_category' );
    }

    private function validate_create_category_action_args( $args ) {
        return $this->validate_create_term_action_args( $args );
    }

    private function execute_create_category_action( $action ) {
        return $this->execute_create_term_action( $action );
    }

    /* --- update_category --- */

    private function preview_update_category( $args ) {
        return $this->preview_update_term( $args, 'category', 'update_category' );
    }

    private function validate_update_category_action_args( $args ) {
        return $this->validate_update_term_action_args( $args );
    }

    private function execute_update_category_action( $action ) {
        return $this->execute_update_term_action( $action );
    }

    /* --- delete_category --- */

    private function preview_delete_category( $args ) {
        return $this->preview_delete_term( $args, 'category', 'delete_category' );
    }

    private function validate_delete_category_action_args( $args ) {
        return $this->validate_delete_term_action_args( $args );
    }

    private function execute_delete_category_action( $action ) {
        return $this->execute_delete_term_action( $action );
    }

    /* ==================================================================
     *  TAGS — Public-facing delegation methods
     * ================================================================== */

    /* --- list_tags --- */

    private function preview_list_tags( $args ) {
        return $this->preview_list_terms( $args, 'post_tag', 'list_tags' );
    }

    public function execute_list_tags( $args ) {
        $preview = $this->preview_list_tags( $args );
        if ( empty( $preview['success'] ) ) {
            return $preview;
        }

        $data = isset( $preview['action']['data'] ) ? $preview['action']['data'] : array();

        return array(
            'ok'       => true,
            'terms'    => isset( $data['terms'] ) ? $data['terms'] : array(),
            'total'    => isset( $data['total'] ) ? intval( $data['total'] ) : 0,
            'page'     => isset( $data['page'] ) ? intval( $data['page'] ) : 1,
            'per_page' => isset( $data['per_page'] ) ? intval( $data['per_page'] ) : 20,
        );
    }

    /* --- get_tag --- */

    private function preview_get_tag( $args ) {
        return $this->preview_get_term( $args, 'post_tag', 'get_tag' );
    }

    public function execute_get_tag( $args ) {
        $preview = $this->preview_get_tag( $args );
        if ( empty( $preview['success'] ) ) {
            return $preview;
        }

        return array(
            'ok'   => true,
            'term' => isset( $preview['action']['data']['term'] ) ? $preview['action']['data']['term'] : array(),
        );
    }

    /* --- create_tag --- */

    private function preview_create_tag( $args ) {
        return $this->preview_create_term( $args, 'post_tag', 'create_tag' );
    }

    private function validate_create_tag_action_args( $args ) {
        return $this->validate_create_term_action_args( $args );
    }

    private function execute_create_tag_action( $action ) {
        return $this->execute_create_term_action( $action );
    }

    /* --- update_tag --- */

    private function preview_update_tag( $args ) {
        return $this->preview_update_term( $args, 'post_tag', 'update_tag' );
    }

    private function validate_update_tag_action_args( $args ) {
        return $this->validate_update_term_action_args( $args );
    }

    private function execute_update_tag_action( $action ) {
        return $this->execute_update_term_action( $action );
    }

    /* --- delete_tag --- */

    private function preview_delete_tag( $args ) {
        return $this->preview_delete_term( $args, 'post_tag', 'delete_tag' );
    }

    private function validate_delete_tag_action_args( $args ) {
        return $this->validate_delete_term_action_args( $args );
    }

    private function execute_delete_tag_action( $action ) {
        return $this->execute_delete_term_action( $action );
    }
}
