<?php

namespace App\Notifications\Matrix;

use Illuminate\Support\Facades\Http;
use Illuminate\Support\Facades\Log;

use App\Models\Echomail as EchomailModel;
use App\Notifications\Matrix;

class Echomail extends Matrix
{
	private const LOGKEY = 'NME';

	/**
	 * Post a message from Matrix.
	 *
	 * @param EchomailModel $o
	 */
	public function __construct(private EchomailModel $o)
	{
		parent::__construct();
	}

	/**
	 * Get the mail representation of the notification.
	 *
	 * @param mixed $notifiable
	 * @return mixed
	 * @throws \Exception
	 */
	public function toMatrix(object $notifiable): mixed
	{
		$room = $notifiable->routeNotificationFor(static::via);

		Log::info(sprintf('%s:+ Sending Echo Message to Matrix [%s]',self::LOGKEY,$room));

		$username = sprintf('%s^%d_%d/%d.%d',
			$this->o->from,
			$this->o->fftn->zone->zone_id,
			$this->o->fftn->host_id,
			$this->o->fftn->node_id,
			$this->o->fftn->point_id,
		);

		$user = sprintf('@%s:%s',$username,preg_replace('#^(http[s]?://)?([^:]+)(:[0-9]+)?$#','$2',config('matrix.server')));

		// Set topic if it is different:
		$subject = Http::withToken(config('matrix.as_token'))
			->get(sprintf('%s/_matrix/client/v3/rooms/%s/state/m.room.topic',config('matrix.server'),$room));

		if (($x=preg_replace('/^RE:\s*/i','',$this->o->subject)) !== $subject->json('topic','Message from Matrix')) {
			$topic = Http::withToken(config('matrix.as_token'))
				->put(sprintf('%s/_matrix/client/v3/rooms/%s/state/m.room.topic',config('matrix.server'),$room),[
					'topic'=>$x,
				]);

			if ($topic->status() !== 200)
				Log::error(sprintf('%s:! Failed to set matrix room topic to [%s] in room [%s]',self::LOGKEY,$x,$room),['msg'=>$topic->body()]);
		}

		$omsg = preg_replace("/\r---.*$/U",'',$this->o->msg);

		$msg = Http::withToken(config('matrix.as_token'))
			->withQueryParameters(['user_id'=>$user])
			->post(sprintf('%s/_matrix/client/v3/rooms/%s/send/m.room.message',config('matrix.server'),$room),[
				'msgtype'=>'m.text',
				'format'=>'org.matrix.custom.html',
				'body'=>mb_convert_encoding(str_replace("\r","\n",$omsg),'UTF-8','IBM850'),
				'formatted_body'=>sprintf("<pre>\n%s\n</pre>",mb_convert_encoding(str_replace("\r","\n",$omsg),'UTF-8','IBM850')),
			]);

		switch ($msg->status()) {
			case 200:
				break;

			case 403:
				Log::alert(sprintf('%s:! Got 403 with errcode [%s] reason [%s]',self::LOGKEY,$msg->json('errcode'),$msg->json('error')));

				// @todo Test that the user doesnt exist
				// If the user doesnt exist in matrix yet
				if (str_starts_with($msg->json('error'),'Application service has not registered this user')) {
					// Register user
					$msg = Http::withToken(config('matrix.as_token'))
						->post(sprintf('%s/_matrix/client/v3/register',config('matrix.server')),[
							'type'=>'m.login.application_service',
							'username'=>$username,
						]);

					if ($msg->status() !== 200) {
						Log::error(sprintf('%s:! Failed to register user [%s] to matrix',self::LOGKEY,$username),['msg'=>$msg->body()]);
						throw new \Exception(sprintf('Failed to invite user [%s] to matrix',$username));
					}

					// @todo Test that the user has been invited
					// Invite user
					$msg = Http::withToken(config('matrix.as_token'))
						//->withQueryParameters(['user_id'=>$user])
						->post(sprintf('%s/_matrix/client/v3/rooms/%s/invite',config('matrix.server'),$room),[
							'user_id'=>$user,
						]);

					if ($msg->status() !== 200) {
						Log::error(sprintf('%s:! Failed to invite user [%s] to matrix room [%s]',self::LOGKEY,$user,$room),['msg'=>$msg->body()]);
						throw new \Exception(sprintf('Failed to invite user [%s] to matrix room [%s]',$user,$room));
					}

					// Join as user
					$msg = Http::withToken(config('matrix.as_token'))
						->withQueryParameters(['user_id'=>$user])
						->post(sprintf('%s/_matrix/client/v3/rooms/%s/join',config('matrix.server'),$room),[
							'user_id'=>$user,
						]);

					if ($msg->status() !== 200) {
						Log::error(sprintf('%s:! Failed to join user [%s] to matrix room [%s]',self::LOGKEY,$user,$room),['msg'=>$msg->body()]);
						throw new \Exception(sprintf('Failed to join user [%s] to matrix room [%s]',$user,$room));
					}

					// retry this message
					throw new \Exception('Need to create user on matrix first');
				}

				break;

			default:
				Log::error(sprintf('%s:! Unknown status [%d] with errcode [%s] reason [%s] when posting message [%d] to matrix',self::LOGKEY,$msg->status(),$msg->json('errcode'),$msg->json('error'),$this->o->id),['msg'=>$msg->body()]);
		}

		return $msg->body();
	}
}