<?php
/**
 * Service Model
 *
 * Handles medical service operations and pricing management
 *
 * @package Care_API
 * @subpackage Models
 * @version    1.0.0
 * @author     Descomplicar® <dev@descomplicar.pt>
 * @link       https://descomplicar.pt
 * @since      1.0.0
 */

namespace Care_API\Models;

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

/**
 * Class Service
 *
 * Model for handling medical services, procedures and pricing
 *
 * @since 1.0.0
 */
class Service {

    /**
     * Database table name
     *
     * @var string
     */
    private static $table_name = 'kc_services';

    /**
     * Service ID
     *
     * @var int
     */
    public $id;

    /**
     * Service data
     *
     * @var array
     */
    private $data = array();

    /**
     * Required fields for service creation
     *
     * @var array
     */
    private static $required_fields = array(
        'name',
        'type',
        'price'
    );

    /**
     * Valid service types
     *
     * @var array
     */
    private static $valid_types = array(
        'consultation' => 'Consultation',
        'procedure' => 'Medical Procedure',
        'diagnostic' => 'Diagnostic Test',
        'therapy' => 'Therapy Session',
        'surgery' => 'Surgery',
        'vaccination' => 'Vaccination',
        'checkup' => 'Health Checkup',
        'other' => 'Other'
    );

    /**
     * Valid service statuses
     *
     * @var array
     */
    private static $valid_statuses = array(
        1 => 'active',
        0 => 'inactive'
    );

    /**
     * Constructor
     *
     * @param int|array $service_id_or_data Service ID or data array
     * @since 1.0.0
     */
    public function __construct( $service_id_or_data = null ) {
        if ( is_numeric( $service_id_or_data ) ) {
            $this->id = (int) $service_id_or_data;
            $this->load_data();
        } elseif ( is_array( $service_id_or_data ) ) {
            $this->data = $service_id_or_data;
            $this->id = isset( $this->data['id'] ) ? (int) $this->data['id'] : null;
        }
    }

    /**
     * Load service data from database
     *
     * @return bool True on success, false on failure
     * @since 1.0.0
     */
    private function load_data() {
        if ( ! $this->id ) {
            return false;
        }

        global $wpdb;
        $table = $wpdb->prefix . self::$table_name;

        $service_data = $wpdb->get_row(
            $wpdb->prepare(
                "SELECT * FROM {$table} WHERE id = %d",
                $this->id
            ),
            ARRAY_A
        );

        if ( $service_data ) {
            $this->data = self::format_service_data( $service_data );
            return true;
        }

        return false;
    }

    /**
     * Create a new service
     *
     * @param array $service_data Service data
     * @return int|WP_Error Service ID on success, WP_Error on failure
     * @since 1.0.0
     */
    public static function create( $service_data ) {
        // Validate required fields
        $validation = self::validate_service_data( $service_data );
        if ( is_wp_error( $validation ) ) {
            return $validation;
        }

        global $wpdb;
        $table = $wpdb->prefix . self::$table_name;

        // Prepare data for insertion
        $insert_data = array(
            'name' => sanitize_text_field( $service_data['name'] ),
            'type' => sanitize_text_field( $service_data['type'] ),
            'price' => number_format( (float) $service_data['price'], 2, '.', '' ),
            'description' => isset( $service_data['description'] ) ? sanitize_textarea_field( $service_data['description'] ) : '',
            'duration' => isset( $service_data['duration'] ) ? (int) $service_data['duration'] : null,
            'status' => isset( $service_data['status'] ) ? (int) $service_data['status'] : 1,
            'created_at' => current_time( 'mysql' )
        );

        $result = $wpdb->insert( $table, $insert_data );

        if ( $result === false ) {
            return new \WP_Error(
                'service_creation_failed',
                'Failed to create service: ' . $wpdb->last_error,
                array( 'status' => 500 )
            );
        }

        return $wpdb->insert_id;
    }

    /**
     * Update service data
     *
     * @param int   $service_id Service ID
     * @param array $service_data Updated service data
     * @return bool|WP_Error True on success, WP_Error on failure
     * @since 1.0.0
     */
    public static function update( $service_id, $service_data ) {
        if ( ! self::exists( $service_id ) ) {
            return new \WP_Error(
                'service_not_found',
                'Service not found',
                array( 'status' => 404 )
            );
        }

        global $wpdb;
        $table = $wpdb->prefix . self::$table_name;

        // Prepare update data
        $update_data = array();
        $allowed_fields = array(
            'name', 'type', 'price', 'description', 'duration', 'status'
        );

        foreach ( $allowed_fields as $field ) {
            if ( isset( $service_data[ $field ] ) ) {
                $value = $service_data[ $field ];
                
                switch ( $field ) {
                    case 'price':
                        $update_data[ $field ] = number_format( (float) $value, 2, '.', '' );
                        break;
                    case 'duration':
                    case 'status':
                        $update_data[ $field ] = (int) $value;
                        break;
                    case 'description':
                        $update_data[ $field ] = sanitize_textarea_field( $value );
                        break;
                    case 'type':
                        if ( array_key_exists( $value, self::$valid_types ) ) {
                            $update_data[ $field ] = sanitize_text_field( $value );
                        }
                        break;
                    default:
                        $update_data[ $field ] = sanitize_text_field( $value );
                }
            }
        }

        if ( empty( $update_data ) ) {
            return new \WP_Error(
                'no_data_to_update',
                'No valid data provided for update',
                array( 'status' => 400 )
            );
        }

        $result = $wpdb->update(
            $table,
            $update_data,
            array( 'id' => $service_id ),
            null,
            array( '%d' )
        );

        if ( $result === false ) {
            return new \WP_Error(
                'service_update_failed',
                'Failed to update service: ' . $wpdb->last_error,
                array( 'status' => 500 )
            );
        }

        return true;
    }

    /**
     * Delete a service
     *
     * @param int $service_id Service ID
     * @return bool|WP_Error True on success, WP_Error on failure
     * @since 1.0.0
     */
    public static function delete( $service_id ) {
        if ( ! self::exists( $service_id ) ) {
            return new \WP_Error(
                'service_not_found',
                'Service not found',
                array( 'status' => 404 )
            );
        }

        // Check if service is being used in appointments
        if ( self::has_dependencies( $service_id ) ) {
            return new \WP_Error(
                'service_has_dependencies',
                'Cannot delete service that is associated with appointments',
                array( 'status' => 409 )
            );
        }

        global $wpdb;
        $table = $wpdb->prefix . self::$table_name;

        $result = $wpdb->delete(
            $table,
            array( 'id' => $service_id ),
            array( '%d' )
        );

        if ( $result === false ) {
            return new \WP_Error(
                'service_deletion_failed',
                'Failed to delete service: ' . $wpdb->last_error,
                array( 'status' => 500 )
            );
        }

        return true;
    }

    /**
     * Get service by ID
     *
     * @param int $service_id Service ID
     * @return array|null Service data or null if not found
     * @since 1.0.0
     */
    public static function get_by_id( $service_id ) {
        global $wpdb;
        $table = $wpdb->prefix . self::$table_name;

        $service = $wpdb->get_row(
            $wpdb->prepare(
                "SELECT * FROM {$table} WHERE id = %d",
                $service_id
            ),
            ARRAY_A
        );

        if ( $service ) {
            return self::format_service_data( $service );
        }

        return null;
    }

    /**
     * Get all services with optional filtering
     *
     * @param array $args Query arguments
     * @return array Array of service data
     * @since 1.0.0
     */
    public static function get_all( $args = array() ) {
        global $wpdb;
        $table = $wpdb->prefix . self::$table_name;

        $defaults = array(
            'type' => null,
            'status' => 1, // Only active by default
            'search' => '',
            'limit' => 50,
            'offset' => 0,
            'orderby' => 'name',
            'order' => 'ASC'
        );

        $args = wp_parse_args( $args, $defaults );

        $where_clauses = array( '1=1' );
        $where_values = array();

        // Type filter
        if ( ! is_null( $args['type'] ) ) {
            $where_clauses[] = 'type = %s';
            $where_values[] = $args['type'];
        }

        // Status filter
        if ( ! is_null( $args['status'] ) ) {
            $where_clauses[] = 'status = %d';
            $where_values[] = $args['status'];
        }

        // Search filter
        if ( ! empty( $args['search'] ) ) {
            $where_clauses[] = '(name LIKE %s OR description LIKE %s)';
            $search_term = '%' . $wpdb->esc_like( $args['search'] ) . '%';
            $where_values[] = $search_term;
            $where_values[] = $search_term;
        }

        $where_sql = implode( ' AND ', $where_clauses );

        // Whitelist for orderby
        $allowed_orderby = array( 'name', 'type', 'price', 'status', 'created_at' );
        $orderby = in_array( $args['orderby'], $allowed_orderby, true ) ? $args['orderby'] : 'name';

        // Whitelist for order
        $order = in_array( strtoupper( $args['order'] ), array( 'ASC', 'DESC' ), true ) ? strtoupper( $args['order'] ) : 'ASC';

        // Build query
        $query = "SELECT * FROM {$table} WHERE {$where_sql} ORDER BY {$orderby} {$order} LIMIT %d OFFSET %d";

        $safe_query = $wpdb->prepare(
            $query,
            array_merge(
                $where_values,
                array( $args['limit'], $args['offset'] )
            )
        );

        $services = $wpdb->get_results( $safe_query, ARRAY_A );

        return array_map( array( self::class, 'format_service_data' ), $services );
    }

    /**
     * Get services by type
     *
     * @param string $type Service type
     * @param array  $args Additional query arguments
     * @return array Array of services
     * @since 1.0.0
     */
    public static function get_by_type( $type, $args = array() ) {
        $args['type'] = $type;
        return self::get_all( $args );
    }

    /**
     * Get most popular services
     *
     * @param array $args Query arguments
     * @return array Array of popular services with usage stats
     * @since 1.0.0
     */
    public static function get_popular_services( $args = array() ) {
        global $wpdb;

        $defaults = array(
            'limit' => 10,
            'date_from' => null,
            'date_to' => null,
            'clinic_id' => null
        );

        $args = wp_parse_args( $args, $defaults );

        $where_clauses = array( 's.status = 1' );
        $where_values = array();

        // Date range filters
        if ( ! is_null( $args['date_from'] ) ) {
            $where_clauses[] = 'a.created_at >= %s';
            $where_values[] = $args['date_from'] . ' 00:00:00';
        }

        if ( ! is_null( $args['date_to'] ) ) {
            $where_clauses[] = 'a.created_at <= %s';
            $where_values[] = $args['date_to'] . ' 23:59:59';
        }

        // Clinic filter
        if ( ! is_null( $args['clinic_id'] ) ) {
            $where_clauses[] = 'a.clinic_id = %d';
            $where_values[] = $args['clinic_id'];
        }

        $where_sql = implode( ' AND ', $where_clauses );

        $query = "SELECT s.*, 
                         COUNT(asm.service_id) as usage_count,
                         MAX(a.created_at) as last_used
                  FROM {$wpdb->prefix}kc_services s
                  LEFT JOIN {$wpdb->prefix}kc_appointment_service_mapping asm ON s.id = asm.service_id
                  LEFT JOIN {$wpdb->prefix}kc_appointments a ON asm.appointment_id = a.id
                  WHERE {$where_sql}
                  GROUP BY s.id
                  ORDER BY usage_count DESC, s.name ASC
                  LIMIT %d";

        $where_values[] = $args['limit'];

        $services = $wpdb->get_results(
            $wpdb->prepare( $query, $where_values ),
            ARRAY_A
        );

        return array_map( function( $service ) {
            $formatted = self::format_service_data( $service );
            $formatted['usage_count'] = (int) $service['usage_count'];
            $formatted['last_used'] = $service['last_used'];
            return $formatted;
        }, $services );
    }

    /**
     * Get service pricing tiers (if implemented)
     *
     * @param int $service_id Service ID
     * @return array Service pricing information
     * @since 1.0.0
     */
    public static function get_pricing( $service_id ) {
        $service = self::get_by_id( $service_id );
        
        if ( ! $service ) {
            return null;
        }

        // Basic pricing - could be extended for complex pricing models
        return array(
            'service_id' => $service['id'],
            'base_price' => $service['price'],
            'currency' => 'EUR', // Could be configurable
            'pricing_type' => 'fixed', // fixed, variable, tiered, etc.
            'includes_consultation' => $service['type'] === 'consultation'
        );
    }

    /**
     * Search services (for autocomplete)
     *
     * @param string $search_term Search term
     * @param int    $limit       Limit results
     * @return array Array of service names
     * @since 1.0.0
     */
    public static function search_services( $search_term, $limit = 20 ) {
        global $wpdb;
        $table = $wpdb->prefix . self::$table_name;

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

        $services = $wpdb->get_results(
            $wpdb->prepare(
                "SELECT id, name, type, price
                 FROM {$table} 
                 WHERE status = 1 AND (name LIKE %s OR description LIKE %s)
                 ORDER BY name ASC 
                 LIMIT %d",
                '%' . $wpdb->esc_like( $search_term ) . '%',
                '%' . $wpdb->esc_like( $search_term ) . '%',
                $limit
            ),
            ARRAY_A
        );

        return array_map( function( $service ) {
            return array(
                'id' => (int) $service['id'],
                'name' => $service['name'],
                'type' => $service['type'],
                'type_text' => self::$valid_types[ $service['type'] ] ?? 'Unknown',
                'price' => (float) $service['price']
            );
        }, $services );
    }

    /**
     * Check if service exists
     *
     * @param int $service_id Service ID
     * @return bool True if exists, false otherwise
     * @since 1.0.0
     */
    public static function exists( $service_id ) {
        global $wpdb;
        $table = $wpdb->prefix . self::$table_name;

        $count = $wpdb->get_var(
            $wpdb->prepare(
                "SELECT COUNT(*) FROM {$table} WHERE id = %d",
                $service_id
            )
        );

        return (int) $count > 0;
    }

    /**
     * Check if service has dependencies (appointments)
     *
     * @param int $service_id Service ID
     * @return bool True if has dependencies, false otherwise
     * @since 1.0.0
     */
    private static function has_dependencies( $service_id ) {
        global $wpdb;

        // Check appointment service mappings
        $mappings_count = $wpdb->get_var(
            $wpdb->prepare(
                "SELECT COUNT(*) FROM {$wpdb->prefix}kc_appointment_service_mapping WHERE service_id = %d",
                $service_id
            )
        );

        return (int) $mappings_count > 0;
    }

    /**
     * Validate service data
     *
     * @param array $service_data Service data to validate
     * @return bool|WP_Error True if valid, WP_Error if invalid
     * @since 1.0.0
     */
    private static function validate_service_data( $service_data ) {
        $errors = array();

        // Check required fields
        foreach ( self::$required_fields as $field ) {
            if ( empty( $service_data[ $field ] ) ) {
                $errors[] = "Field '{$field}' is required";
            }
        }

        // Validate price
        if ( isset( $service_data['price'] ) ) {
            if ( ! is_numeric( $service_data['price'] ) || (float) $service_data['price'] < 0 ) {
                $errors[] = 'Price must be a non-negative number';
            }
        }

        // Validate type
        if ( isset( $service_data['type'] ) && 
             ! array_key_exists( $service_data['type'], self::$valid_types ) ) {
            $errors[] = 'Invalid service type';
        }

        // Validate duration
        if ( isset( $service_data['duration'] ) ) {
            if ( ! is_numeric( $service_data['duration'] ) || (int) $service_data['duration'] <= 0 ) {
                $errors[] = 'Duration must be a positive number (in minutes)';
            }
        }

        // Validate status
        if ( isset( $service_data['status'] ) && 
             ! array_key_exists( $service_data['status'], self::$valid_statuses ) ) {
            $errors[] = 'Invalid status value';
        }

        // Check for duplicate service name
        if ( ! empty( $service_data['name'] ) ) {
            global $wpdb;
            $table = $wpdb->prefix . self::$table_name;

            $existing_service = $wpdb->get_var(
                $wpdb->prepare(
                    "SELECT id FROM {$table} WHERE name = %s",
                    $service_data['name']
                )
            );

            if ( $existing_service ) {
                $errors[] = 'A service with this name already exists';
            }
        }

        if ( ! empty( $errors ) ) {
            return new \WP_Error(
                'service_validation_failed',
                'Service validation failed',
                array( 
                    'status' => 400,
                    'errors' => $errors 
                )
            );
        }

        return true;
    }

    /**
     * Format service data for API response
     *
     * @param array $service_data Raw service data
     * @return array Formatted service data
     * @since 1.0.0
     */
    private static function format_service_data( $service_data ) {
        if ( ! $service_data ) {
            return null;
        }

        $formatted = array(
            'id' => (int) $service_data['id'],
            'name' => $service_data['name'],
            'type' => $service_data['type'],
            'type_text' => self::$valid_types[ $service_data['type'] ] ?? 'Unknown',
            'price' => (float) $service_data['price'],
            'description' => $service_data['description'] ?? '',
            'duration' => isset( $service_data['duration'] ) ? (int) $service_data['duration'] : null,
            'status' => (int) $service_data['status'],
            'status_text' => self::$valid_statuses[ $service_data['status'] ] ?? 'unknown',
            'created_at' => $service_data['created_at']
        );

        return $formatted;
    }

    /**
     * Get service statistics
     *
     * @param array $filters Optional filters
     * @return array Service statistics
     * @since 1.0.0
     */
    public static function get_statistics( $filters = array() ) {
        global $wpdb;
        $table = $wpdb->prefix . self::$table_name;

        $where_clauses = array( '1=1' );
        $where_values = array();

        if ( ! empty( $filters['type'] ) ) {
            $where_clauses[] = 'type = %s';
            $where_values[] = $filters['type'];
        }

        $where_sql = implode( ' AND ', $where_clauses );

        $stats = array(
            'total_services' => 0,
            'active_services' => 0,
            'inactive_services' => 0,
            'services_by_type' => array(),
            'average_price' => 0,
            'price_range' => array(
                'min' => 0,
                'max' => 0
            ),
            'most_expensive_service' => null,
            'least_expensive_service' => null
        );

        // Total services
        $query = "SELECT COUNT(*) FROM {$table} WHERE {$where_sql}";
        $stats['total_services'] = (int) $wpdb->get_var( $wpdb->prepare( $query, $where_values ) );

        // Services by status
        foreach ( array_keys( self::$valid_statuses ) as $status_id ) {
            $status_where_sql = $where_sql . ' AND status = %d';
            $query = "SELECT COUNT(*) FROM {$table} WHERE {$status_where_sql}";
            $count = (int) $wpdb->get_var( $wpdb->prepare( $query, array_merge( $where_values, array( $status_id ) ) ) );
            $stats[ self::$valid_statuses[ $status_id ] . '_services' ] = $count;
        }

        // Services by type
        foreach ( array_keys( self::$valid_types ) as $type ) {
            $type_where_sql = $where_sql . ' AND type = %s AND status = 1';
            $query = "SELECT COUNT(*) FROM {$table} WHERE {$type_where_sql}";
            $count = (int) $wpdb->get_var( $wpdb->prepare( $query, array_merge( $where_values, array( $type ) ) ) );
            if ( $count > 0 ) {
                $stats['services_by_type'][ $type ] = array(
                    'count' => $count,
                    'name' => self::$valid_types[ $type ]
                );
            }
        }

        // Price statistics (active services only)
        $price_where_sql = $where_sql . ' AND status = 1 AND price > 0';

        // Average price
        $query = "SELECT AVG(CAST(price AS DECIMAL(10,2))) FROM {$table} WHERE {$price_where_sql}";
        $stats['average_price'] = round( (float) $wpdb->get_var( $wpdb->prepare( $query, $where_values ) ) ?: 0, 2 );

        // Price range
        $query = "SELECT MIN(CAST(price AS DECIMAL(10,2))), MAX(CAST(price AS DECIMAL(10,2))) FROM {$table} WHERE {$price_where_sql}";
        $price_range = $wpdb->get_row( $wpdb->prepare( $query, $where_values ), ARRAY_N );
        if ( $price_range ) {
            $stats['price_range']['min'] = (float) $price_range[0] ?: 0;
            $stats['price_range']['max'] = (float) $price_range[1] ?: 0;
        }

        // Most and least expensive services
        if ( $stats['total_services'] > 0 ) {
            $query = "SELECT name, price FROM {$table} WHERE {$price_where_sql} ORDER BY CAST(price AS DECIMAL(10,2)) DESC LIMIT 1";
            $most_expensive = $wpdb->get_row( $wpdb->prepare( $query, $where_values ), ARRAY_A );
            if ( $most_expensive ) {
                $stats['most_expensive_service'] = array(
                    'name' => $most_expensive['name'],
                    'price' => (float) $most_expensive['price']
                );
            }

            $query = "SELECT name, price FROM {$table} WHERE {$price_where_sql} ORDER BY CAST(price AS DECIMAL(10,2)) ASC LIMIT 1";
            $least_expensive = $wpdb->get_row( $wpdb->prepare( $query, $where_values ), ARRAY_A );
            if ( $least_expensive ) {
                $stats['least_expensive_service'] = array(
                    'name' => $least_expensive['name'],
                    'price' => (float) $least_expensive['price']
                );
            }
        }

        return $stats;
    }

    /**
     * Get services by clinic
     *
     * @param int $clinic_id Clinic ID
     * @param array $args Query arguments
     * @return array Array of service data
     * @since 1.0.0
     */
    public static function get_by_clinic( $clinic_id, $args = array() ) {
        global $wpdb;
        $table = $wpdb->prefix . self::$table_name;

        $defaults = array(
            'status' => null,
            'category' => null,
            'search' => '',
            'price_min' => null,
            'price_max' => null,
            'limit' => 50,
            'offset' => 0,
            'orderby' => 'name',
            'order' => 'ASC'
        );

        $args = wp_parse_args( $args, $defaults );

        $where_clauses = array( 's.clinic_id = %d' );
        $where_values = array( $clinic_id );

        // Add filters
        if ( ! is_null( $args['status'] ) ) {
            $where_clauses[] = 's.status = %d';
            $where_values[] = $args['status'];
        }

        if ( ! empty( $args['category'] ) ) {
            $where_clauses[] = 's.category = %s';
            $where_values[] = $args['category'];
        }

        if ( ! empty( $args['search'] ) ) {
            $where_clauses[] = '(s.name LIKE %s OR s.description LIKE %s)';
            $search_term = '%' . $wpdb->esc_like( $args['search'] ) . '%';
            $where_values[] = $search_term;
            $where_values[] = $search_term;
        }

        if ( ! is_null( $args['price_min'] ) ) {
            $where_clauses[] = 'CAST(s.price AS DECIMAL(10,2)) >= %f';
            $where_values[] = (float) $args['price_min'];
        }

        if ( ! is_null( $args['price_max'] ) ) {
            $where_clauses[] = 'CAST(s.price AS DECIMAL(10,2)) <= %f';
            $where_values[] = (float) $args['price_max'];
        }

        $where_sql = implode( ' AND ', $where_clauses );

        // Whitelist for orderby
        $allowed_orderby = array( 'name', 'price', 'category', 'status', 'times_billed', 'total_revenue' );
        $orderby = in_array( $args['orderby'], $allowed_orderby, true ) ? 's.' . $args['orderby'] : 's.name';

        // Whitelist for order
        $order = in_array( strtoupper( $args['order'] ), array( 'ASC', 'DESC' ), true ) ? strtoupper( $args['order'] ) : 'ASC';

        $query = $wpdb->prepare(
            "SELECT s.*, 
                    c.name as clinic_name,
                    COUNT(b.id) as times_billed,
                    SUM(CASE WHEN b.payment_status = 'paid' THEN CAST(b.actual_amount AS DECIMAL(10,2)) ELSE 0 END) as total_revenue
             FROM {$table} s
             LEFT JOIN {$wpdb->prefix}kc_clinics c ON s.clinic_id = c.id
             LEFT JOIN {$wpdb->prefix}kc_bills b ON FIND_IN_SET(s.id, b.service_id)
             WHERE {$where_sql}
             GROUP BY s.id
             ORDER BY {$orderby} {$order}
             LIMIT %d OFFSET %d",
            array_merge( $where_values, array( $args['limit'], $args['offset'] ) )
        );

        $services = $wpdb->get_results( $query, ARRAY_A );

        return array_map( function( $service ) {
            return array(
                'id' => (int) $service['id'],
                'name' => $service['name'],
                'description' => $service['description'],
                'price' => (float) $service['price'],
                'currency' => $service['currency'],
                'category' => $service['category'],
                'duration_minutes' => (int) $service['duration_minutes'],
                'status' => (int) $service['status'],
                'clinic' => array(
                    'id' => (int) $service['clinic_id'],
                    'name' => $service['clinic_name']
                ),
                'usage_stats' => array(
                    'times_billed' => (int) $service['times_billed'],
                    'total_revenue' => (float) $service['total_revenue'] ?: 0
                ),
                'charges' => isset( $service['charges'] ) && ! empty( $service['charges'] ) ? 
                    json_decode( $service['charges'], true ) : array(),
                'image' => $service['image'],
                'created_at' => $service['created_at']
            );
        }, $services );
    }
}