<?php
/**
 * Bill Model
 *
 * Handles billing operations and payment 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 Bill
 *
 * Model for handling billing, invoices and payment management
 *
 * @since 1.0.0
 */
class Bill {

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

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

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

    /**
     * Required fields for bill creation
     *
     * @var array
     */
    private static $required_fields = array(
        'title',
        'total_amount',
        'clinic_id'
    );

    /**
     * Valid payment statuses
     *
     * @var array
     */
    private static $valid_payment_statuses = array(
        'pending' => 'Pending',
        'paid' => 'Paid',
        'partial' => 'Partially Paid',
        'overdue' => 'Overdue',
        'cancelled' => 'Cancelled'
    );

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

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

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

        $bill_data = self::get_bill_full_data( $this->id );
        
        if ( $bill_data ) {
            $this->data = $bill_data;
            return true;
        }

        return false;
    }

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

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

        // Calculate actual amount (total - discount)
        $total_amount = (float) $bill_data['total_amount'];
        $discount = isset( $bill_data['discount'] ) ? (float) $bill_data['discount'] : 0;
        $actual_amount = $total_amount - $discount;

        // Prepare data for insertion
        $insert_data = array(
            'encounter_id' => isset( $bill_data['encounter_id'] ) ? (int) $bill_data['encounter_id'] : null,
            'appointment_id' => isset( $bill_data['appointment_id'] ) ? (int) $bill_data['appointment_id'] : null,
            'title' => sanitize_text_field( $bill_data['title'] ),
            'total_amount' => number_format( $total_amount, 2, '.', '' ),
            'discount' => number_format( $discount, 2, '.', '' ),
            'actual_amount' => number_format( $actual_amount, 2, '.', '' ),
            'status' => isset( $bill_data['status'] ) ? (int) $bill_data['status'] : 1,
            'payment_status' => isset( $bill_data['payment_status'] ) ? sanitize_text_field( $bill_data['payment_status'] ) : 'pending',
            'clinic_id' => (int) $bill_data['clinic_id'],
            'created_at' => current_time( 'mysql' )
        );

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

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

        return $wpdb->insert_id;
    }

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

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

        // Prepare update data
        $update_data = array();
        $allowed_fields = array(
            'title', 'total_amount', 'discount', 'status', 'payment_status'
        );

        foreach ( $allowed_fields as $field ) {
            if ( isset( $bill_data[ $field ] ) ) {
                $value = $bill_data[ $field ];
                
                switch ( $field ) {
                    case 'total_amount':
                    case 'discount':
                        $update_data[ $field ] = number_format( (float) $value, 2, '.', '' );
                        break;
                    case 'status':
                        $update_data[ $field ] = (int) $value;
                        break;
                    case 'payment_status':
                        if ( in_array( $value, array_keys( self::$valid_payment_statuses ) ) ) {
                            $update_data[ $field ] = sanitize_text_field( $value );
                        }
                        break;
                    default:
                        $update_data[ $field ] = sanitize_text_field( $value );
                }
            }
        }

        // Recalculate actual amount if total or discount changed
        if ( isset( $update_data['total_amount'] ) || isset( $update_data['discount'] ) ) {
            $current_bill = self::get_by_id( $bill_id );
            
            $new_total = isset( $update_data['total_amount'] ) ? 
                (float) $update_data['total_amount'] : 
                (float) $current_bill['total_amount'];
                
            $new_discount = isset( $update_data['discount'] ) ? 
                (float) $update_data['discount'] : 
                (float) $current_bill['discount'];
                
            $update_data['actual_amount'] = number_format( $new_total - $new_discount, 2, '.', '' );
        }

        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' => $bill_id ),
            null,
            array( '%d' )
        );

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

        return true;
    }

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

        // Check if bill is paid - might want to prevent deletion
        $bill = self::get_by_id( $bill_id );
        if ( $bill['payment_status'] === 'paid' ) {
            return new \WP_Error(
                'cannot_delete_paid_bill',
                'Cannot delete a paid bill',
                array( 'status' => 409 )
            );
        }

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

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

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

        return true;
    }

    /**
     * Get bill by ID
     *
     * @param int $bill_id Bill ID
     * @return array|null Bill data or null if not found
     * @since 1.0.0
     */
    public static function get_by_id( $bill_id ) {
        return self::get_bill_full_data( $bill_id );
    }

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

        $defaults = array(
            'clinic_id' => null,
            'encounter_id' => null,
            'appointment_id' => null,
            'status' => null,
            'payment_status' => null,
            'date_from' => null,
            'date_to' => null,
            'search' => '',
            'limit' => 50,
            'offset' => 0,
            'orderby' => 'created_at',
            'order' => 'DESC'
        );

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

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

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

        // Encounter filter
        if ( ! is_null( $args['encounter_id'] ) ) {
            $where_clauses[] = 'b.encounter_id = %d';
            $where_values[] = $args['encounter_id'];
        }

        // Appointment filter
        if ( ! is_null( $args['appointment_id'] ) ) {
            $where_clauses[] = 'b.appointment_id = %d';
            $where_values[] = $args['appointment_id'];
        }

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

        // Payment status filter
        if ( ! is_null( $args['payment_status'] ) ) {
            $where_clauses[] = 'b.payment_status = %s';
            $where_values[] = $args['payment_status'];
        }

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

        if ( ! is_null( $args['date_to'] ) ) {
            $where_clauses[] = 'DATE(b.created_at) <= %s';
            $where_values[] = $args['date_to'];
        }

        // Search filter
        if ( ! empty( $args['search'] ) ) {
            $where_clauses[] = '(b.title LIKE %s OR p.first_name LIKE %s OR p.last_name LIKE %s OR c.name LIKE %s)';
            $search_term = '%' . $wpdb->esc_like( $args['search'] ) . '%';
            $where_values = array_merge( $where_values, array_fill( 0, 4, $search_term ) );
        }

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

        // Whitelist for orderby
        $allowed_orderby = array( 'created_at', 'patient_name', 'clinic_name', 'total_amount', 'payment_status' );
        $orderby = in_array( $args['orderby'], $allowed_orderby, true ) ? $args['orderby'] : 'created_at';
        
        // Whitelist for order
        $order = in_array( strtoupper( $args['order'] ), array( 'ASC', 'DESC' ), true ) ? strtoupper( $args['order'] ) : 'DESC';

        // Build query
        $query = "SELECT b.*, 
                         c.name as clinic_name,
                         CONCAT(p.first_name, ' ', p.last_name) as patient_name,
                         p.user_email as patient_email,
                         e.encounter_date,
                         a.appointment_start_date
                  FROM {$table} b
                  LEFT JOIN {$wpdb->prefix}kc_clinics c ON b.clinic_id = c.id
                  LEFT JOIN {$wpdb->prefix}kc_patient_encounters e ON b.encounter_id = e.id
                  LEFT JOIN {$wpdb->prefix}users p ON e.patient_id = p.ID
                  LEFT JOIN {$wpdb->prefix}kc_appointments a ON b.appointment_id = a.id
                  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'] )
            )
        );

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

        return array_map( array( self::class, 'format_bill_data' ), $bills );
    }

    /**
     * Get bill full data with related entities
     *
     * @param int $bill_id Bill ID
     * @return array|null Full bill data or null if not found
     * @since 1.0.0
     */
    public static function get_bill_full_data( $bill_id ) {
        global $wpdb;
        $table = $wpdb->prefix . self::$table_name;

        $bill = $wpdb->get_row(
            $wpdb->prepare(
                "SELECT b.*, 
                        c.name as clinic_name, c.address as clinic_address,
                        CONCAT(p.first_name, ' ', p.last_name) as patient_name,
                        p.user_email as patient_email,
                        e.encounter_date,
                        CONCAT(d.first_name, ' ', d.last_name) as doctor_name,
                        a.appointment_start_date, a.appointment_start_time
                 FROM {$table} b
                 LEFT JOIN {$wpdb->prefix}kc_clinics c ON b.clinic_id = c.id
                 LEFT JOIN {$wpdb->prefix}kc_patient_encounters e ON b.encounter_id = e.id
                 LEFT JOIN {$wpdb->prefix}users p ON e.patient_id = p.ID
                 LEFT JOIN {$wpdb->prefix}users d ON e.doctor_id = d.ID
                 LEFT JOIN {$wpdb->prefix}kc_appointments a ON b.appointment_id = a.id
                 WHERE b.id = %d",
                $bill_id
            ),
            ARRAY_A
        );

        if ( ! $bill ) {
            return null;
        }

        return self::format_bill_data( $bill );
    }

    /**
     * Process payment for bill
     *
     * @param int   $bill_id Bill ID
     * @param array $payment_data Payment information
     * @return bool|WP_Error True on success, WP_Error on failure
     * @since 1.0.0
     */
    public static function process_payment( $bill_id, $payment_data ) {
        if ( ! self::exists( $bill_id ) ) {
            return new \WP_Error(
                'bill_not_found',
                'Bill not found',
                array( 'status' => 404 )
            );
        }

        $bill = self::get_by_id( $bill_id );

        // Validate payment amount
        $payment_amount = (float) $payment_data['amount'];
        $bill_amount = (float) $bill['actual_amount'];

        if ( $payment_amount <= 0 ) {
            return new \WP_Error(
                'invalid_payment_amount',
                'Payment amount must be greater than zero',
                array( 'status' => 400 )
            );
        }

        if ( $payment_amount > $bill_amount ) {
            return new \WP_Error(
                'payment_exceeds_bill',
                'Payment amount exceeds bill amount',
                array( 'status' => 400 )
            );
        }

        // Determine new payment status
        $new_status = 'paid';
        if ( $payment_amount < $bill_amount ) {
            $new_status = 'partial';
        }

        // Update bill payment status
        $result = self::update( $bill_id, array(
            'payment_status' => $new_status
        ) );

        if ( is_wp_error( $result ) ) {
            return $result;
        }

        // Log payment (could be extended to create payment records)
        do_action( 'kivicare_payment_processed', $bill_id, $payment_data );

        return true;
    }

    /**
     * Get overdue bills
     *
     * @param array $args Query arguments
     * @return array Array of overdue bills
     * @since 1.0.0
     */
    public static function get_overdue_bills( $args = array() ) {
        $defaults = array(
            'days_overdue' => 30,
            'clinic_id' => null,
            'limit' => 50,
            'offset' => 0
        );

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

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

        $where_clauses = array(
            'payment_status IN ("pending", "partial")',
            'status = 1',
            'DATEDIFF(CURDATE(), created_at) > %d'
        );
        $where_values = array( $args['days_overdue'] );

        if ( ! is_null( $args['clinic_id'] ) ) {
            $where_clauses[] = 'clinic_id = %d';
            $where_values[] = $args['clinic_id'];
        }

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

        $bills = $wpdb->get_results(
            $wpdb->prepare(
                "SELECT *, DATEDIFF(CURDATE(), created_at) as days_overdue
                 FROM {$table} 
                 WHERE {$where_sql}
                 ORDER BY created_at ASC
                 LIMIT %d OFFSET %d",
                array_merge( $where_values, array( $args['limit'], $args['offset'] ) )
            ),
            ARRAY_A
        );

        // Update overdue bills status
        foreach ( $bills as &$bill ) {
            if ( $bill['payment_status'] !== 'overdue' ) {
                self::update( $bill['id'], array( 'payment_status' => 'overdue' ) );
                $bill['payment_status'] = 'overdue';
            }
        }

        return array_map( array( self::class, 'format_bill_data' ), $bills );
    }

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

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

        return (int) $count > 0;
    }

    /**
     * Validate bill data
     *
     * @param array $bill_data Bill data to validate
     * @return bool|WP_Error True if valid, WP_Error if invalid
     * @since 1.0.0
     */
    private static function validate_bill_data( $bill_data ) {
        $errors = array();

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

        // Validate amounts
        if ( isset( $bill_data['total_amount'] ) ) {
            if ( ! is_numeric( $bill_data['total_amount'] ) || (float) $bill_data['total_amount'] <= 0 ) {
                $errors[] = 'Total amount must be a positive number';
            }
        }

        if ( isset( $bill_data['discount'] ) ) {
            if ( ! is_numeric( $bill_data['discount'] ) || (float) $bill_data['discount'] < 0 ) {
                $errors[] = 'Discount must be a non-negative number';
            }

            // Check if discount doesn't exceed total
            if ( isset( $bill_data['total_amount'] ) && 
                 (float) $bill_data['discount'] > (float) $bill_data['total_amount'] ) {
                $errors[] = 'Discount cannot exceed total amount';
            }
        }

        // Validate payment status
        if ( isset( $bill_data['payment_status'] ) && 
             ! array_key_exists( $bill_data['payment_status'], self::$valid_payment_statuses ) ) {
            $errors[] = 'Invalid payment status';
        }

        // Validate entities exist
        if ( ! empty( $bill_data['clinic_id'] ) && 
             ! Clinic::exists( $bill_data['clinic_id'] ) ) {
            $errors[] = 'Clinic not found';
        }

        if ( ! empty( $bill_data['encounter_id'] ) && 
             ! Encounter::exists( $bill_data['encounter_id'] ) ) {
            $errors[] = 'Encounter not found';
        }

        if ( ! empty( $bill_data['appointment_id'] ) && 
             ! Appointment::exists( $bill_data['appointment_id'] ) ) {
            $errors[] = 'Appointment not found';
        }

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

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

        return true;
    }

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

        $formatted = array(
            'id' => (int) $bill_data['id'],
            'title' => $bill_data['title'],
            'total_amount' => (float) $bill_data['total_amount'],
            'discount' => (float) $bill_data['discount'],
            'actual_amount' => (float) $bill_data['actual_amount'],
            'status' => (int) $bill_data['status'],
            'status_text' => self::$valid_statuses[ $bill_data['status'] ] ?? 'unknown',
            'payment_status' => $bill_data['payment_status'],
            'payment_status_text' => self::$valid_payment_statuses[ $bill_data['payment_status'] ] ?? 'Unknown',
            'created_at' => $bill_data['created_at'],
            'clinic' => array(
                'id' => (int) $bill_data['clinic_id'],
                'name' => $bill_data['clinic_name'] ?? '',
                'address' => $bill_data['clinic_address'] ?? ''
            ),
            'encounter_id' => isset( $bill_data['encounter_id'] ) ? (int) $bill_data['encounter_id'] : null,
            'appointment_id' => isset( $bill_data['appointment_id'] ) ? (int) $bill_data['appointment_id'] : null,
            'patient' => array(
                'name' => $bill_data['patient_name'] ?? '',
                'email' => $bill_data['patient_email'] ?? ''
            ),
            'doctor_name' => $bill_data['doctor_name'] ?? '',
            'encounter_date' => $bill_data['encounter_date'] ?? null,
            'appointment_date' => $bill_data['appointment_start_date'] ?? null,
            'appointment_time' => $bill_data['appointment_start_time'] ?? null,
            'days_overdue' => isset( $bill_data['days_overdue'] ) ? (int) $bill_data['days_overdue'] : null
        );

        return $formatted;
    }

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

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

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

        if ( ! empty( $filters['date_from'] ) ) {
            $where_clauses[] = 'DATE(created_at) >= %s';
            $where_values[] = $filters['date_from'];
        }

        if ( ! empty( $filters['date_to'] ) ) {
            $where_clauses[] = 'DATE(created_at) <= %s';
            $where_values[] = $filters['date_to'];
        }

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

        $stats = array(
            'total_bills' => 0,
            'total_revenue' => 0,
            'pending_amount' => 0,
            'paid_amount' => 0,
            'overdue_amount' => 0,
            'bills_today' => 0,
            'bills_this_month' => 0,
            'average_bill_amount' => 0,
            'payment_status_breakdown' => array()
        );

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

        // Total revenue (actual amount)
        $query = "SELECT SUM(CAST(actual_amount AS DECIMAL(10,2))) FROM {$table} WHERE {$where_sql}";
        $stats['total_revenue'] = (float) $wpdb->get_var( $wpdb->prepare( $query, $where_values ) ) ?: 0;

        // Revenue by payment status
        foreach ( array_keys( self::$valid_payment_statuses ) as $status ) {
            $status_where_sql = $where_sql . ' AND payment_status = %s';
            $status_values = array_merge( $where_values, array( $status ) );
            
            $amount_query = "SELECT SUM(CAST(actual_amount AS DECIMAL(10,2))) FROM {$table} WHERE {$status_where_sql}";
            $count_query = "SELECT COUNT(*) FROM {$table} WHERE {$status_where_sql}";
            
            $amount = (float) $wpdb->get_var( $wpdb->prepare( $amount_query, $status_values ) ) ?: 0;
            $count = (int) $wpdb->get_var( $wpdb->prepare( $count_query, $status_values ) );
            
            $stats[ $status . '_amount' ] = $amount;
            $stats['payment_status_breakdown'][ $status ] = array(
                'count' => $count,
                'amount' => $amount
            );
        }

        // Bills today
        $today_where_sql = $where_sql . ' AND DATE(created_at) = CURDATE()';
        $query = "SELECT COUNT(*) FROM {$table} WHERE {$today_where_sql}";
        $stats['bills_today'] = (int) $wpdb->get_var( $wpdb->prepare( $query, $where_values ) );

        // Bills this month
        $month_where_sql = $where_sql . ' AND MONTH(created_at) = MONTH(CURDATE()) AND YEAR(created_at) = YEAR(CURDATE())';
        $query = "SELECT COUNT(*) FROM {$table} WHERE {$month_where_sql}";
        $stats['bills_this_month'] = (int) $wpdb->get_var( $wpdb->prepare( $query, $where_values ) );

        // Average bill amount
        if ( $stats['total_bills'] > 0 ) {
            $stats['average_bill_amount'] = round( $stats['total_revenue'] / $stats['total_bills'], 2 );
        }

        return $stats;
    }

    /**
     * Get bills by patient
     *
     * @param int $patient_id Patient user ID
     * @param array $args Query arguments
     * @return array Array of bill data
     * @since 1.0.0
     */
    public static function get_by_patient( $patient_id, $args = array() ) {
        global $wpdb;
        $table = $wpdb->prefix . self::$table_name;

        $defaults = array(
            'clinic_id' => null,
            'status' => null,
            'payment_status' => null,
            'date_from' => null,
            'date_to' => null,
            'limit' => 50,
            'offset' => 0,
            'orderby' => 'created_at',
            'order' => 'DESC'
        );

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

        $where_clauses = array( 'e.patient_id = %d' );
        $where_values = array( $patient_id );

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

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

        if ( ! is_null( $args['payment_status'] ) ) {
            $where_clauses[] = 'b.payment_status = %s';
            $where_values[] = $args['payment_status'];
        }

        if ( ! is_null( $args['date_from'] ) ) {
            $where_clauses[] = 'b.created_at >= %s';
            $where_values[] = $args['date_from'];
        }

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

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

        // Whitelist for orderby
        $allowed_orderby = array( 'created_at', 'total_amount', 'payment_status' );
        $orderby = in_array( $args['orderby'], $allowed_orderby, true ) ? 'b.' . $args['orderby'] : 'b.created_at';

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

        $query = $wpdb->prepare(
            "SELECT b.*, 
                    c.name as clinic_name,
                    CONCAT(pu.first_name, ' ', pu.last_name) as patient_name,
                    e.encounter_date,
                    a.appointment_start_date
             FROM {$table} b
             LEFT JOIN {$wpdb->prefix}kc_clinics c ON b.clinic_id = c.id
             LEFT JOIN {$wpdb->prefix}kc_patient_encounters e ON b.encounter_id = e.id
             LEFT JOIN {$wpdb->prefix}users pu ON e.patient_id = pu.ID
             LEFT JOIN {$wpdb->prefix}kc_appointments a ON b.appointment_id = a.id
             WHERE {$where_sql}
             ORDER BY {$orderby} {$order}
             LIMIT %d OFFSET %d",
            array_merge( $where_values, array( $args['limit'], $args['offset'] ) )
        );

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

        return array_map( array( self::class, 'format_bill_data' ), $bills );
    }

    /**
     * Get revenue statistics
     *
     * @param array $args Query arguments
     * @return array Revenue statistics
     * @since 1.0.0
     */
    public static function get_revenue_stats( $args = array() ) {
        global $wpdb;
        $table = $wpdb->prefix . self::$table_name;

        $defaults = array(
            'clinic_id' => null,
            'doctor_id' => null,
            'period' => 'month', // month, year, custom
            'date_from' => null,
            'date_to' => null
        );

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

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

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

        if ( ! is_null( $args['doctor_id'] ) ) {
            $where_clauses[] = 'doctor_id = %d';
            $where_values[] = $args['doctor_id'];
        }

        // Set date range based on period
        switch ( $args['period'] ) {
            case 'month':
                $where_clauses[] = 'MONTH(created_at) = MONTH(CURRENT_DATE())';
                $where_clauses[] = 'YEAR(created_at) = YEAR(CURRENT_DATE())';
                break;
            case 'year':
                $where_clauses[] = 'YEAR(created_at) = YEAR(CURRENT_DATE())';
                break;
            case 'custom':
                if ( ! is_null( $args['date_from'] ) ) {
                    $where_clauses[] = 'created_at >= %s';
                    $where_values[] = $args['date_from'];
                }
                if ( ! is_null( $args['date_to'] ) ) {
                    $where_clauses[] = 'created_at <= %s';
                    $where_values[] = $args['date_to'];
                }
                break;
        }

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

        // Revenue stats query
        $query = "SELECT 
                    COUNT(*) as total_bills,
                    SUM(CASE WHEN payment_status = 'paid' THEN CAST(actual_amount AS DECIMAL(10,2)) ELSE 0 END) as paid_revenue,
                    SUM(CASE WHEN payment_status = 'pending' THEN CAST(total_amount AS DECIMAL(10,2)) ELSE 0 END) as pending_revenue,
                    SUM(CASE WHEN payment_status = 'overdue' THEN CAST(total_amount AS DECIMAL(10,2)) ELSE 0 END) as overdue_revenue,
                    AVG(CAST(total_amount AS DECIMAL(10,2))) as average_bill_amount,
                    COUNT(DISTINCT patient_id) as unique_patients,
                    COUNT(DISTINCT clinic_id) as clinics_with_revenue
                  FROM {$table} 
                  WHERE {$where_sql}";

        $stats = $wpdb->get_row( $wpdb->prepare( $query, $where_values ), ARRAY_A );

        // Monthly breakdown for charts
        $monthly_query = "SELECT 
                            MONTH(created_at) as month,
                            YEAR(created_at) as year,
                            SUM(CASE WHEN payment_status = 'paid' THEN CAST(actual_amount AS DECIMAL(10,2)) ELSE 0 END) as monthly_revenue,
                            COUNT(*) as monthly_bills
                          FROM {$table} 
                          WHERE {$where_sql}
                          GROUP BY YEAR(created_at), MONTH(created_at)
                          ORDER BY year DESC, month DESC
                          LIMIT 12";

        $monthly_data = $wpdb->get_results( $wpdb->prepare( $monthly_query, $where_values ), ARRAY_A );

        // Format the results
        $revenue_stats = array(
            'summary' => array(
                'total_bills' => (int) $stats['total_bills'],
                'paid_revenue' => (float) $stats['paid_revenue'] ?: 0,
                'pending_revenue' => (float) $stats['pending_revenue'] ?: 0,
                'overdue_revenue' => (float) $stats['overdue_revenue'] ?: 0,
                'total_revenue' => (float) $stats['paid_revenue'] + (float) $stats['pending_revenue'] + (float) $stats['overdue_revenue'],
                'average_bill_amount' => round( (float) $stats['average_bill_amount'] ?: 0, 2 ),
                'unique_patients' => (int) $stats['unique_patients'],
                'clinics_with_revenue' => (int) $stats['clinics_with_revenue']
            ),
            'monthly_breakdown' => array_map( function( $item ) {
                return array(
                    'month' => (int) $item['month'],
                    'year' => (int) $item['year'],
                    'revenue' => (float) $item['monthly_revenue'],
                    'bills_count' => (int) $item['monthly_bills'],
                    'period' => date( 'M Y', mktime( 0, 0, 0, $item['month'], 1, $item['year'] ) )
                );
            }, $monthly_data ),
            'payment_status_distribution' => array(
                'paid_percentage' => $stats['paid_revenue'] > 0 ? 
                    round( ( $stats['paid_revenue'] / ( $stats['paid_revenue'] + $stats['pending_revenue'] + $stats['overdue_revenue'] ) ) * 100, 1 ) : 0,
                'pending_percentage' => $stats['pending_revenue'] > 0 ? 
                    round( ( $stats['pending_revenue'] / ( $stats['paid_revenue'] + $stats['pending_revenue'] + $stats['overdue_revenue'] ) ) * 100, 1 ) : 0,
                'overdue_percentage' => $stats['overdue_revenue'] > 0 ? 
                    round( ( $stats['overdue_revenue'] / ( $stats['paid_revenue'] + $stats['pending_revenue'] + $stats['overdue_revenue'] ) ) * 100, 1 ) : 0
            ),
            'period_info' => array(
                'period' => $args['period'],
                'date_from' => $args['date_from'],
                'date_to' => $args['date_to'],
                'generated_at' => current_time( 'mysql' )
            )
        );

        return $revenue_stats;
    }
}