<?php if (!defined('ABSPATH')) exit;class Lwcai_Rest{public static function init(){add_action('rest_api_init',[__CLASS__,'routes'])}public static function routes(){register_rest_route('lwcai/v1','/chat',['methods'=>'POST','callback'=>[__CLASS__,'handle_chat'],'permission_callback'=>'__return_true']);register_rest_route('lwcai/v1','/handoff',['methods'=>'POST','callback'=>[__CLASS__,'handle_handoff'],'permission_callback'=>'__return_true']);// Admin reply:check capability here;nonce is verified inside the handler register_rest_route('lwcai/v1','/admin/reply',['methods'=>'POST','callback'=>[__CLASS__,'admin_reply'],'permission_callback'=>function(){return current_user_can('manage_options')}])}private static function is_office_open($opts){if (empty($opts['office_enabled']) || empty($opts['office_hours'])) return true;$tz=wp_timezone();$now=new DateTime('now',$tz);$dow=$now->format('D');$hours=$opts['office_hours'];if (!is_array($hours)){$d=json_decode($hours,true);$hours=is_array($d)?$d:[]}foreach($hours as $row){if(count($row)!==3) continue;list($day,$start,$end)=$row;if($day===$dow){$s=DateTime::createFromFormat('H:i',$start,$tz);$e=DateTime::createFromFormat('H:i',$end,$tz);if($s&&$e){$s->setDate($now->format('Y'),$now->format('m'),$now->format('d'));$e->setDate($now->format('Y'),$now->format('m'),$now->format('d'));return ($now>=$s && $now<=$e)}}}return false}public static function handle_chat(WP_REST_Request $req){$uid=sanitize_text_field($req['conversation_uid']);$message=sanitize_textarea_field($req['message']);$name=sanitize_text_field($req['name'] ?? '');$email=sanitize_email($req['email'] ?? '');if (empty($uid) || empty($message)) return new WP_Error('bad_request','Missing conversation_uid or message',['status'=>400]);$opts=Lwcai_Settings::get();$cid=Lwcai_ChatStore::upsert_conversation($uid,$name,$email);Lwcai_ChatStore::add_message($cid,'user',$message);if (!self::is_office_open($opts)) return ['status'=>'offline','reply'=>$opts['offline_message']];$history=Lwcai_ChatStore::get_history($cid,20);$messages=array_merge([['role'=>'system','content'=>$opts['system_prompt']]],$history);$reply=self::call_openai($opts,$messages);if (is_wp_error($reply)) return new WP_Error('ai_error',$reply->get_error_message(),['status'=>500]);Lwcai_ChatStore::add_message($cid,'assistant',$reply);return ['status'=>'ok','reply'=>$reply]}private static function call_openai($opts,$messages){if (empty($opts['api_key'])) return new WP_Error('missing_key','Missing API key in settings.');$endpoint='https://api.openai.com/v1/chat/completions';$body=['model'=>$opts['api_model'],'messages'=>$messages,'temperature'=>0.4];$args=['headers'=>['Content-Type'=>'application/json','Authorization'=>'Bearer '.$opts['api_key']],'timeout'=>20,'body'=>wp_json_encode($body)];$res=wp_remote_post($endpoint,$args);if(is_wp_error($res)) return $res;$code=wp_remote_retrieve_response_code($res);$json=json_decode(wp_remote_retrieve_body($res),true);if($code!==200 || empty($json['choices'][0]['message']['content'])) return new WP_Error('api_fail','OpenAI API error: '.($json['error']['message'] ?? 'unknown'));return $json['choices'][0]['message']['content']}public static function handle_handoff(WP_REST_Request $req){$uid=sanitize_text_field($req['conversation_uid']);$name=sanitize_text_field($req['name'] ?? '');$email=sanitize_email($req['email'] ?? '');$msg=sanitize_textarea_field($req['message'] ?? '');if (empty($uid)) return new WP_Error('bad_request','Missing conversation_uid',['status'=>400]);$opts=Lwcai_Settings::get();$cid=Lwcai_ChatStore::upsert_conversation($uid,$name,$email);if(!empty($msg)) Lwcai_ChatStore::add_message($cid,'user',$msg);Lwcai_ChatStore::add_message($cid,'note','Human handoff requested');if(!empty($opts['email_alerts']) && !empty($opts['alert_email'])) wp_mail($opts['alert_email'],'[AI Chatbot] Human handoff requested',"Conversation: $uid\nName: $name\nEmail: $email\nMessage: $msg");return ['status'=>'ok']}public static function admin_reply(WP_REST_Request $req){// Accept nonce from header or request body for compatibility $nonce=$req->get_header('X-WP-Nonce');if (!$nonce && isset($req['_wpnonce'])){$nonce=sanitize_text_field($req['_wpnonce'])}if (! wp_verify_nonce($nonce,'wp_rest')){return new WP_Error('forbidden','Invalid nonce',['status'=>403])}$conversation_id=absint($req['conversation_id']);$content=wp_kses_post($req['content']);if(!$conversation_id || empty($content)) return new WP_Error('bad_request','Missing conversation_id or content',['status'=>400]);Lwcai_ChatStore::add_admin_reply($conversation_id,$content);return ['status'=>'ok']}}