local Packages = script.Parent.Parent.Parent.Parent
local TextService = game:GetService("TextService")

local Roact = require(Packages.Roact)
local t = require(Packages.t)
local Cryo = require(Packages.Cryo)

local withStyle = require(Packages.UIBlox.Core.Style.withStyle)
local ImageSetComponent = require(Packages.UIBlox.Core.ImageSet.ImageSetComponent)
local Controllable = require(Packages.UIBlox.Core.Control.Controllable)
local ControlState = require(Packages.UIBlox.Core.Control.Enum.ControlState)

local FitTextLabel = require(Packages.FitFrame).FitTextLabel

-- TODO AVBURST-3748: Remove this soon after TextBoundsRoundUp is turned on to make the UIBlox places display
-- the same way as the App
local EngineFeatureTextBoundsRoundUp do
	local success, value = pcall(function()
		return game:GetEngineFeature("TextBoundsRoundUp")
	end)
	EngineFeatureTextBoundsRoundUp = success and value
end

local InputButton = Roact.PureComponent:extend("InputButton")

local validateProps = t.strictInterface({
	text = t.string,
	size = t.optional(t.UDim2),
	image = t.table,
	imageColor = t.Color3,
	fillImage = t.optional(t.table),
	fillImageSize = t.optional(t.UDim2),
	fillImageColor = t.optional(t.Color3),
	selectedColor = t.Color3,
	textColor = t.Color3,
	transparency = t.number,
	onActivated = t.callback,
	isDisabled = t.optional(t.boolean),
	layoutOrder = t.optional(t.number),
})

InputButton.defaultProps = {
	layoutOrder = 0,
	isDisabled = false,
}

local SELECTION_BUTTON_SIZE = 26
local HORIZONTAL_PADDING = 8

function InputButton:init()
	self.state = {
		outerImage = self.props.image,
		outerTransparency = 1,
		outerImageColor = self.props.imageColor,
		innerImage = self.props.image,
		innerImageColor = self.props.fillImageColor,
		innerTransparency = 1,
	}

	self.changeSprite = function(buttonState)
		if buttonState == ControlState.Hover then
			if not self.props.isDisabled then
				self:setState({
					outerImageColor = self.props.selectedColor
				})
			end
		elseif buttonState == ControlState.Default then
			self:setState({
				outerImageColor = self.props.imageColor
			})
		end
	end

	if not self.props.size then
		--Initalize height to SELECTION_BUTTON_SIZE as the height can't be smaller than the button height
		self.sizeBinding, self.updateSizeBinding = Roact.createBinding(UDim2.new(1, 0, 0, SELECTION_BUTTON_SIZE))

		self.textAbsoluteSizeChanged = function(rbx)
			local sizeY = SELECTION_BUTTON_SIZE
			if rbx.AbsoluteSize.Y > sizeY then
				sizeY = rbx.AbsoluteSize.Y
			end
			self.updateSizeBinding(UDim2.new(1, 0, 0, sizeY))
		end
	end
end

function InputButton:render()
	assert(validateProps(self.props))

	return withStyle(function(stylePalette)
		local font = stylePalette.Font
		local fontSize = font.Body.RelativeSize * font.BaseSize

		local textComponent
		local textComponentProps = {
			LayoutOrder = 2,
			BackgroundTransparency = 1,

			Text = self.props.text,
			TextXAlignment = Enum.TextXAlignment.Left,

			TextSize = fontSize,
			Font = font.Body.Font,
			TextWrapped = true,
			TextColor3 = self.props.textColor,
			TextTransparency = self.props.transparency,
		}

		if self.props.size then
			local size = self.props.size
			local frameSize = Vector2.new(size.X.Offset - SELECTION_BUTTON_SIZE - HORIZONTAL_PADDING, size.Y.Offset)
			local touchZoneWidth = TextService:GetTextSize(self.props.text, fontSize, font.Body.Font, frameSize).X
			if (not EngineFeatureTextBoundsRoundUp) and touchZoneWidth > 0 then
				-- GetTextSize documentation recommends to add a pixel of padding to the result to ensure no text is cut off
				-- Only add that extra padding if there is text to display
				touchZoneWidth = touchZoneWidth + 1
			end

			textComponent = "TextButton"
			textComponentProps = Cryo.Dictionary.join(textComponentProps, {
				Size = UDim2.new(0, touchZoneWidth, 1, 0),
				[Roact.Event.Activated] = not self.props.isDisabled and self.props.onActivated or nil,
			})
		else
			textComponent = FitTextLabel
			textComponentProps = Cryo.Dictionary.join(textComponentProps, {
				width = UDim.new(1, -SELECTION_BUTTON_SIZE - HORIZONTAL_PADDING),
				onActivated = self.props.onActivated,
				[Roact.Change.AbsoluteSize] = self.textAbsoluteSizeChanged,
			})
		end

		local fillImage = self.props.fillImage

		return Roact.createElement("Frame",	{
				Size = self.props.size or self.sizeBinding,
				BackgroundTransparency = 1,
				LayoutOrder = self.props.layoutOrder,
			}, {
				HorizontalLayout = Roact.createElement("UIListLayout", {
					SortOrder = Enum.SortOrder.LayoutOrder,
					FillDirection = Enum.FillDirection.Horizontal,
					Padding = UDim.new(0, HORIZONTAL_PADDING),
					VerticalAlignment = Enum.VerticalAlignment.Center
				}),
				Padding = Roact.createElement("UIPadding", {
					PaddingLeft = UDim.new(0, HORIZONTAL_PADDING),
				}),
				InputButtonImage = Roact.createElement(Controllable, {
					controlComponent = {
					component = ImageSetComponent.Button,
						props = {
							BackgroundTransparency = 1,
							Size = UDim2.new(0, SELECTION_BUTTON_SIZE, 0, SELECTION_BUTTON_SIZE),
							Image = self.props.image,
							ImageTransparency = self.props.transparency,
							ScaleType = self.props.buttonSliceType,
							SliceCenter = self.props.buttonSliceCenter,
							ImageColor3 = self.state.outerImageColor,
							[Roact.Event.Activated] = self.props.onActivated,
							LayoutOrder = 1,
						},
						children = {
							InputFillImage = fillImage and Roact.createElement(ImageSetComponent.Label, {
								BackgroundTransparency = 1,
								Size = self.props.fillImageSize,
								Image = fillImage,
								ImageTransparency = self.props.transparency,
								ImageColor3 = self.props.fillImageColor,
								AnchorPoint = Vector2.new(0.5, 0.5),
								Position = UDim2.new(0.5, 0, 0.5, 0),
							})
						}
					},
					isDisabled = self.props.isDisabled,

					onStateChanged = function(_, newState)
						self.changeSprite(newState)
					end,
				}),
				-- Only create this element if there is text to display
				InputButtonText = (self.props.text ~= "") and Roact.createElement(Controllable, {
					controlComponent =
					{
						component = textComponent,
						props = textComponentProps,
					},
					isDisabled = self.props.isDisabled,
					onStateChanged = function(_, newState)
						self.changeSprite(newState)
					end,
				})
			}
		)
	end)
end

return InputButton
