--------------------
--     Config     --
--------------------

local updatetime = 0.25 -- How often, in minutes, it updates the bans from mysql.
-- (So if a person banned from one server joins another server using the same DB, it could take up to this long to get rid of him/her)

local baninterval = 44000 -- Ban time to report to console. We can't use 0 because we don't want it to be saved with writeid. 44k minutes == 1 month.
-- If you plan on keeping your server on one map without restarting for more than a month at a time (hah) then you'll need to up this value.

module( "UBan", package.seeall )

if not SERVER then return end
require( "mysqloo" )

----- Database connection details -----
 
local DATABASE_HOST = "127.0.0.1"        --Host
local DATABASE_PORT = 3306                      --Port
local DATABASE_NAME = "bans"            --Database
local DATABASE_USERNAME = "root"                --Username
local DATABASE_PASSWORD = "38nQjpyfWT6w3A2a"  
local table = "gbans"

--=== DO NOT EDIT BELOW THIS POINT ====

local server = mysqloo.connect(DATABASE_HOST, DATABASE_USERNAME, DATABASE_PASSWORD, DATABASE_NAME, DATABASE_PORT)

Bans = {} -- Keep track of all the last known bans

-- Because we use this a lot
function Format( str )
	if not str then return "NULL" end
	return string.format( "%q", str )
end

-- Ban user
function BanUser( banner, ply, time, comment )
	local time2
	if not time or time == 0 then
		time2 = baninterval
	else
		time2 = math.min( time / 60, baninterval )
	end

	local str = string.format( "kickid %s Banned on global ban list\n", ply:SteamID() )
	game.ConsoleCommand( str )

	local str = string.format( "banid %f %s kick\n", time2, ply:SteamID() ) -- Convert time to minutes
	game.ConsoleCommand( str )

	return ManualBan( banner, ply:SteamID(), ply:Nick(), time, comment )
end

function ManualBan( banner, steamid, name, time, comment )
	if not steamid then
		error( "Bad arguments passed to UBan.ManualBan", 2 )
		return
	end

	local Qc = server:query("SELECT CURRENT_USER() as user")
    Qc.onSuccess = function(this)
		Msg("Successfully SELECTED CURRENT_USER - Line 63")
		local curuser = this:getData()
		
		curuser = curuser[1]["user"] -- Get to the info we want

		local curip = string.gsub(curuser, ".-@", "" ) or "127.0.0.1"
		local curport = GetConVarNumber( "DATABASE_PORT" )

		local bannername
		local bannersteam
		if banner and banner:IsValid() and banner:IsPlayer() then
			bannername = banner:Nick()
			bannersteam = banner:SteamID()
		end
			
		local timestring = "0" -- ban duration part
		if time and time > 0 then
			timestring = "ADDTIME( NOW(), SEC_TO_TIME( " .. time .. " ) )"
		end

		
		local Qc = server:query( "INSERT INTO " .. table .. " ( steamid, name, unban_time, comment, serverip, serverport, adminname, adminsteamid ) VALUES( \"" .. server:escape( steamid ) .. "\", " .. Format( server:escape( name ) ) .. ", " .. timestring .. ", " .. Format( server:escape( comment ) ) .. ", " .. Format( server:escape( curip )) .. ", "  .. curport .. ", " .. Format( server:escape( bannername ) ) .. ", " .. Format( server:escape( bannersteam ) ) .. " )" )
			Msg("Successfully Connected - Line 85\n")
		Qc.onSuccess = function(this)
			local result = this:getData()
			
			if time == 0 then
				Bans[ steamid ] = baninterval
			else
				Bans[ steamid ] = time
			end
			return result
		end
		Qc.onFailure = function(Err) print(Err) end
			Msg("Failed to Connect - Line 97\n")
		Qc:start()
	end
	Qc.onFailure = function(Err) print(Err) end
	Msg("Failed to Connect - Line 101\n")
    Qc:start()
		

end

function ClearBan( steamid )
	
	local Qc = server:query("UPDATE " .. table .. " SET unban_time=NOW(), comment=CONCAT( \"(ban lifted before expired) \", comment ) WHERE (NOW() < unban_time OR unban_time = 0) AND steamid=\"" .. server:escape( steamid ) .. "\"")
    Qc.onFailure = function(Err) print(Err) 
	Msg("Failed to connect - line 111\n")
	end
    Qc:start()
	
	game.ConsoleCommand( "removeid " .. steamid .. "\n" )
	
	Bans[ steamid ] = nil	
end

function GetBans()
	
	local Qc = server:query("SELECT id, steamid, TIME_TO_SEC( TIMEDIFF( unban_time, NOW() ) ) as timeleft, name, time, unban_time, comment, INET_NTOA( serverip ) as serverip, serverport, adminname, adminsteamid FROM " .. table .. " WHERE NOW() < unban_time OR unban_time = 0")
    Qc.onSuccess = function(data)
		Msg("Successfully Connected - Line 124\n")
		local results = data:getData()
		return results
	end
	Qc.onFailure = function(Err) print(Err) 
		Msg("Failed to connect - Line 127\n")
	end
    Qc:start()
end

function DoBans()

	local Qc = server:query("SELECT steamid, TIME_TO_SEC( TIMEDIFF( unban_time, NOW() ) ) as timeleft FROM " .. table .. " WHERE NOW() < unban_time OR unban_time = 0")
    Qc.onSuccess = function(data)
		Msg("Successfully Connected - Line 138\n")
		local results = data:getData()

		local steamids = {}

		for _, t in ipairs( results ) do
			local steamid = t.steamid

			local time = t.timeleft
			if not time or time == 0 then
				time = baninterval
			else
				time = math.min( time / 60, baninterval )
			end

			steamids[ steamid ] = math.max( time, steamids[ steamid ] or 0 ) -- We're doing this so oddly so we can catch multiple results and use the largest one.
		end

		-- We're using this following chunk of code to identify current steamids in the server
		local cursteamids = {}
		local players = player.GetAll()
		for _, ply in ipairs( players ) do
			cursteamids[ ply:SteamID() ] = ply
		end

		for steamid, time in pairs( steamids ) do -- loop through all currently banned ids
			if cursteamids[ steamid ] then -- Currently connected
				local str = string.format( "kickid %s Banned on global ban list\n", steamid )
				game.ConsoleCommand( str )
				Bans[ steamid ] = nil -- Clear their ban info to make sure they get banned. (A 'reban' should only ever arise if console removeid's a steamid)
			end

			if not Bans[ steamid ] or Bans[ steamid ] < time or Bans[ steamid ] > time + baninterval * 2 then -- If we don't already have them marked as banned or it's a new time
				local str = string.format( "banid %f %s kick\n", time, steamid )
				game.ConsoleCommand( str )
				-- print( str ) -- For debug
			end
			Bans[ steamid ] = time
		end
		
		for steamid in pairs( Bans ) do -- loop through all recorded bans
			if not steamids[ steamid ] then -- If they're not on the ban list we just pulled off the server, they're out of jail!
				game.ConsoleCommand( "removeid " .. steamid .. "\n" )
				Bans[ steamid ] = nil
			end
		end
		
		
	end
	Qc.onFailure = function(Err) print(Err) end
	Msg("Failed to Connect - Line 188\n")
    Qc:start()


	
end

server.onConnected = checkTable
server.onConnectionFailed = function(D, E) print( "[Uban] Failed to Connect to Database: " .. E ) end
server.onConnected = function()
	DoBans() -- Initial
	timer.Create( "UBantimer", updatetime * 60, 0, DoBans ) -- Updates
end
server:connect()