MG_TicketSystem = MG_TicketSystem or {}

MG_TicketSystem.ForbiddenRanks = {
	["owner"] = true,
	["stellv. owner"] = true,
	["infrastrukturadmin"] = true,
	["community-manager"] = true,
	["teamleiter"] = true,
	["developer"] = true
}

if CLIENT then
	local Redeem
	net.Receive("TicketSystem_Redeem", function(len)
		if IsValid(Redeem) then return end
		Redeem = Derma_StringRequest("Ticket einlösen", "Bitte gebe hier, deinen Ticket-Code ein:", "Leer lassen um deine eigenen Tickets einzusehen", function(ticket_num)
			ticket_num = string.Replace(ticket_num, "Leer lassen um deine eigenen Tickets einzusehen", "")
			net.Start("TicketSystem_SendRedeem")
				net.WriteString(ticket_num)
			net.SendToServer()
		end, nil, "Überprüfen", "Zurück")
	end)

	net.Receive("TicketSystem_AddClipboard", function(len)
		local text = net.ReadString()
		SetClipboardText(text)
		local ply = LocalPlayer()
		if ply then
			ply:ChatPrint("\""..text.."\" in die Zwischenablage kopiert.")
		end
	end)

	local Frame
	net.Receive("TicketSystem_GetTickets", function()
		local tickets = net.ReadTable()
		if IsValid(Frame) then Frame:Close() end
		if #tickets > 1 then
			Frame = vgui.Create("DFrame")
			Frame:SetSize(600, 400)
			Frame:Center()
			Frame:MakePopup()
			Frame:SetTitle("Deine Tickets")
			local List = vgui.Create("DListView", Frame)
			List:Dock(FILL)
			List:SetMultiSelect(false)
			List:AddColumn("Ticket-Code")
			List:AddColumn("Rang")
			List:AddColumn("Geld")

			local function AddTickets()
				if #tickets <= 0 then
					Frame:Close()
				end
				for _, v in ipairs(tickets) do
					local rank = v.rank
					rank = rank and rank != "NULL" and (rank:gsub("^%l", string.upper)).."-Rang" or "Nicht vorhanden"
					local money = v.money
					money = money and money != "NULL" and string.Comma(money) or "Nicht vorhanden"
					List:AddLine(v.ticket_num, rank, money)
				end
			end
			AddTickets()

			function List:OnRowSelected(row, panel)
				local Menu = vgui.Create("DMenu", panel)
				Menu:AddOption("Ticket verwenden", function()
					net.Start("TicketSystem_SendRedeem")
						net.WriteString(tickets[row].ticket_num)
					net.SendToServer()
					table.remove(tickets, row)
					List:Clear()
					AddTickets()
				end):SetIcon("icon16/key.png")
				Menu:AddOption("Ticket veröffentlichen", function()
					net.Start("TicketSystem_SetPublic")
						net.WriteString(tickets[row].ticket_num)
					net.SendToServer()
					table.remove(tickets, row)
					List:Clear()
					AddTickets()
				end):SetIcon("icon16/world.png")
				Menu:AddOption("Ticket zerstören", function()
					Derma_Query("Willst du dieses Ticket wirklich zerstören?", "Ticket zerstören", "Ticket zerstören", function()
						net.Start("TicketSystem_DestroyTicket")
							net.WriteString(tickets[row].ticket_num)
						net.SendToServer()
						table.remove(tickets, row)
						List:Clear()
						AddTickets()
					end, "Abbrechen")
				end):SetIcon("icon16/exclamation.png")
				Menu:AddOption("Ticket-Code in Zwischenablage kopieren", function()
					SetClipboardText(tickets[row].ticket_num)
					LocalPlayer():ChatPrint("\""..tickets[row].ticket_num.."\" in die Zwischenablage kopiert.")
				end):SetIcon("icon16/cut.png")
				Menu:Open()
			end
		else
			local ticket = tickets[1]
			local money = ticket.money
			money = money and money != "NULL" and string.Comma(money) or "kein"
			local rank = ticket.rank
			rank = rank and rank != "NULL" and "den "..(rank:gsub("^%l", string.upper)).."-Rang" or "keinen Rang"
			Frame = Derma_Query("Du hast ein unbenutztes Ticket! Dieses beinhaltet "..money.." Geld und "..rank..". Wie möchtest du mit diesem verfahren?", "Dein Ticket", "Ticket verwenden", function()
				net.Start("TicketSystem_SendRedeem")
					net.WriteString(tickets[1].ticket_num)
				net.SendToServer()
				end, "Ticket veröffentlichen", function()
					net.Start("TicketSystem_SetPublic")
						net.WriteString(tickets[1].ticket_num)
					net.SendToServer()
				end, "Ticket zerstören", function()
					Derma_Query("Willst du dieses Ticket wirklich zerstören?", "Ticket zerstören", "Ticket zerstören", function()
						net.Start("TicketSystem_DestroyTicket")
							net.WriteString(tickets[1].ticket_num)
						net.SendToServer()
					end, "Abbrechen")
				end, "Fenster schließen & Code kopieren", function()
					SetClipboardText(tickets[1].ticket_num)
					LocalPlayer():ChatPrint("\""..tickets[1].ticket_num.."\" in die Zwischenablage kopiert.")
			end)
		end
	end)

	local Frame
	net.Receive("TicketSystem_ShowTickets", function()
		local tickets = net.ReadTable()
		if IsValid(Frame) then Frame:Close() end
		Frame = vgui.Create("DFrame")
		Frame:SetSize(500, 400)
		Frame:Center()
		Frame:MakePopup()
		Frame:SetTitle("Ticketlistung")
		local List = vgui.Create("DListView", Frame)
		List:Dock(FILL)
		List:SetMultiSelect(false)
		List:AddColumn("Ticket-Code")
		List:AddColumn("SteamID")
		List:AddColumn("Rang")
		List:AddColumn("Geld")

		local function AddTickets()
			for _, v in ipairs(tickets) do
				local steamid = v.steamid
				steamid = steamid and steamid != "NULL" and steamid or "Nicht vorhanden"
				local rank = v.rank
				rank = rank and rank != "NULL" and (rank:gsub("^%l", string.upper)).."-Rang" or "Nicht vorhanden"
				local money = v.money
				money = money and money != "NULL" and string.Comma(money) or "Nicht vorhanden"
				List:AddLine(v.ticket_num, steamid, rank, money)
			end
		end
		AddTickets()

		function List:OnRowSelected(row, panel)
			local Menu = vgui.Create("DMenu", panel)
			local steamid = tickets[row].steamid 
			steamid = steamid and steamid != "NULL" and steamid
			if steamid then
				Menu:AddOption("Ticket veröffentlichen", function() 
					net.Start("Admin_TicketSystem_SetPublic")
						net.WriteString(tickets[row].ticket_num)
					net.SendToServer()
					tickets[row].steamid = nil
					List:Clear()
					AddTickets()
				end):SetIcon("icon16/world.png")
			else
				Menu:AddOption("Ticket an SteamID binden", function() 
					Derma_StringRequest("Ticket an SteamID binden", "Gebe die SteamID eines Spielers ein, um das Ticket an diese zu binden:", "", function(text)
						text = string.upper(string.Trim(text))
						if text:match("^(STEAM_[0-9]+:[0-9]+:[0-9]+)$") then
							net.Start("TicketSystem_SetPrivate")
								net.WriteString(tickets[row].ticket_num)
								net.WriteString(text)
							net.SendToServer()
							tickets[row].steamid = text
							List:Clear()
							AddTickets()
						else
							Derma_Message("Diese SteamID existiert nicht!", "Falsche SteamID")
						end
					end, nil, "Akzeptieren", "Abbrechen")
				end):SetIcon("icon16/key_add.png")
			end
			Menu:AddOption("Ticket zerstören", function() 
				net.Start("Admin_TicketSystem_DestroyTicket")
					net.WriteString(tickets[row].ticket_num)
				net.SendToServer()
				table.remove(tickets, row)
				List:Clear()
				AddTickets()
			end):SetIcon("icon16/delete.png")
			Menu:AddOption("Ticket-Code in Zwischenablage kopieren", function() 
				SetClipboardText(tickets[row].ticket_num)
				LocalPlayer():ChatPrint("\""..tickets[row].ticket_num.."\" in die Zwischenablage kopiert.")
			end):SetIcon("icon16/cut.png")
			if steamid then
				Menu:AddOption("SteamID in Zwischenablage kopieren", function() 
					SetClipboardText(steamid)
					LocalPlayer():ChatPrint("\""..steamid.."\" in die Zwischenablage kopiert.")
				end):SetIcon("icon16/cut_red.png")
			end
			Menu:Open()
		end
	end)
end

local function FindTicketNumber(callback)
	local ticket_num = math.random(10000000000000, 99999999999999)
	MG_TicketSystem.Query("SELECT * FROM mg_don_tickets WHERE ticket_num = "..MG_TicketSystem.SQLStr(ticket_num), function(exists)
		if exists then
			FindTicketNumber(callback)
		else
			callback(ticket_num)
		end
	end)
end

local CATEGORY_NAME = "Tickets"
function MG_TicketSystem.CreateTicket(calling_ply, money, rank, steamid)
	local is_ply = IsValid(calling_ply)
	local access = false
	if is_ply then
		if calling_ply:query("ulx maketicket") then
			access = true
		end
	else
		access = true
	end
	if !access then return end
	if money and money != "money" and money != "0" and money != "x" and string.Trim(money) != "" then
		local amt = tonumber(money)
		if !amt or amt < 0 or amt > 100000000 then 
			ULib.tsayError(calling_ply, "Du musst eine gültige Summe an Geld angeben!")
			return
		end
	end
	if !money or money == "money" or money == "0" or money == "x" or string.Trim(money) == "" then
		money = "NULL"
	end
	if rank and rank != "rank" and rank != "user" and rank != "x" and string.Trim(rank) != "" and (!table.HasValue(ulx.group_names, rank) or MG_TicketSystem.ForbiddenRanks[rank]) then
		ULib.tsayError(calling_ply, "Du musst einen gültigen Rang angeben!")
		return
	end
	if !rank or rank == "rank" or rank == "user" or rank == "x" or string.Trim(rank) == "" then
		rank = "NULL"
	end
	if money == "NULL" and rank == "NULL" then
		ULib.tsayError(calling_ply, "Du musst einen Rang oder einen Geldbetrag festlegen!")
		return
	end
	FindTicketNumber(function(ticket_num)
		if is_ply and !IsValid(calling_ply) then return end
		if !steamid or string.Trim(steamid) == "" or steamid == "x" or steamid == "steamid (not required)" then
			steamid = "NULL"
		else
			if !steamid:match("^(STEAM_[0-9]+:[0-9]+:[0-9]+)$") then
				ULib.tsayError(calling_ply, "Diese SteamID existiert nicht!")
				return
			end
		end
		if steamid == "NULL" then
			MG_TicketSystem.Query("INSERT INTO mg_don_tickets (ticket_num, money, rank) VALUES("..ticket_num..", "..money..", "..MG_TicketSystem.SQLStr(rank)..")")
		else
			MG_TicketSystem.Query("INSERT INTO mg_don_tickets (ticket_num, steamid, money, rank) VALUES("..ticket_num..", "..MG_TicketSystem.SQLStr(steamid)..", "..money..", "..MG_TicketSystem.SQLStr(rank)..")", function()
				for _, v in ipairs(player.GetAll()) do
					if v:SteamID() == steamid then
						MG_TicketSystem.ShowTickets(v)
						break
					end
				end
			end)
		end
		hook.Run("MG_TicketSystem_CreateTicket", calling_ply, ticket_num, steamid, money, rank)
		if rank == "NULL" then
			rank = "no"
		else
			rank = "the "..rank
		end
		if money == "NULL" then
			money = "no"
		end
		if steamid == "NULL" then
			ulx.fancyLogAdmin(calling_ply, true, "#A created a ticket. It contains "..string.Comma(money).." money and "..rank.." rank.")
		else
			ulx.fancyLogAdmin(calling_ply, true, "#A created a ticket bound to "..steamid..". It contains "..string.Comma(money).." money and "..rank.." rank.")
		end
		net.Start("TicketSystem_AddClipboard")
			net.WriteString(ticket_num)
		net.Send(calling_ply)
	end)
end

local createticket = ulx.command(CATEGORY_NAME, "ulx maketicket", MG_TicketSystem.CreateTicket)
createticket:addParam{type=ULib.cmds.StringArg, hint="money"}
createticket:addParam{type=ULib.cmds.StringArg, hint="rank", nil, ULib.cmds.optional}
createticket:addParam{type=ULib.cmds.StringArg, hint="steamid (not required)", nil, ULib.cmds.optional}
createticket:defaultAccess(ULib.ACCESS_SUPERADMIN)
createticket:help("Creates tickets using steamids or random generated numbers.")

function MG_TicketSystem.RedeemTicket(calling_ply)
	if !IsValid(calling_ply) or !calling_ply:IsPlayer() then return end
	net.Start("TicketSystem_Redeem")
	net.Send(calling_ply)
end

local redeem = ulx.command(CATEGORY_NAME, "ulx redeem", MG_TicketSystem.RedeemTicket, "!redeem")
redeem:defaultAccess(ULib.ACCESS_ALL)
redeem:help("Redeem tickets by using a code or view your own tickets.")

function MG_TicketSystem.DeleteTicket(calling_ply, ticket_num)
	ticket_num = tonumber(ticket_num)
	if !ticket_num then
		ULib.tsayError(calling_ply, "Du musst ein gültiges Code-Format angeben!")
		return
	end
	local is_ply = IsValid(calling_ply)
	MG_TicketSystem.GetTicketByID(ticket_num, function(ticket)
		if is_ply and !IsValid(calling_ply) then return end
		if ticket then
			MG_TicketSystem.DestroyTicket(ticket_num)
			ulx.fancyLogAdmin(calling_ply, true, "#A deleted ticket ("..ticket_num..")")
		else
			ULib.tsayError(calling_ply, "Ticket-Code ist ungültig!")
		end
	end)
end

local deleteticket = ulx.command(CATEGORY_NAME, "ulx deleteticket", MG_TicketSystem.DeleteTicket)
deleteticket:addParam{type=ULib.cmds.StringArg, hint="ticket number"}
deleteticket:defaultAccess(ULib.ACCESS_SUPERADMIN)
deleteticket:help("Deletes tickets using the ticket number.")

function MG_TicketSystem.SV_ShowTickets(calling_ply)
	local is_ply = IsValid(calling_ply)
	MG_TicketSystem.GetTickets(function(tickets)
		tickets = tickets or {}
		if is_ply and IsValid(calling_ply) then
			net.Start("TicketSystem_ShowTickets")
				net.WriteTable(tickets)
			net.Send(calling_ply)
		elseif !is_ply then
			ULib.console(calling_ply, "Ticket-Code | SteamID | Rang & Geld")
			for _, v in ipairs(tickets) do
				ULib.console(calling_ply, v.ticket_num.."  "..(v.steamid and v.steamid != "NULL" and v.steamid or "x				 ").."  "..(v.rank and v.rank != "NULL" and v.rank or "x").."  "..(v.money and v.money != "NULL" and v.money or "x"))
			end
		end
	end)
end

local showtickets = ulx.command(CATEGORY_NAME, "ulx showtickets", MG_TicketSystem.SV_ShowTickets, "!showtickets")
showtickets:defaultAccess(ULib.ACCESS_SUPERADMIN)
showtickets:help("Opens the ticket admin interface.")