<?php
/**
 * Prescription Model
 *
 * Handles prescription operations and medication 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 Prescription
 *
 * Model for handling patient prescriptions and medication management
 *
 * @since 1.0.0
 */
class Prescription {

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

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

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

    /**
     * Required fields for prescription creation
     *
     * @var array
     */
    private static $required_fields = array(
        'encounter_id',
        'patient_id',
        'name',
        'frequency',
        'duration'
    );

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

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

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

        return false;
    }

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

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

        // Prepare data for insertion
        $insert_data = array(
            'encounter_id' => (int) $prescription_data['encounter_id'],
            'patient_id' => (int) $prescription_data['patient_id'],
            'name' => sanitize_textarea_field( $prescription_data['name'] ),
            'frequency' => sanitize_text_field( $prescription_data['frequency'] ),
            'duration' => sanitize_text_field( $prescription_data['duration'] ),
            'instruction' => isset( $prescription_data['instruction'] ) ? sanitize_textarea_field( $prescription_data['instruction'] ) : '',
            'added_by' => get_current_user_id(),
            'created_at' => current_time( 'mysql' ),
            'is_from_template' => isset( $prescription_data['is_from_template'] ) ? (int) $prescription_data['is_from_template'] : 0
        );

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

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

        return $wpdb->insert_id;
    }

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

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

        // Prepare update data
        $update_data = array();
        $allowed_fields = array(
            'name', 'frequency', 'duration', 'instruction', 'is_from_template'
        );

        foreach ( $allowed_fields as $field ) {
            if ( isset( $prescription_data[ $field ] ) ) {
                $value = $prescription_data[ $field ];
                
                switch ( $field ) {
                    case 'name':
                    case 'instruction':
                        $update_data[ $field ] = sanitize_textarea_field( $value );
                        break;
                    case 'is_from_template':
                        $update_data[ $field ] = (int) $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' => $prescription_id ),
            null,
            array( '%d' )
        );

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

        return true;
    }

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

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

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

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

        return true;
    }

    /**
     * Get prescription by ID
     *
     * @param int $prescription_id Prescription ID
     * @return array|null Prescription data or null if not found
     * @since 1.0.0
     */
    public static function get_by_id( $prescription_id ) {
        return self::get_prescription_full_data( $prescription_id );
    }

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

        $defaults = array(
            'encounter_id' => null,
            'patient_id' => 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();

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

        // Patient filter
        if ( ! is_null( $args['patient_id'] ) ) {
            $where_clauses[] = 'p.patient_id = %d';
            $where_values[] = $args['patient_id'];
        }

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

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

        // Whitelist for orderby
        $allowed_orderby = array( 'created_at', 'patient_name', 'name', 'frequency', 'duration' );
        $orderby = in_array( $args['orderby'], $allowed_orderby, true ) ? 'p.' . $args['orderby'] : 'p.created_at';

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

        // Build query
        $query = "SELECT p.*, 
                         CONCAT(pt.first_name, ' ', pt.last_name) as patient_name,
                         pt.user_email as patient_email,
                         CONCAT(ab.first_name, ' ', ab.last_name) as added_by_name,
                         e.encounter_date
                  FROM {$table} p
                  LEFT JOIN {$wpdb->prefix}users pt ON p.patient_id = pt.ID
                  LEFT JOIN {$wpdb->prefix}users ab ON p.added_by = ab.ID
                  LEFT JOIN {$wpdb->prefix}kc_patient_encounters e ON p.encounter_id = e.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'] )
            )
        );

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

        return array_map( array( self::class, 'format_prescription_data' ), $prescriptions );
    }

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

        $prescription = $wpdb->get_row(
            $wpdb->prepare(
                "SELECT p.*, 
                        CONCAT(pt.first_name, ' ', pt.last_name) as patient_name,
                        pt.user_email as patient_email,
                        CONCAT(ab.first_name, ' ', ab.last_name) as added_by_name,
                        e.encounter_date,
                        CONCAT(d.first_name, ' ', d.last_name) as doctor_name
                 FROM {$table} p
                 LEFT JOIN {$wpdb->prefix}users pt ON p.patient_id = pt.ID
                 LEFT JOIN {$wpdb->prefix}users ab ON p.added_by = ab.ID
                 LEFT JOIN {$wpdb->prefix}kc_patient_encounters e ON p.encounter_id = e.id
                 LEFT JOIN {$wpdb->prefix}users d ON e.doctor_id = d.ID
                 WHERE p.id = %d",
                $prescription_id
            ),
            ARRAY_A
        );

        if ( ! $prescription ) {
            return null;
        }

        return self::format_prescription_data( $prescription );
    }

    /**
     * Get prescriptions by encounter
     *
     * @param int $encounter_id Encounter ID
     * @return array Array of prescriptions
     * @since 1.0.0
     */
    public static function get_by_encounter( $encounter_id ) {
        return self::get_all( array( 'encounter_id' => $encounter_id ) );
    }

    /**
     * Get prescriptions by patient
     *
     * @param int   $patient_id Patient ID
     * @param array $args       Additional query arguments
     * @return array Array of prescriptions
     * @since 1.0.0
     */
    public static function get_by_patient( $patient_id, $args = array() ) {
        $args['patient_id'] = $patient_id;
        return self::get_all( $args );
    }

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

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

        $medications = $wpdb->get_results(
            $wpdb->prepare(
                "SELECT DISTINCT name, COUNT(*) as usage_count
                 FROM {$table} 
                 WHERE name LIKE %s 
                 GROUP BY name 
                 ORDER BY usage_count DESC, name ASC 
                 LIMIT %d",
                '%' . $wpdb->esc_like( $search_term ) . '%',
                $limit
            ),
            ARRAY_A
        );

        return array_map( function( $med ) {
            return array(
                'name' => $med['name'],
                'usage_count' => (int) $med['usage_count']
            );
        }, $medications );
    }

    /**
     * Get most prescribed medications
     *
     * @param array $args Query arguments
     * @return array Array of top medications
     * @since 1.0.0
     */
    public static function get_top_medications( $args = array() ) {
        global $wpdb;
        $table = $wpdb->prefix . self::$table_name;

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

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

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

        $join = '';
        // Doctor filter (through encounter)
        if ( ! is_null( $args['doctor_id'] ) ) {
            $join = " LEFT JOIN {$wpdb->prefix}kc_patient_encounters e ON p.encounter_id = e.id";
            $where_clauses[] = 'e.doctor_id = %d';
            $where_values[] = $args['doctor_id'];
        }

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

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

        // Patient filter
        if ( ! is_null( $args['patient_id'] ) ) {
            $where_clauses[] = 'p.patient_id = %d';
            $where_values[] = $args['patient_id'];
        }

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

        $query = "SELECT p.name, 
                         COUNT(*) as prescription_count,
                         COUNT(DISTINCT p.patient_id) as unique_patients,
                         MAX(p.created_at) as last_prescribed
                  FROM {$table} p
                  {$join}
                  WHERE {$where_sql}
                   GROUP BY p.name 
                   ORDER BY prescription_count DESC 
                   LIMIT %d";

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

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

        return array_map( function( $med ) {
            return array(
                'name' => $med['name'],
                'prescription_count' => (int) $med['prescription_count'],
                'unique_patients' => (int) $med['unique_patients'],
                'last_prescribed' => $med['last_prescribed']
            );
        }, $medications );
    }

    /**
     * Get prescription templates
     *
     * @param array $args Query arguments
     * @return array Array of prescription templates
     * @since 1.0.0
     */
    public static function get_templates( $args = array() ) {
        global $wpdb;
        $table = $wpdb->prefix . self::$table_name;

        $defaults = array(
            'limit' => 50,
            'doctor_id' => null
        );

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

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

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

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

        $query = "SELECT name, frequency, duration, instruction,
                         COUNT(*) as usage_count,
                         MAX(created_at) as last_used
                  FROM {$table} 
                  WHERE {$where_sql}
                  GROUP BY name, frequency, duration, instruction 
                  ORDER BY usage_count DESC, last_used DESC
                  LIMIT %d";

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

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

        return array_map( function( $template ) {
            return array(
                'name' => $template['name'],
                'frequency' => $template['frequency'],
                'duration' => $template['duration'],
                'instruction' => $template['instruction'],
                'usage_count' => (int) $template['usage_count'],
                'last_used' => $template['last_used']
            );
        }, $templates );
    }

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

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

        return (int) $count > 0;
    }

    /**
     * Validate prescription data
     *
     * @param array $prescription_data Prescription data to validate
     * @return bool|WP_Error True if valid, WP_Error if invalid
     * @since 1.0.0
     */
    private static function validate_prescription_data( $prescription_data ) {
        $errors = array();

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

        // Validate entities exist
        if ( ! empty( $prescription_data['encounter_id'] ) && 
             ! Encounter::exists( $prescription_data['encounter_id'] ) ) {
            $errors[] = 'Encounter not found';
        }

        if ( ! empty( $prescription_data['patient_id'] ) && 
             ! Patient::exists( $prescription_data['patient_id'] ) ) {
            $errors[] = 'Patient not found';
        }

        // Validate medication name
        if ( ! empty( $prescription_data['name'] ) ) {
            if ( strlen( $prescription_data['name'] ) > 500 ) {
                $errors[] = 'Medication name is too long (max 500 characters)';
            }
        }

        // Validate frequency format
        if ( ! empty( $prescription_data['frequency'] ) ) {
            if ( strlen( $prescription_data['frequency'] ) > 199 ) {
                $errors[] = 'Frequency is too long (max 199 characters)';
            }
        }

        // Validate duration format
        if ( ! empty( $prescription_data['duration'] ) ) {
            if ( strlen( $prescription_data['duration'] ) > 199 ) {
                $errors[] = 'Duration is too long (max 199 characters)';
            }
        }

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

        return true;
    }

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

        $formatted = array(
            'id' => (int) $prescription_data['id'],
            'name' => $prescription_data['name'],
            'frequency' => $prescription_data['frequency'],
            'duration' => $prescription_data['duration'],
            'instruction' => $prescription_data['instruction'],
            'is_from_template' => (bool) $prescription_data['is_from_template'],
            'created_at' => $prescription_data['created_at'],
            'encounter' => array(
                'id' => (int) $prescription_data['encounter_id'],
                'encounter_date' => $prescription_data['encounter_date'] ?? null
            ),
            'patient' => array(
                'id' => (int) $prescription_data['patient_id'],
                'name' => $prescription_data['patient_name'] ?? '',
                'email' => $prescription_data['patient_email'] ?? ''
            ),
            'added_by' => array(
                'id' => (int) $prescription_data['added_by'],
                'name' => $prescription_data['added_by_name'] ?? ''
            ),
            'doctor_name' => $prescription_data['doctor_name'] ?? ''
        );

        return $formatted;
    }

    /**
     * Get prescription statistics
     *
     * @param array $filters Optional filters
     * @return array Prescription 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['patient_id'] ) ) {
            $where_clauses[] = 'patient_id = %d';
            $where_values[] = $filters['patient_id'];
        }

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

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

        $stats = array(
            'total_prescriptions' => 0,
            'unique_medications' => 0,
            'prescriptions_today' => 0,
            'prescriptions_this_week' => 0,
            'prescriptions_this_month' => 0,
            'template_prescriptions' => 0,
            'avg_prescriptions_per_encounter' => 0
        );

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

        // Unique medications
        $query = "SELECT COUNT(DISTINCT name) FROM {$table} WHERE {$where_sql}";
        $stats['unique_medications'] = (int) $wpdb->get_var( $wpdb->prepare( $query, $where_values ) );

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

        // Prescriptions this week
        $week_where_sql = $where_sql . ' AND WEEK(created_at) = WEEK(CURDATE()) AND YEAR(created_at) = YEAR(CURDATE())';
        $query = "SELECT COUNT(*) FROM {$table} WHERE {$week_where_sql}";
        $stats['prescriptions_this_week'] = (int) $wpdb->get_var( $wpdb->prepare( $query, $where_values ) );

        // Prescriptions 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['prescriptions_this_month'] = (int) $wpdb->get_var( $wpdb->prepare( $query, $where_values ) );

        // Template prescriptions
        $template_where_sql = $where_sql . ' AND is_from_template = 1';
        $query = "SELECT COUNT(*) FROM {$table} WHERE {$template_where_sql}";
        $stats['template_prescriptions'] = (int) $wpdb->get_var( $wpdb->prepare( $query, $where_values ) );

        // Average prescriptions per encounter
        if ( $stats['total_prescriptions'] > 0 ) {
            $unique_encounters_query = "SELECT COUNT(DISTINCT encounter_id) FROM {$table} WHERE {$where_sql}";
            $unique_encounters = (int) $wpdb->get_var( $wpdb->prepare( $unique_encounters_query, $where_values ) );
            if ( $unique_encounters > 0 ) {
                $stats['avg_prescriptions_per_encounter'] = round( $stats['total_prescriptions'] / $unique_encounters, 2 );
            }
        }

        return $stats;
    }
}