local hex_to_hsluv = require("kanagawa-paper.lib.hsluv").hex_to_hsluv
local hsluv_to_rgb = require("kanagawa-paper.lib.hsluv").hsluv_to_rgb
local hsluv_to_hex = require("kanagawa-paper.lib.hsluv").hsluv_to_hex
local hex_to_rgb = require("kanagawa-paper.lib.hsluv").hex_to_rgb
local rgb_to_hsluv = require("kanagawa-paper.lib.hsluv").rgb_to_hsluv

---@class HSLuvColor
local Color = {}
local Color_mt = {
	__index = Color,
	__tostring = function(self)
		return self:to_hex()
	end,
}

local function none_to_hex()
	return "NONE"
end

---Create a new HSLuv color object from a RGB hex string
---@param hex string Hex color
---@return HSLuvColor
function Color.new(hex)
	if hex:lower() == "none" then
		return setmetatable({ H = 0, S = 0, L = 0, to_hex = none_to_hex }, Color_mt)
	end
	local H, S, L = unpack(hex_to_hsluv(hex))
	return setmetatable({ H = H, S = S, L = L }, Color_mt)
end

function Color:to_rgb()
	return hsluv_to_rgb({ self.H, self.S, self.L })
end

function Color:to_hex()
	return hsluv_to_hex({ self.H, self.S, self.L })
end

local function blendRGB(a, b, r)
	local c = {}
	for i = 1, 3 do
		c[i] = math.sqrt((1 - r) * math.pow(a[i], 2) + r * math.pow(b[i], 2))
	end
	return c
end

--- Blend Color with another color (hex)
---@param b string Hex color
---@param r number Blend ratio [0, 1]
---@return HSLuvColor
function Color:blend(b, r)
	if b:lower() == "none" then
		return self
	end
	local c = blendRGB(self:to_rgb(), hex_to_rgb(b), r)
	self.H, self.S, self.L = unpack(rgb_to_hsluv(c))
	return self
end

---@param r number Brighten ratio [-1, 1]
---@param bg? string background color, if light, r = -r
---@return HSLuvColor
function Color:brighten(r, bg)
	if bg and bg:lower() == "none" then
		return self
	end
	local bg_lightness = bg and hex_to_hsluv(bg)[3] or 0
	r = bg_lightness > 50 and -r or r

	local lspace = r > 0 and 100 - self.L or self.L
	self.L = self.L + lspace * r
	return self
end

---@param r number Saturate ratio [-1, 1]
---@return HSLuvColor
function Color:saturate(r)
	local lspace = r > 0 and 100 - self.S or self.S
	self.S = self.S + lspace * r
	return self
end

local M = {}
return setmetatable(M, {
	__call = function(_, ...)
		return Color.new(...)
	end,
})
