local wezterm = require("wezterm")

-- return table containing the meeting information
local M = {}

M.alert_popup_before_seconds = 15
M.alert_if_in_next_minutes = 30
M.theme_bg = "#2c323c"
M.theme_red = "#f92572"
M.nerd_font_meeting = "󰤙"

M.ical_buddy = "/usr/local/bin/icalBuddy"
M.ignored_cal = "Birthdays, Family, Contacts, School, Home, Canadian Holidays, Canadiens, Jours fériés au Canada"

-- Execute a shell command and return the output
-- @param command: The shell command to execute
-- @return string: The output of the shell command
local function execute_command(command)
	local f = io.popen(command)
	if f == nil then
		return ""
	end
	local result = f:read("*all")
	f:close()
	return result
end

-- Check if iCalBuddy is installed
-- @return boolean: True if iCalBuddy is installed, false otherwise
local function check_ical_buddy()
	local result = execute_command("test -f " .. M.ical_buddy)
	if result ~= "" then
		return true
	else
		wezterm.log_error("Error: iCalBuddy is not installed")
		return false
	end
end

-- Get the next meeting command
-- @param event_filter: The event filter to use with iCalBuddy
-- @return string: The iCalBuddy command to get the next meeting information
local function get_meeting_command(event_filter)
	return string.format(
		'%s --includeEventProps "title,datetime,attendees" --propertyOrder "title,datetime" --noCalendarNames --dateFormat "%%A" --includeOnlyEventsFromNowOn --limitItems 1 --excludeAllDayEvents --separateByDate --excludeCals "%s" %s',
		M.ical_buddy,
		M.ignored_cal,
		event_filter
	)
end

-- Get the next meeting from iCalBuddy
-- @return string: The next meeting information
local function get_next_meeting()
	local command = get_meeting_command("eventsToday")
	local next_meeting = execute_command(command)
	if next_meeting == "" then
		return nil
	end
	return next_meeting
end

-- Parse the iCalBuddy output
-- @param ical_output: The output from iCalBuddy
-- @return table: A table containing the meeting information such as title, start time, end time, and attendees list
local function parse_result(ical_output)
	local title, start_time, end_time, attendees
	for line in ical_output:gmatch("[^\r\n]+") do
		if line:match("%d%d:%d%d - %d%d:%d%d") then
			start_time = line:match("(%d%d:%d%d) -")
			end_time = line:match(" - (%d%d:%d%d)")
		end
		if line:match("attendees:") then
			attendees = line:gsub("attendees: ", ""):gsub("^%s*(.-)%s*$", "%1")
		end
	end
	title = ical_output:match("[^\r\n]+") -- Assumes the first line is the title
	return { title = title, start_time = start_time, end_time = end_time, attendees = attendees }
end

-- Calculate the time until the next meeting
-- @param start_time: The start time of the meeting in HH:MM format
-- @return table: A table containing the minutes until the meeting and the epoch difference
local function calculate_times(start_time)
	if not start_time then
		return { minutes_till_meeting = -1, epoch_diff = 0 }
	end

	local current_date = os.date("*t")
	local epoch_meeting = os.time({
		year = current_date.year,
		month = current_date.month,
		day = current_date.day,
		hour = tonumber(start_time:sub(1, 2)),
		min = tonumber(start_time:sub(4, 5)),
		sec = 0,
	})
	local epoch_now = os.time()
	local epoch_diff = epoch_meeting - epoch_now
	local minutes_till_meeting = math.floor(epoch_diff / 60)

	return { minutes_till_meeting = minutes_till_meeting, epoch_diff = epoch_diff }
end

-- Display a system notification with the meeting information
-- @param title: The title of the notification
-- @param message: The message of the notification
local function send_notification(title, message)
	local command = string.format('notify-send "%s" "%s"', title, message)
	os.execute(command)
end

-- Open a new tab with the meeting information
-- @param meeting: The meeting object containing the meeting information
local function open_meeting_tab(meeting)
	local meeting_info = string.format(
		"Title: %s\nStart Time: %s\nEnd Time: %s\nAttendees: %s",
		meeting.title,
		meeting.start_time,
		meeting.end_time,
		meeting.attendees
	)

	wezterm.mux.spawn_window({
		title = "Next Meeting",
		cwd = wezterm.home_dir,
		command = { "echo", meeting_info }, -- Display meeting info in the new tab
	})
end

-- Check for the next meeting and display a notification if it is starting soon
-- @return number: 0 if successful, 1 if iCalBuddy is not installed, -1 if there is no upcoming meeting
function M.main()
	if not check_ical_buddy() then
		return 1
	end

	local next_meeting = get_next_meeting()
	if not next_meeting then
		return -1
	end

	local meeting = parse_result(next_meeting)
	local times = calculate_times(meeting.start_time)
	if times.minutes_till_meeting == -1 then
		return 0
	end

	-- Display notification if meeting is starting soon
	if times.epoch_diff <= M.alert_popup_before_seconds then
		-- local meeting_message = string.format(
		-- 	"Title: %s\nStart Time: %s\nEnd Time: %s\nAttendees: %s",
		-- 	meeting.title,
		-- 	meeting.start_time,
		-- 	meeting.end_time,
		-- 	meeting.attendees
		-- )
		-- send_notification("Upcoming Meeting", meeting_message)
		open_meeting_tab(meeting)
	end

	return 0
end

-- Set up a timer to check for meetings periodically
-- @param interval_seconds: The interval in seconds to check for meetings periodically. Default is 60 seconds
function M.setup_update_timer(interval_seconds)
	interval_seconds = interval_seconds or 60 -- Default interval is 60 seconds

	local function update()
		local exit_code = M.main()
		if exit_code == -1 then
			wezterm.log_debug("No upcoming meeting found.")
		elseif exit_code == 1 then
			wezterm.log_error("Error: iCalBuddy is not installed")
		elseif exit_code ~= 0 then
			wezterm.log_error("Error checking for meetings")
		end
	end

	-- Initially update
	update()

	-- Set up periodic update timer
	wezterm.update_built_in_timer("meeting_notifier_update", interval_seconds, update)
end

-- Test the meeting notifier functions
-- This function is for testing purposes only
function M.test_meeting_notifier()
	-- Test if iCalBuddy is installed
	local icalbuddy_installed = check_ical_buddy()
	wezterm.log_info("iCalBuddy Check: ", icalbuddy_installed)

	-- Test getting the next meeting
	local next_meeting = get_next_meeting()
	wezterm.log_info("Next Meeting: ", next_meeting)

	if next_meeting and next_meeting ~= "" then
		-- Test parsing the meeting result
		local meeting = parse_result(next_meeting)
		wezterm.log_info("Parsed Meeting:")
		wezterm.log_info("Title: ", meeting.title)
		wezterm.log_info("Start Time: ", meeting.start_time)
		wezterm.log_info("End Time: ", meeting.end_time)
		wezterm.log_info("Attendees: ", meeting.attendees)

		-- Test calculating times
		local times = calculate_times(meeting.start_time)
		wezterm.log_info("Time Until Meeting: ", times.minutes_till_meeting, " minutes (", times.epoch_diff, " seconds)")

		-- Test notification function
		local meeting_message = string.format(
			"Title: %s\nStart Time: %s\nEnd Time: %s\nAttendees: %s",
			meeting.title,
			meeting.start_time,
			meeting.end_time,
			meeting.attendees
		)
		send_notification("Test Notification", meeting_message)
	else
		wezterm.log_info("No upcoming meeting found.")
	end
end

return M

