package ru.bitel.bgbilling.modules.tv.dyn.ares.iptvportal;

import java.io.IOException;
import java.net.InetSocketAddress;
import java.net.URI;
import java.net.URL;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.concurrent.atomic.AtomicLong;

import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;

import ru.bitel.bgbilling.common.BGException;
import ru.bitel.bgbilling.modules.tv.common.bean.TvDevice;
import ru.bitel.bgbilling.modules.tv.dyn.JsonClient;
import ru.bitel.common.ParameterMap;
import ru.bitel.common.Utils;

public class IptvPortalJsonClient
    extends JsonClient
{
	private static final Logger logger = LogManager.getLogger();

	private static AtomicLong requestId = new AtomicLong();

	private URL authorizeUrl;

	private String sessionId;

	public static IptvPortalJsonClient newInstance( TvDevice tvDevice, ParameterMap config )
		throws Exception
	{
		String host = null;
		int port = 0;

		List<InetSocketAddress> addressList = tvDevice.getHosts();
		if ( addressList.size() > 0 )
		{
			InetSocketAddress socketAddress = addressList.get( 0 );
			host = socketAddress.getAddress().getHostName();
			port = socketAddress.getPort();
		}

		if ( Utils.isBlankString( host ) )
		{
			host = "127.0.0.1";
		}

		if ( port <= 0 )
		{
			port = 443;
		}

		URL url = URI.create( config.get( "om.url", config.get( "iptvportal.api.url", "https://" + host + ":" + port + "/api/jsonsql/" ) ) ).toURL();
		URL authorizeUrl = URI.create( config.get( "om.url", config.get( "iptvportal.api.url", "https://" + host + ":" + port + "/api/jsonrpc/" ) ) ).toURL();

		String login = config.get( "om.login", config.get( "iptvportal.api.login", tvDevice.getUsername() ) );
		String password = config.get( "om.password", config.get( "iptvportal.api.password", tvDevice.getPassword() ) );

		return new IptvPortalJsonClient( url, authorizeUrl, login, password );
	}

	public IptvPortalJsonClient( URL url, URL authorizeUrl, String username, String password )
		throws Exception
	{
		super( url, username, password );

		this.authorizeUrl = authorizeUrl;
		this.basicAuth = false;
	}

	public void setAuthorizeUrl( URL authorizeUrl )
	{
		this.authorizeUrl = authorizeUrl;
	}

	protected JSONObject invoke( final String method, final JSONObject params )
	    throws IOException, BGException, JSONException
	{
		return invoke( this.url, method, params );
	}

	protected JSONObject invoke( final URL url, final String method, final JSONObject params )
	    throws IOException, BGException, JSONException
	{
		JSONObject req = new JSONObject()
			.put( "jsonrpc", "2.0" )
			.put( "id", requestId.incrementAndGet() )
			.put( "method", method )
			.put( "params", params );

		Map<String, String> requestProperties = null;
		if ( sessionId != null )
		{
			requestProperties = Collections.singletonMap( "Iptvportal-Authorization", "sessionid=" + sessionId );
		}

		return super.invoke( url, Method.post, requestProperties, "", null, req );
	}

	public boolean authorize()
	    throws JSONException, IOException, BGException
	{
		JSONObject params = new JSONObject()
			.put( "username", username )
			.put( "password", password );

		JSONObject res = invoke( this.authorizeUrl, "authorize_user", params );

		if ( !res.isNull( "result" ) )
		{
			res = res.getJSONObject( "result" );

			this.sessionId = res.getString( "session_id" );

			return true;
		}
		else
		{
			res = res.getJSONObject( "error" );
			logger.error( "Error while connecting to device: " + res.getString( "message" ) );

			return false;
		}
	}

	public void disconnect()
	{
		this.sessionId = null;
	}

	protected int insert( String table, String columns, Object... values )
	    throws IOException, BGException, JSONException
	{
		JSONObject params = new JSONObject()
			.put( "into", table )
			.put( "columns", new JSONArray( columns.split( "\\s*,\\s*" ) ) )
			.put( "values", new JSONArray( values ) )
			.put( "returning", "id" );

		JSONObject res = invoke( "insert", params );

		if ( !res.isNull( "result" ) )
		{
			JSONArray array = res.getJSONArray( "result" );
			if ( array.length() > 0 )
			{
				return array.optInt( 0, -1 );
			}
			else
			{
				return -1;
			}
		}
		else
		{
			res = res.getJSONObject( "error" );
			logger.error( "Error while insert: " + res.getString( "message" ) );

			return -1;
		}
	}

	protected int update( String table, String whereColumn, String whereValue, String columns, Object... values )
	    throws IOException, BGException, JSONException
	{
		JSONObject params = new JSONObject();
		params.put( "table", table );

		JSONObject set = new JSONObject();

		int i = 0;
		for ( String column : columns.split( "\\s*,\\s*" ) )
		{
			set.put( column, values[i++] );
		}

		params.put( "set", set );
		params.put( "where", new JSONObject( Collections.singletonMap( "eq", Arrays.asList( whereColumn, whereValue ) ) ) );
		params.put( "returning", "id" );

		JSONObject res = invoke( "update", params );

		if ( !res.isNull( "result" ) )
		{
			JSONArray array = res.getJSONArray( "result" );
			if ( array.length() > 0 )
			{
				return array.getInt( 0 );
			}
			else
			{
				return -1;
			}
		}
		else
		{
			res = res.getJSONObject( "error" );
			logger.error( "Error while update: " + res.getString( "message" ) );

			return -1;
		}
	}

	protected JSONObject remove( String table, JSONObject where )
	    throws IOException, BGException, JSONException
	{
		JSONObject params = new JSONObject();
		params.put( "from", table );
		params.put( "where", where );
		params.put( "returning", "id" );

		return invoke( "delete", params );
	}

	protected JSONObject remove( String table, String whereColumn, String whereValue )
	    throws IOException, BGException, JSONException
	{
		return remove( table, new JSONObject( Collections.singletonMap( "eq", Arrays.asList( whereColumn, whereValue ) ) ) );
	}

	protected JSONObject select( String table, String columns, JSONObject where )
	    throws IOException, BGException, JSONException
	{
		JSONObject params = new JSONObject();
		params.put( "data", new JSONArray( columns.split( "\\s*,\\s*" ) ) );
		params.put( "from", table );

		if ( where != null )
		{
			params.put( "where", where );
		}

		return invoke( "select", params );
	}
}
