<?php
/**
 * Response Standardization Service
 *
 * Provides consistent API response formatting across all endpoints
 *
 * @package Care_API
 * @subpackage Services
 * @version    1.0.0
 * @author     Descomplicar® <dev@descomplicar.pt>
 * @link       https://descomplicar.pt
 * @since      1.0.0
 */

namespace Care_API\Services;

use WP_REST_Response;
use WP_Error;

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

/**
 * Class Response_Standardization_Service
 *
 * Standardizes all API responses for consistency
 *
 * @since 1.0.0
 */
class Response_Standardization_Service {

    /**
     * API version
     *
     * @var string
     */
    private static $api_version = '1.0.0';

    /**
     * Response formats
     *
     * @var array
     */
    private static $formats = array(
        'json' => 'application/json',
        'xml' => 'application/xml',
        'csv' => 'text/csv'
    );

    /**
     * Initialize response standardization service
     *
     * @since 1.0.0
     */
    public static function init() {
        // Hook into REST API response formatting
        add_filter( 'rest_prepare_user', array( __CLASS__, 'standardize_user_response' ), 10, 3 );
        add_filter( 'rest_post_dispatch', array( __CLASS__, 'standardize_response_headers' ), 10, 3 );
    }

    /**
     * Create standardized success response
     *
     * @param mixed  $data         Response data
     * @param string $message      Success message
     * @param int    $status_code  HTTP status code
     * @param array  $meta         Additional metadata
     * @return WP_REST_Response Standardized response
     * @since 1.0.0
     */
    public static function success( $data = null, $message = 'Success', $status_code = 200, $meta = array() ) {
        $response_data = array(
            'success' => true,
            'message' => $message,
            'data' => $data,
            'meta' => array_merge( array(
                'timestamp' => current_time( 'Y-m-d\TH:i:s\Z' ),
                'api_version' => self::$api_version,
                'request_id' => self::generate_request_id()
            ), $meta )
        );

        // Add pagination if present
        if ( isset( $meta['pagination'] ) ) {
            $response_data['pagination'] = $meta['pagination'];
            unset( $response_data['meta']['pagination'] );
        }

        $response = new WP_REST_Response( $response_data, $status_code );
        self::add_standard_headers( $response );

        return $response;
    }

    /**
     * Create standardized error response
     *
     * @param string|WP_Error $error       Error message or WP_Error object
     * @param int             $status_code HTTP status code
     * @param array           $meta        Additional metadata
     * @return WP_REST_Response Standardized error response
     * @since 1.0.0
     */
    public static function error( $error, $status_code = 400, $meta = array() ) {
        $error_data = array();

        if ( is_wp_error( $error ) ) {
            $error_data = array(
                'code' => $error->get_error_code(),
                'message' => $error->get_error_message(),
                'details' => $error->get_error_data()
            );
        } elseif ( is_string( $error ) ) {
            $error_data = array(
                'code' => 'generic_error',
                'message' => $error,
                'details' => null
            );
        } elseif ( is_array( $error ) ) {
            $error_data = array_merge( array(
                'code' => 'validation_error',
                'message' => 'Validation failed',
                'details' => null
            ), $error );
        }

        $response_data = array(
            'success' => false,
            'error' => $error_data,
            'meta' => array_merge( array(
                'timestamp' => current_time( 'Y-m-d\TH:i:s\Z' ),
                'api_version' => self::$api_version,
                'request_id' => self::generate_request_id()
            ), $meta )
        );

        $response = new WP_REST_Response( $response_data, $status_code );
        self::add_standard_headers( $response );

        return $response;
    }

    /**
     * Create standardized list response with pagination
     *
     * @param array  $items       List items
     * @param int    $total       Total number of items
     * @param int    $page        Current page
     * @param int    $per_page    Items per page
     * @param string $message     Success message
     * @param array  $meta        Additional metadata
     * @return WP_REST_Response Standardized list response
     * @since 1.0.0
     */
    public static function list_response( $items, $total, $page, $per_page, $message = 'Items retrieved successfully', $meta = array() ) {
        $total_pages = ceil( $total / $per_page );
        
        $pagination = array(
            'current_page' => (int) $page,
            'per_page' => (int) $per_page,
            'total_items' => (int) $total,
            'total_pages' => (int) $total_pages,
            'has_next_page' => $page < $total_pages,
            'has_previous_page' => $page > 1,
            'next_page' => $page < $total_pages ? $page + 1 : null,
            'previous_page' => $page > 1 ? $page - 1 : null
        );

        return self::success( $items, $message, 200, array_merge( $meta, array(
            'pagination' => $pagination,
            'count' => count( $items )
        ) ) );
    }

    /**
     * Create standardized created response
     *
     * @param mixed  $data    Created resource data
     * @param string $message Success message
     * @param array  $meta    Additional metadata
     * @return WP_REST_Response Standardized created response
     * @since 1.0.0
     */
    public static function created( $data, $message = 'Resource created successfully', $meta = array() ) {
        return self::success( $data, $message, 201, $meta );
    }

    /**
     * Create standardized updated response
     *
     * @param mixed  $data    Updated resource data
     * @param string $message Success message
     * @param array  $meta    Additional metadata
     * @return WP_REST_Response Standardized updated response
     * @since 1.0.0
     */
    public static function updated( $data, $message = 'Resource updated successfully', $meta = array() ) {
        return self::success( $data, $message, 200, $meta );
    }

    /**
     * Create standardized deleted response
     *
     * @param string $message Success message
     * @param array  $meta    Additional metadata
     * @return WP_REST_Response Standardized deleted response
     * @since 1.0.0
     */
    public static function deleted( $message = 'Resource deleted successfully', $meta = array() ) {
        return self::success( null, $message, 200, $meta );
    }

    /**
     * Create standardized not found response
     *
     * @param string $resource Resource type
     * @param mixed  $identifier Resource identifier
     * @param array  $meta     Additional metadata
     * @return WP_REST_Response Standardized not found response
     * @since 1.0.0
     */
    public static function not_found( $resource = 'Resource', $identifier = null, $meta = array() ) {
        $message = $identifier 
            ? "{$resource} with ID {$identifier} not found"
            : "{$resource} not found";

        return self::error( array(
            'code' => 'resource_not_found',
            'message' => $message,
            'details' => array(
                'resource' => $resource,
                'identifier' => $identifier
            )
        ), 404, $meta );
    }

    /**
     * Create standardized validation error response
     *
     * @param array  $validation_errors Array of validation errors
     * @param string $message          Error message
     * @param array  $meta            Additional metadata
     * @return WP_REST_Response Standardized validation error response
     * @since 1.0.0
     */
    public static function validation_error( $validation_errors, $message = 'Validation failed', $meta = array() ) {
        return self::error( array(
            'code' => 'validation_failed',
            'message' => $message,
            'details' => array(
                'validation_errors' => $validation_errors,
                'error_count' => count( $validation_errors )
            )
        ), 400, $meta );
    }

    /**
     * Create standardized permission denied response
     *
     * @param string $action   Action attempted
     * @param string $resource Resource type
     * @param array  $meta     Additional metadata
     * @return WP_REST_Response Standardized permission denied response
     * @since 1.0.0
     */
    public static function permission_denied( $action = '', $resource = '', $meta = array() ) {
        $message = 'You do not have permission to perform this action';
        if ( $action && $resource ) {
            $message = "You do not have permission to {$action} {$resource}";
        }

        return self::error( array(
            'code' => 'insufficient_permissions',
            'message' => $message,
            'details' => array(
                'action' => $action,
                'resource' => $resource,
                'user_id' => get_current_user_id()
            )
        ), 403, $meta );
    }

    /**
     * Create standardized server error response
     *
     * @param string $message Error message
     * @param array  $meta    Additional metadata
     * @return WP_REST_Response Standardized server error response
     * @since 1.0.0
     */
    public static function server_error( $message = 'Internal server error', $meta = array() ) {
        return self::error( array(
            'code' => 'server_error',
            'message' => $message,
            'details' => null
        ), 500, $meta );
    }

    /**
     * Create standardized rate limit response
     *
     * @param int   $retry_after Seconds until retry is allowed
     * @param array $meta        Additional metadata
     * @return WP_REST_Response Standardized rate limit response
     * @since 1.0.0
     */
    public static function rate_limit_exceeded( $retry_after = 60, $meta = array() ) {
        $response = self::error( array(
            'code' => 'rate_limit_exceeded',
            'message' => 'Too many requests. Please try again later.',
            'details' => array(
                'retry_after' => $retry_after
            )
        ), 429, $meta );

        $response->header( 'Retry-After', $retry_after );
        return $response;
    }

    /**
     * Format single resource data
     *
     * @param mixed  $resource    Resource object
     * @param string $resource_type Resource type
     * @param array  $fields      Fields to include (optional)
     * @return array Formatted resource data
     * @since 1.0.0
     */
    public static function format_resource( $resource, $resource_type, $fields = array() ) {
        if ( ! $resource ) {
            return null;
        }

        $formatted = array();

        switch ( $resource_type ) {
            case 'patient':
                $formatted = self::format_patient( $resource, $fields );
                break;
            case 'doctor':
                $formatted = self::format_doctor( $resource, $fields );
                break;
            case 'appointment':
                $formatted = self::format_appointment( $resource, $fields );
                break;
            case 'encounter':
                $formatted = self::format_encounter( $resource, $fields );
                break;
            case 'prescription':
                $formatted = self::format_prescription( $resource, $fields );
                break;
            case 'bill':
                $formatted = self::format_bill( $resource, $fields );
                break;
            case 'clinic':
                $formatted = self::format_clinic( $resource, $fields );
                break;
            default:
                $formatted = (array) $resource;
        }

        // Filter fields if specified
        if ( ! empty( $fields ) && is_array( $formatted ) ) {
            $formatted = array_intersect_key( $formatted, array_flip( $fields ) );
        }

        return $formatted;
    }

    /**
     * Format patient data
     *
     * @param object $patient Patient object
     * @param array  $fields  Fields to include
     * @return array Formatted patient data
     * @since 1.0.0
     */
    private static function format_patient( $patient, $fields = array() ) {
        return array(
            'id' => (int) $patient->ID,
            'first_name' => $patient->first_name ?? '',
            'last_name' => $patient->last_name ?? '',
            'full_name' => trim( ( $patient->first_name ?? '' ) . ' ' . ( $patient->last_name ?? '' ) ),
            'email' => $patient->user_email ?? '',
            'contact_no' => $patient->contact_no ?? '',
            'date_of_birth' => $patient->dob ?? null,
            'gender' => $patient->gender ?? '',
            'address' => $patient->address ?? '',
            'city' => $patient->city ?? '',
            'state' => $patient->state ?? '',
            'country' => $patient->country ?? '',
            'postal_code' => $patient->postal_code ?? '',
            'blood_group' => $patient->blood_group ?? '',
            'clinic_id' => isset( $patient->clinic_id ) ? (int) $patient->clinic_id : null,
            'status' => isset( $patient->status ) ? (int) $patient->status : 1,
            'created_at' => $patient->user_registered ?? null,
            'updated_at' => $patient->updated_at ?? null
        );
    }

    /**
     * Format doctor data
     *
     * @param object $doctor Doctor object
     * @param array  $fields Fields to include
     * @return array Formatted doctor data
     * @since 1.0.0
     */
    private static function format_doctor( $doctor, $fields = array() ) {
        return array(
            'id' => (int) $doctor->ID,
            'first_name' => $doctor->first_name ?? '',
            'last_name' => $doctor->last_name ?? '',
            'full_name' => trim( ( $doctor->first_name ?? '' ) . ' ' . ( $doctor->last_name ?? '' ) ),
            'email' => $doctor->user_email ?? '',
            'mobile_number' => $doctor->mobile_number ?? '',
            'specialties' => $doctor->specialties ?? array(),
            'license_number' => $doctor->license_number ?? '',
            'experience_years' => isset( $doctor->experience_years ) ? (int) $doctor->experience_years : 0,
            'consultation_fee' => isset( $doctor->consultation_fee ) ? (float) $doctor->consultation_fee : 0.0,
            'clinic_id' => isset( $doctor->clinic_id ) ? (int) $doctor->clinic_id : null,
            'status' => isset( $doctor->status ) ? (int) $doctor->status : 1,
            'created_at' => $doctor->user_registered ?? null,
            'updated_at' => $doctor->updated_at ?? null
        );
    }

    /**
     * Format appointment data
     *
     * @param object $appointment Appointment object
     * @param array  $fields      Fields to include
     * @return array Formatted appointment data
     * @since 1.0.0
     */
    private static function format_appointment( $appointment, $fields = array() ) {
        return array(
            'id' => (int) $appointment->id,
            'appointment_start_date' => $appointment->appointment_start_date,
            'appointment_start_time' => $appointment->appointment_start_time,
            'appointment_end_date' => $appointment->appointment_end_date,
            'appointment_end_time' => $appointment->appointment_end_time,
            'visit_type' => $appointment->visit_type ?? 'consultation',
            'patient_id' => (int) $appointment->patient_id,
            'doctor_id' => (int) $appointment->doctor_id,
            'clinic_id' => (int) $appointment->clinic_id,
            'description' => $appointment->description ?? '',
            'status' => (int) $appointment->status,
            'status_text' => self::get_appointment_status_text( $appointment->status ),
            'appointment_report' => $appointment->appointment_report ?? '',
            'created_at' => $appointment->created_at,
            'updated_at' => $appointment->updated_at ?? null
        );
    }

    /**
     * Format encounter data
     *
     * @param object $encounter Encounter object
     * @param array  $fields    Fields to include
     * @return array Formatted encounter data
     * @since 1.0.0
     */
    private static function format_encounter( $encounter, $fields = array() ) {
        return array(
            'id' => (int) $encounter->id,
            'encounter_date' => $encounter->encounter_date,
            'patient_id' => (int) $encounter->patient_id,
            'doctor_id' => (int) $encounter->doctor_id,
            'clinic_id' => (int) $encounter->clinic_id,
            'appointment_id' => isset( $encounter->appointment_id ) ? (int) $encounter->appointment_id : null,
            'description' => $encounter->description ?? '',
            'status' => $encounter->status ?? 'completed',
            'added_by' => isset( $encounter->added_by ) ? (int) $encounter->added_by : null,
            'template_id' => isset( $encounter->template_id ) ? (int) $encounter->template_id : null,
            'created_at' => $encounter->created_at,
            'updated_at' => $encounter->updated_at ?? null
        );
    }

    /**
     * Format prescription data
     *
     * @param object $prescription Prescription object
     * @param array  $fields       Fields to include
     * @return array Formatted prescription data
     * @since 1.0.0
     */
    private static function format_prescription( $prescription, $fields = array() ) {
        return array(
            'id' => (int) $prescription->id,
            'encounter_id' => (int) $prescription->encounter_id,
            'patient_id' => (int) $prescription->patient_id,
            'medication_name' => $prescription->name ?? '',
            'frequency' => $prescription->frequency ?? '',
            'duration' => $prescription->duration ?? '',
            'instructions' => $prescription->instruction ?? '',
            'added_by' => isset( $prescription->added_by ) ? (int) $prescription->added_by : null,
            'is_from_template' => (bool) ( $prescription->is_from_template ?? false ),
            'created_at' => $prescription->created_at,
            'updated_at' => $prescription->updated_at ?? null
        );
    }

    /**
     * Format bill data
     *
     * @param object $bill   Bill object
     * @param array  $fields Fields to include
     * @return array Formatted bill data
     * @since 1.0.0
     */
    private static function format_bill( $bill, $fields = array() ) {
        return array(
            'id' => (int) $bill->id,
            'encounter_id' => isset( $bill->encounter_id ) ? (int) $bill->encounter_id : null,
            'appointment_id' => isset( $bill->appointment_id ) ? (int) $bill->appointment_id : null,
            'clinic_id' => (int) $bill->clinic_id,
            'title' => $bill->title ?? '',
            'total_amount' => (float) ( $bill->total_amount ?? 0 ),
            'discount' => (float) ( $bill->discount ?? 0 ),
            'actual_amount' => (float) ( $bill->actual_amount ?? 0 ),
            'status' => (int) $bill->status,
            'payment_status' => $bill->payment_status ?? 'pending',
            'created_at' => $bill->created_at,
            'updated_at' => $bill->updated_at ?? null
        );
    }

    /**
     * Format clinic data
     *
     * @param object $clinic Clinic object
     * @param array  $fields Fields to include
     * @return array Formatted clinic data
     * @since 1.0.0
     */
    private static function format_clinic( $clinic, $fields = array() ) {
        return array(
            'id' => (int) $clinic->id,
            'name' => $clinic->name ?? '',
            'email' => $clinic->email ?? '',
            'telephone_no' => $clinic->telephone_no ?? '',
            'specialties' => is_string( $clinic->specialties ) ? json_decode( $clinic->specialties, true ) : ( $clinic->specialties ?? array() ),
            'address' => $clinic->address ?? '',
            'city' => $clinic->city ?? '',
            'state' => $clinic->state ?? '',
            'country' => $clinic->country ?? '',
            'postal_code' => $clinic->postal_code ?? '',
            'clinic_admin_id' => isset( $clinic->clinic_admin_id ) ? (int) $clinic->clinic_admin_id : null,
            'status' => (int) ( $clinic->status ?? 1 ),
            'profile_image' => $clinic->profile_image ?? null,
            'clinic_logo' => $clinic->clinic_logo ?? null,
            'created_at' => $clinic->created_at ?? null,
            'updated_at' => $clinic->updated_at ?? null
        );
    }

    /**
     * Add standard headers to response
     *
     * @param WP_REST_Response $response Response object
     * @since 1.0.0
     */
    private static function add_standard_headers( WP_REST_Response $response ) {
        $response->header( 'X-API-Version', self::$api_version );
        $response->header( 'X-Powered-By', 'Care API' );
        $response->header( 'X-Content-Type-Options', 'nosniff' );
        $response->header( 'X-Frame-Options', 'DENY' );
        $response->header( 'X-XSS-Protection', '1; mode=block' );
    }

    /**
     * Generate unique request ID
     *
     * @return string Request ID
     * @since 1.0.0
     */
    private static function generate_request_id() {
        return 'req_' . uniqid() . '_' . substr( md5( microtime( true ) ), 0, 8 );
    }

    /**
     * Get appointment status text
     *
     * @param int $status Status code
     * @return string Status text
     * @since 1.0.0
     */
    private static function get_appointment_status_text( $status ) {
        $status_map = array(
            1 => 'scheduled',
            2 => 'completed',
            3 => 'cancelled',
            4 => 'no_show',
            5 => 'rescheduled'
        );

        return $status_map[$status] ?? 'unknown';
    }

    /**
     * Standardize user response
     *
     * @param WP_REST_Response $response Response object
     * @param object           $user     User object
     * @param WP_REST_Request  $request  Request object
     * @return WP_REST_Response
     * @since 1.0.0
     */
    public static function standardize_user_response( $response, $user, $request ) {
        // Only standardize Care API responses
        if ( strpos( $request->get_route(), '/care/v1/' ) !== false ) {
            $data = $response->get_data();
            
            // Add standard user formatting
            if ( isset( $data['id'] ) ) {
                $user_type = 'patient';
                if ( in_array( 'doctor', $user->roles ) ) {
                    $user_type = 'doctor';
                } elseif ( in_array( 'administrator', $user->roles ) ) {
                    $user_type = 'admin';
                } elseif ( in_array( 'kivicare_receptionist', $user->roles ) ) {
                    $user_type = 'receptionist';
                }
                
                $data['user_type'] = $user_type;
                $data['full_name'] = trim( ( $data['first_name'] ?? '' ) . ' ' . ( $data['last_name'] ?? '' ) );
                
                $response->set_data( $data );
            }
        }
        
        return $response;
    }

    /**
     * Standardize response headers
     *
     * @param WP_REST_Response $response Response object
     * @param WP_REST_Server   $server   Server object
     * @param WP_REST_Request  $request  Request object
     * @return WP_REST_Response
     * @since 1.0.0
     */
    public static function standardize_response_headers( $response, $server, $request ) {
        // Only handle Care API responses
        if ( strpos( $request->get_route(), '/care/v1/' ) !== false ) {
            self::add_standard_headers( $response );
        }
        
        return $response;
    }
}