util.AddNetworkString("ixCaptureSync")

PLUGIN.capturePoints = PLUGIN.capturePoints or {}

function PLUGIN:GetPlayersInRadius(point, radius)
    local players = {}
    local pos = point:GetPos()

    for _, ply in ipairs(player.GetAll()) do
        if ply:Alive() and pos:Distance(ply:GetPos()) <= radius and not (ply.IsAdminMode and ply:IsAdminMode()) then
            table.insert(players, ply)
        end
    end

    return players
end

function PLUGIN:GetFactionFromPlayers(players)
	local factions = {}
	
	for _, ply in ipairs(players) do
		local char = ply:GetCharacter()
		if (char) then
			local faction = char:GetFaction()
			factions[faction] = (factions[faction] or 0) + 1
		end
	end
	
	return factions
end

function PLUGIN:GetFactionOnlineCount(faction)
	local count = 0
	
	for _, ply in ipairs(player.GetAll()) do
		local char = ply:GetCharacter()
		if char and char:GetFaction() == faction and not (ply.IsAdminMode and ply:IsAdminMode()) then
			count = count + 1
		end
	end
	
	return count
end

function PLUGIN:CanFactionCapture(faction)
	local minOnline = self.factionMinOnline[faction]
	if (!minOnline) then return true end
	
	local online = self:GetFactionOnlineCount(faction)
	return online >= minOnline
end

function PLUGIN:CanCapture(factions)
	local count = 0
	for _ in pairs(factions) do
		count = count + 1
	end
	
	return count == 1
end

function PLUGIN:GiveRewards(faction, players)
	for _, ply in ipairs(players) do
		local char = ply:GetCharacter()
		if (char) then
			char:GiveMoney(self.moneyReward)
			ply:Notify("Вы получили " .. self.moneyReward .. " за захват точки!")
		end
	end
	
	if (ix.plugin.list["arsenal"]) then
		ix.plugin.list["arsenal"]:AddFactionSupply(faction, self.arsenalReward)
	end
	
	if (ix.plugin.list["vehicles"]) then
		ix.plugin.list["vehicles"]:AddFactionPoints(faction, self.vehicleReward)
	end
	
	-- Логируем выдачу наград
	local serverlogsPlugin = ix.plugin.list["serverlogs"]
	if (serverlogsPlugin) then
		local factionName = ix.faction.Get(faction).name or tostring(faction)
		local message = string.format("Фракция '%s' получила награду за захват: %d снабжения, %d очков техники, %d денег на игрока", 
			factionName, self.arsenalReward, self.vehicleReward, self.moneyReward)
		serverlogsPlugin:AddLog("CAPTURE_REWARD", message, nil, {
			faction = faction,
			factionName = factionName,
			arsenalReward = self.arsenalReward,
			vehicleReward = self.vehicleReward,
			moneyReward = self.moneyReward,
			playerCount = #players
		})
	end
end

function PLUGIN:ProcessCapture(point)
	local players = self:GetPlayersInRadius(point, self.captureRadius)
	
	if (#players == 0) then
		if point.capturingFaction != nil then
			point.capturingFaction = nil
			point.capturingPlayers = 0
			self:SyncCapturePoint(point)
		end
		return
	end
	
	local factions = self:GetFactionFromPlayers(players)
	
	if (!self:CanCapture(factions)) then
		if point.capturingFaction != nil or !point.captureBlocked then
			point.capturingFaction = nil
			point.capturingPlayers = 0
			point.captureBlocked = true
			self:SyncCapturePoint(point)
		end
		return
	end
	
	point.captureBlocked = false
	
	local faction, playerCount = next(factions)
	
	if (playerCount < self.minCapturePlayers) then
		if point.capturingFaction != nil or !point.captureBlocked then
			point.capturingFaction = nil
			point.capturingPlayers = 0
			point.captureBlocked = true
			self:SyncCapturePoint(point)
		end
		return
	end
	
	if (!self:CanFactionCapture(faction)) then
		if point.capturingFaction != nil or !point.captureBlocked then
			point.capturingFaction = nil
			point.capturingPlayers = 0
			point.captureBlocked = true
			self:SyncCapturePoint(point)
		end
		return
	end
	
	if (point.ownerFaction == faction) then
		if point.captureProgress != 100 or point.capturingFaction != nil then
			point.captureProgress = 100
			point.capturingFaction = nil
			point.capturingPlayers = 0
			self:SyncCapturePoint(point)
		end
		return
	end
	
	-- Логируем начало захвата при первом касании
	if (!point.capturingFaction or point.capturingFaction != faction) then
		-- Сбрасываем прогресс если начинается захват другой фракцией
		point.captureProgress = 0
		
		-- Уведомляем владельцев о захвате
		if (point.ownerFaction and point.ownerFaction != 0 and point.ownerFaction != faction) then
			local capturerName = ix.faction.Get(faction).name or "Противник"
			for _, v in ipairs(player.GetAll()) do
				local char = v:GetCharacter()
				if (char and char:GetFaction() == point.ownerFaction) then
					v:Notify("ВНИМАНИЕ! Ваша точка захватывается фракцией " .. capturerName .. "!")
				end
			end
		end

		local serverlogsPlugin = ix.plugin.list["serverlogs"]
		if (serverlogsPlugin) then
			local factionName = ix.faction.Get(faction).name or tostring(faction)
			local pointPos = tostring(point:GetPos())
			local message = string.format("Фракция '%s' начала захват точки (позиция: %s)", factionName, pointPos)
			serverlogsPlugin:AddLog("CAPTURE_START", message, nil, {
				faction = faction,
				factionName = factionName,
				playerCount = playerCount,
				pointPosition = pointPos
			})
		end
	end
	
	point.capturingFaction = faction
	point.capturingPlayers = playerCount
	
	local baseSpeed = 100 / self.captureBaseTime -- Progress per second
	local captureSpeed = baseSpeed * playerCount * FrameTime()
	
	point.captureProgress = (point.captureProgress or 0) + captureSpeed
	
	if (point.captureProgress >= 100) then
		point.captureProgress = 100
		local oldOwner = point.ownerFaction
		point.ownerFaction = faction
		point:SetOwnerFactionID(faction)
		point.capturingFaction = nil
		point.capturingPlayers = 0
		
		self:GiveRewards(faction, players)
		self:SaveData()
		
		-- Логируем успешный захват точки
		local serverlogsPlugin = ix.plugin.list["serverlogs"]
		if (serverlogsPlugin) then
			local factionName = ix.faction.Get(faction).name or tostring(faction)
			local pointPos = tostring(point:GetPos())
			local oldFactionData = oldOwner and ix.faction.Get(oldOwner)
			local oldFactionName = (oldFactionData and oldFactionData.name) or (oldOwner and tostring(oldOwner)) or "Нейтральная"
			local message = string.format("Точка захвачена фракцией '%s' (предыдущий владелец: %s)", factionName, oldFactionName)
			serverlogsPlugin:AddLog("CAPTURE_COMPLETE", message, nil, {
				faction = faction,
				factionName = factionName,
				oldOwner = oldOwner,
				oldOwnerName = oldFactionName,
				playerCount = playerCount,
				pointPosition = pointPos
			})
		end
		
		for _, ply in ipairs(player.GetAll()) do
			local char = ply:GetCharacter()
			if (char and char:GetFaction() == faction) then
				ply:Notify("Точка захвачена вашей фракцией!")
			end
		end
	end
	
	-- Only sync if progress changed significantly, something started/stopped, or a timer passed
	local curTime = CurTime()
	point.nextSync = point.nextSync or 0

	if (point.lastSyncProgress == nil or 
		math.abs(point.captureProgress - point.lastSyncProgress) >= 1 or 
		curTime >= point.nextSync) then
		
		self:SyncCapturePoint(point)
		point.lastSyncProgress = point.captureProgress
		point.nextSync = curTime + 0.2 -- Sync at most 5 times per second
	end
end

function PLUGIN:SyncCapturePoint(point)
	net.Start("ixCaptureSync")
		net.WriteEntity(point)
		net.WriteFloat(point.captureProgress or 0)
		net.WriteUInt(point.ownerFaction or 0, 8)
		net.WriteUInt(point.capturingFaction or 0, 8)
		net.WriteUInt(point.capturingPlayers or 0, 8)
		net.WriteBool(point.captureBlocked or false)
	net.Broadcast()
end

function PLUGIN:Think()
	if (!self.nextTickReward) then
		self.nextTickReward = CurTime() + (self.tickInterval or 300)
	elseif (CurTime() >= self.nextTickReward) then
		self:ProcessPeriodicRewards()
		self.nextTickReward = CurTime() + (self.tickInterval or 300)
	end

	for _, point in ipairs(self.capturePoints) do
		if (IsValid(point)) then
			self:ProcessCapture(point)
		end
	end
	
	if (!self.nextAutoSave or CurTime() >= self.nextAutoSave) then
		self:SaveData()
		self.nextAutoSave = CurTime() + 60
	end
end

function PLUGIN:RegisterCapturePoint(point)
	table.insert(self.capturePoints, point)
	point.captureProgress = 0
	point.ownerFaction = nil
	point:SetOwnerFactionID(0)
	point.capturingFaction = nil
	point.capturingPlayers = 0
end

function PLUGIN:EntityRemoved(entity)
	if (entity:GetClass() == "ix_capture_point") then
		for i, point in ipairs(self.capturePoints) do
			if (point == entity) then
				table.remove(self.capturePoints, i)
				break
			end
		end
	end
end

function PLUGIN:SaveData()
	local data = {}
	
	for _, point in ipairs(ents.FindByClass("ix_capture_point")) do
		if IsValid(point) then
			data[#data + 1] = {
				pos = point:GetPos(),
				angles = point:GetAngles(),
				ownerFaction = point.ownerFaction or 0,
				captureProgress = point.captureProgress or 0
			}
		end
	end
	
	self:SetData(data)
end

function PLUGIN:LoadData()
    local data = self:GetData()

    if (!data or #data == 0) then
        for _, v in ipairs(self.defaultPoints or {}) do
            local point = ents.Create("ix_capture_point")
            if IsValid(point) then
                point:SetPos(v.pos)
                point:SetAngles(v.ang)
                point:Spawn()

                timer.Simple(0.1, function()
                    if IsValid(point) then
                        point.ownerFaction = 0
                        point:SetOwnerFactionID(0)
                        point.captureProgress = 0
                    end
                end)
            end
        end

        return
    end

    for _, v in ipairs(data) do
        local point = ents.Create("ix_capture_point")
        if IsValid(point) then
            point:SetPos(v.pos)
            point:SetAngles(v.angles)
            point:Spawn()

            timer.Simple(0.1, function()
                if IsValid(point) then
                    point.ownerFaction = v.ownerFaction
                    point:SetOwnerFactionID(v.ownerFaction or 0)
                    point.captureProgress = v.captureProgress or 0

                    self:SyncCapturePoint(point)
                end
            end)
        end
    end
end

function PLUGIN:OnUnloaded()
	self:SaveData()
end

-- Подсчет захваченных точек каждой фракцией
function PLUGIN:GetCapturedPointsCount()
	local counts = {}
	
	for _, point in ipairs(self.capturePoints) do
		if IsValid(point) and point.ownerFaction and point.ownerFaction ~= 0 then
			counts[point.ownerFaction] = (counts[point.ownerFaction] or 0) + 1
		end
	end
	
	return counts
end

-- Периодическое начисление очков
function PLUGIN:ProcessPeriodicRewards()
	local counts = self:GetCapturedPointsCount()
	
	for faction, pointCount in pairs(counts) do
		if pointCount > 0 then
			local supplyReward = pointCount * self.tickSupplyPerPoint
			local vehicleReward = pointCount * self.tickVehiclePerPoint
			
			if ix.plugin.list["arsenal"] then
				ix.plugin.list["arsenal"]:AddFactionSupply(faction, supplyReward)
			end
			
			if ix.plugin.list["vehicles"] then
				ix.plugin.list["vehicles"]:AddFactionPoints(faction, vehicleReward)
			end
			
			local factionName = ix.faction.Get(faction).name or "Фракция"
			for _, ply in ipairs(player.GetAll()) do
				local char = ply:GetCharacter()
				if char and char:GetFaction() == faction then
					ply:Notify(string.format("Ваша фракция получила: +%d снабжения, +%d очков техники (точек: %d)", 
						supplyReward, vehicleReward, pointCount))
				end
			end
		end
	end
end

ix.command.Add("CaptureSystemSave", {
	description = "Сохранить точки захвата",
	superAdminOnly = true,
	OnRun = function(self, client)
		local plugin = ix.plugin.Get("capture")
		if (!plugin) then
			return "@commandNoExist"
		end
		
		plugin:SaveData()
		local count = #ents.FindByClass("ix_capture_point")
		client:Notify("Точки захвата сохранены (" .. count .. " шт.)")
	end
})
