--!strict

-- Services
local HttpService = game:GetService("HttpService")
local ReplicatedStorage = game:GetService("ReplicatedStorage")

-- Remote Events
local connections = ReplicatedStorage:WaitForChild("connections")
local streamInfoEvent = connections:WaitForChild("StreamInfoEvent")
local streamFrameEvent = connections:WaitForChild("StreamFrameEvent")
local streamResolutionChanged = connections:WaitForChild("StreamResolutionChanged")

local requestStreamInfo = connections:WaitForChild("RequestStreamInfo")

-- Configuration
local INFO_URL: string = "http://coolpixels.net:5678/info"
local FRAME_URL: string = "http://coolpixels.net:5678/frame"
local RECONNECT_DELAY_SECONDS: number = 1 -- Seconds to wait before retrying a failed connection/info fetch

print("Stream Manager: Initializing...")

-- --- Fetch Stream Info ---
-- This function will continuously try to fetch and validate stream info
-- It will only return once valid info is received.
local function fetchStreamInfo(): { fps: number, height: number, width: number }
	local currentInfo: { fps: number, height: number, width: number } | nil = nil
	local attempts: number = 0

	while currentInfo == nil do
		attempts += 1
		print(string.format("Stream Manager: Attempting to fetch stream info (Attempt %d)...", attempts))

		local success, result = pcall(function()
			local infoJson = HttpService:GetAsync(INFO_URL)
			return HttpService:JSONDecode(infoJson)
		end)

		if success then
			if typeof(result) == "table" and result.fps and result.height and result.width then
				currentInfo = result
				print(
					string.format(
						"Stream Manager: Successfully fetched stream info: Width=%d, Height=%d, FPS=%d",
						currentInfo.width,
						currentInfo.height,
						currentInfo.fps
					)
				)
			else
				warn("Stream Manager: Fetched info is not in expected format:", result)
			end
		else
			warn("Stream Manager: Failed to fetch stream info:", result)
		end

		if currentInfo == nil then
			print(string.format("Stream Manager: Retrying info fetch in %d second(s)...", RECONNECT_DELAY_SECONDS))
			task.wait(RECONNECT_DELAY_SECONDS)
		end
	end
	return currentInfo
end

-- --- Initial Setup ---
local currentInfo = fetchStreamInfo() -- Initial fetch of stream info
local targetFPS = currentInfo.fps
local frameDelay = 1 / targetFPS

-- Send initial stream info to all clients
streamInfoEvent:FireAllClients(currentInfo)
print("Stream Manager: Sent initial stream info to all clients")

-- Handle requests for stream info from clients
requestStreamInfo.OnServerEvent:Connect(function(player)
	print(string.format("Stream Manager: Client %s requested stream info", player.Name))
	if currentInfo then
		streamInfoEvent:FireClient(player, currentInfo)
		print(string.format("Stream Manager: Sent stream info to %s", player.Name))
	else
		warn(string.format("Stream Manager: Cannot send stream info to %s - no valid info available", player.Name))
	end
end)

-- --- Main Stream Loop ---
local frameCount: number = 0
while true do
	frameCount += 1
	local frameData: string | nil = nil

	-- Attempt to fetch the current frame
	print(string.format("Stream Manager: Fetching frame %d from %s...", frameCount, FRAME_URL))
	local success, result = pcall(function()
		return HttpService:GetAsync(FRAME_URL)
	end)

	if success then
		if typeof(result) == "string" then
			frameData = result
			print(string.format("Stream Manager: Successfully fetched frame %d (%d bytes).", frameCount, #frameData))

			-- --- Check for Resolution Changes ---
			-- Re-fetch info to see if resolution has changed
			local newInfo: { fps: number, height: number, width: number } | nil = nil
			local infoSuccess, infoResult = pcall(function()
				local infoJson = HttpService:GetAsync(INFO_URL)
				return HttpService:JSONDecode(infoJson)
			end)

			if
				infoSuccess
				and typeof(infoResult) == "table"
				and infoResult.fps
				and infoResult.height
				and infoResult.width
			then
				newInfo = infoResult
			else
				warn("Stream Manager: Failed to re-fetch info for resolution check:", infoResult)
			end

			if newInfo and (newInfo.width ~= currentInfo.width or newInfo.height ~= currentInfo.height) then
				print(
					string.format(
						"Stream Manager: Resolution changed from %dx%d to %dx%d. Notifying clients.",
						currentInfo.width,
						currentInfo.height,
						newInfo.width,
						newInfo.height
					)
				)

				currentInfo = newInfo -- Update current info
				targetFPS = currentInfo.fps
				frameDelay = 1 / targetFPS

				-- Notify all clients of resolution change
				streamResolutionChanged:FireAllClients(currentInfo)

				-- IMPORTANT FIX: Discard the current frame (which belongs to the old resolution)
				-- and continue to the next loop iteration to fetch a new frame with the new resolution.
				warn("Stream Manager: Discarding current frame as resolution changed. Fetching new frame...")
				task.wait(frameDelay) -- Wait for the expected frame time before fetching the *next* frame
				continue -- Skip the rest of the current loop iteration
			end

			-- --- Send Frame Data to Clients ---
			print(string.format("Stream Manager: Sending frame %d to all clients...", frameCount))
			streamFrameEvent:FireAllClients(frameData, currentInfo.width, currentInfo.height)
			print(string.format("Stream Manager: Frame %d sent. Waiting for next frame...", frameCount))
		else
			warn(
				string.format(
					"Stream Manager: HttpService:GetAsync returned non-string data for frame %d: %s. Retrying after delay.",
					frameCount,
					tostring(result)
				)
			)
			print(string.format("Stream Manager: Retrying frame fetch in %d second(s)...", RECONNECT_DELAY_SECONDS))
			task.wait(RECONNECT_DELAY_SECONDS)
		end
	else
		warn(
			string.format(
				"Stream Manager: Failed to fetch frame %d: %s. Stream might be down. Retrying after delay.",
				frameCount,
				tostring(result)
			)
		)
		print(string.format("Stream Manager: Retrying frame fetch in %d second(s)...", RECONNECT_DELAY_SECONDS))
		task.wait(RECONNECT_DELAY_SECONDS)
		-- If frame fetch fails, it's a good idea to re-check info as well,
		-- in case the server restarted with different settings.
		currentInfo = fetchStreamInfo() -- This will block until new info is received
		-- Re-evaluate targetFPS, frameDelay based on newInfo
		targetFPS = currentInfo.fps
		frameDelay = 1 / targetFPS
		-- Send updated info to clients
		streamInfoEvent:FireAllClients(currentInfo)
	end

	-- Wait to maintain target FPS
	task.wait(frameDelay)
end
