local assert_error = require("lapis.application").assert_error
local assert_valid = require("lapis.validate").assert_valid
local csrf         = require "lapis.csrf"
local capture      = require "utils.capture"
local format       = require "utils.text_formatter"
local generate     = require "utils.generate"
local process      = require "utils.request_processor"
local Posts        = require "models.posts"

return {
	before = function(self)

		-- Get board
		for _, board in ipairs(self.boards) do
			if board.name == self.params.uri_name then
				self.board = board
				break
			end
		end

		-- Board not found
		if not self.board or self.params.page and not tonumber(self.params.page) then
			return self:write({ redirect_to = self:url_for("web.pages.index") })
		end

		-- Get announcements
		-- TODO: Consolidate these into a single call
		self.announcements        = assert_error(capture.get(self:url_for("api.announcements.announcement", { uri_id="global" })))
		local board_announcements = assert_error(capture.get(self:url_for("api.boards.announcements", { uri_name=self.params.uri_name })))
		for _, announcement in ipairs(board_announcements) do
			table.insert(self.announcements, announcement)
		end

		-- Page title
		self.page_title = string.format(
			"/%s/ - %s",
			self.board.name,
			self.board.title
		)

		-- Flag comments as required or not
		self.comment_flag = self.board.thread_comment

		-- Generate CSRF token
		self.csrf_token = csrf.generate_token(self)

		-- Current page
		self.params.page = self.params.page or 1

		-- Get threads
		local response = assert_error(capture.get(self:url_for("api.boards.threads", { uri_name=self.params.uri_name, uri_page=self.params.page })))
		self.threads   = response.threads
		self.pages     = response.pages

		-- Get posts
		for _, thread in ipairs(self.threads) do
			-- Get posts visible on the board index
			thread.posts = Posts:get_index_posts(thread.id)

			-- Get hidden posts
			thread.hidden = Posts:count_hidden_posts(thread.id)

			-- Get op
			local op = thread.posts[#thread.posts]
			if not op then
				assert_error(false, { "err_orphaned", { thread.id } })
			end

			thread.url = self:url_for("web.boards.thread", { uri_name=self.board.name, thread=op.post_id })

			-- Format comments
			for _, post in ipairs(thread.posts) do
				-- OP gets a thread tag
				if post.post_id == op.post_id then
					post.thread = post.post_id
				end

				post.name            = post.name or self.board.anon_name
				post.reply           = self:url_for("web.boards.thread", { uri_name=self.board.name, thread=op.post_id, anchor="q", id=post.post_id })
				post.link            = self:url_for("web.boards.thread", { uri_name=self.board.name, thread=op.post_id, anchor="p", id=post.post_id })
				post.remix           = self:url_for("web.boards.thread", { uri_name=self.board.name, thread=op.post_id, anchor="r", id=post.post_id })
				post.timestamp       = os.date("%Y-%m-%d (%a) %H:%M:%S", post.timestamp)
				post.file_size       = math.floor(post.file_size / 1024)
				post.file_dimensions = ""

				if post.file_width > 0 and post.file_height > 0 then
					post.file_dimensions = string.format(", %dx%d", post.file_width, post.file_height)
				end

				if not post.file_duration or post.file_duration == "0" then
					post.file_duration = ""
				else
					post.file_duration = string.format(", %s", post.file_duration)
				end

				if post.file_path then
					local name, ext = post.file_path:match("^(.+)(%..+)$")
					ext = string.lower(ext)

					-- Get thumbnail URL
					if post.file_type == "audio" then
						if post == thread.posts[#thread.posts] then
							post.thumb = self:format_url(self.static_url, "op_audio.png")
						else
							post.thumb = self:format_url(self.static_url, "post_audio.png")
						end
					elseif post.file_type == "image" then
						if post.file_spoiler then
							if post == thread.posts[#thread.posts] then
								post.thumb = self:format_url(self.static_url, "op_spoiler.png")
							else
								post.thumb = self:format_url(self.static_url, "post_spoiler.png")
							end
						else
							if ext == ".webm" or ext == ".svg" then
								post.thumb = self:format_url(self.files_url, self.board.name, 's' .. name .. '.png')
							else
								post.thumb = self:format_url(self.files_url, self.board.name, 's' .. post.file_path)
							end
						end
					end

					post.file_path = self:format_url(self.files_url, self.board.name, post.file_path)
				end

				-- Process comment
				if post.comment then
					local comment = post.comment
					comment = format.sanitize(comment)
					comment = format.quote(comment, self, self.board, post)
					comment = format.green_text(comment)
					comment = format.blue_text(comment)
					comment = format.spoiler(comment)
					comment = format.new_lines(comment)
					post.comment = comment
				else
					post.comment = ""
				end
			end
		end
	end,
	on_error = function(self)
		self.errors = generate.errors(self.i18n, self.errors)
		return { render = "board"}
	end,
	GET = function()
		return { render = "board" }
	end,
	POST = function(self)
		-- Validate CSRF token
		csrf.assert_token(self)

		local board_url = self:url_for("web.boards.board", { uri_name=self.board.name })

		-- Submit new thread
		if self.params.submit then
			-- Validate user input
			assert_valid(self.params, {
				{ "name",    max_length=255 },
				{ "subject", max_length=255 },
				{ "options", max_length=255 },
				{ "comment", max_length=self.text_size }
			})

			-- Validate post
			local post = assert_error(process.create_thread(self.params, self.session, self.board))
			return { redirect_to = self:url_for("web.boards.thread", { uri_name=self.board.name, thread=post.post_id, anchor="p", id=post.post_id }) }
		end

		-- Delete thread
		if self.params.delete and self.params.thread_id then
			-- Validate user input
			assert_valid(self.params, {
				{ "post_id", exists=true }
			})

			-- Validate deletion
			assert_error(process.delete_thread(self.params, self.session, self.board))
			return { redirect_to = board_url }
		end

		-- Delete post
		if self.params.delete and not self.params.thread_id then
			-- Validate user input
			assert_valid(self.params, {
				{ "post_id", exists=true }
			})

			-- Validate deletion
			assert_error(process.delete_post(self.params, self.session, self.board))
			return { redirect_to = board_url }
		end

		-- Report post
		if self.params.report then
			-- Validate user input
			assert_valid(self.params, {
				{ "board",   exists=true },
				{ "post_id", exists=true }
			})

			-- Validate report
			assert_error(process.report_post(self.params, self.board))
			return { redirect_to = board_url }
		end

		-- Admin commands
		if self.session.admin or self.session.mod then
			-- Sticky thread
			if self.params.sticky then
				assert_error(process.sticky_thread(self.params, self.board))
				return { redirect_to = board_url }
			end

			-- Lock thread
			if self.params.lock then
				assert_error(process.lock_thread(self.params, self.board))
				return { redirect_to = board_url }
			end

			-- Save thread
			if self.params.save then
				assert_error(process.save_thread(self.params, self.board))
				return { redirect_to = board_url }
			end

			-- Override thread
			if self.params.override then
				assert_error(process.override_thread(self.params, self.board))
				return { redirect_to = board_url }
			end

			-- Ban user
			if self.params.ban then
				assert_error(process.ban_user(self.params, self.board))
				return { redirect_to = board_url }
			end
		end

		return { redirect_to = board_url }
	end
}
