
#include "Webserv.hpp"

enum send_return
{
	SEND_IN_PROGRESS,
	SEND_COMPLETE,
	SEND_CLOSE,
};

void	Webserv::_response(Client *client)
{
	int ret = _send_response(client);

	if (g_last_signal)
		_handle_last_signal();

	if (ret == SEND_CLOSE)
	{
		_close_client(client->get_cl_fd());
	}
	else if (ret == SEND_COMPLETE)
	{
		if (client->get_rq_headers("Connection") == "close"
		|| client->status == 400 // TODO: Refactoring
		|| client->status == 408
		|| client->status == 413)
			_close_client(client->get_cl_fd());
		else
		{
			_epoll_update(client->get_cl_fd(), EPOLLIN, EPOLL_CTL_MOD);
			client->clear();
		}
	}
}

int	Webserv::_send_response(Client *client)
{
	ssize_t	ret;

	std::cerr << "send()\n";

	if (client->response.empty())
	{
		_append_base_headers(client);
		if (!client->status)
		{
			_construct_response(client);
			if (client->cgi_pipe_rfd)
				return SEND_IN_PROGRESS;
		}
	}
	else if (!client->cgi_output.empty())
	{
		/*DEBUG*/ std::cout << "\n" B_PURPLE "[response]:" RESET "\n"; ::print_special(client->response); std::cout << B_PURPLE "-----------" RESET "\n\n";
		/*DEBUG*/ std::cout << "\n" B_PURPLE "[script output]:" RESET "\n"; ::print_special(client->cgi_output); std::cout << B_PURPLE "-----------" RESET "\n\n";
		_check_script_output(client, client->cgi_output); // FD_CGI : adjust for client->cgi_output;
		if (client->status < 400)
			client->response += client->cgi_output;
		/*DEBUG*/ std::cout << "\n" B_PURPLE "[response + output]:" RESET "\n"; ::print_special(client->response); std::cout << B_PURPLE "-----------" RESET "\n\n";		
	}

	_insert_status_line(client);
	if (client->status >= 400)
		_error_html_response(client);

/*DEBUG*/ std::cout << "\n" B_PURPLE "[response + output + headers]:" RESET "\n"; ::print_special(client->response); std::cout << "\n" B_PURPLE "-----------" RESET "\n\n";

	// /* Debug */ std::cerr << "client->response.size() = " << client->response.size() << "\n"; // DEBUG
	ret = ::send(client->get_cl_fd(), client->response.c_str(), client->response.size(), 0);
	if (ret == -1)
	{
		std::perror("err send()");
		std::cerr << "client.fd =" << client->get_cl_fd() << "\n"; // DEBUG
		return SEND_CLOSE;
	}
	if (ret == 0) // actually never happen ?
	{
		std::cerr << "SEND RET 0 for client.fd =" << client->get_cl_fd() << "\n"; // DEBUG
		return SEND_CLOSE;
	}
	// /* Debug */ std::cerr << "ret send() = " << ret << "\n"; // DEBUG

	return SEND_COMPLETE;
}

void	Webserv::_append_base_headers(Client *client)
{
	client->response.append("Server: Webserv/0.1" CRLF);

	if (client->get_rq_headers("Connection") == "close"
	|| client->status == 400 // TODO: Refactoring
	|| client->status == 408
	|| client->status == 413)
		client->response.append("Connection: close" CRLF);
	else
		client->response.append("Connection: keep-alive" CRLF);
}

void	Webserv::_construct_response(Client *client)
{
	std::string	path;
	std::string	script_output;
	
	path = _replace_url_root(client, client->get_rq_abs_path());
	if (_is_cgi(client, path))
	{
		_exec_cgi(client);
		return;
	}
	_process_method(client, path);
}

void	Webserv::_process_method(Client *client, std::string &path)
{
//	std::cerr << "allow_methods = " << http_methods_to_str(client->assigned_location->allow_methods) << "\n"; // debug
	std::cerr << "Path again:			" << path << '\n'; // debug

	switch (client->get_rq_method())
	{
		case (GET):
			_get(client, path); break;
		case (POST):
			_post(client, path); break;
		case (DELETE):
			_delete(client, path); break;
		default:
			break;
	}
}

std::string	Webserv::_replace_url_root(Client *client, std::string path)
{
	// /* Debug */ std::cerr << "assigned_location->path =	" << client->assigned_location->path << "\n"; // debug
	// /* Debug */ std::cerr << "path before =			" << path << "\n"; // DEBUG
	if (client->assigned_location->path == "/")
		path.insert(0, client->assigned_location->root);
	else
		path.replace(0, client->assigned_location->path.size(), client->assigned_location->root);
	// /* Debug */ std::cerr << "path after =			" << path << "\n"; // DEBUG
	return path;
}

/*
	https://www.rfc-editor.org/rfc/rfc9112.html#name-status-line
*/
void	Webserv::_insert_status_line(Client *client)
{
	std::string	status_line;
	std::string	status = _http_status[client->status];
	if (status.empty())
		status = ::itos(client->status);

	status_line.append("HTTP/1.1 ");
	status_line.append(status);
	status_line.append(CRLF);
	client->response.insert(0, status_line);
}

void	Webserv::_error_html_response(Client *client)
{
	std::cout << "_error_html_response()\n";

	if (client->assigned_server
	&& !client->assigned_server->error_pages[client->status].empty()
	&& ::eval_file_access(client->assigned_server->error_pages[client->status], R_OK) == 0 )
	{
		_get_file(client, client->assigned_server->error_pages[client->status]);
	}
	else
	{
		std::string	status = _http_status[client->status];
		if (status.empty())
			status = "Error " + ::itos(client->status);
		std::string	html_page = HTML_ERROR;
		::replace_all_substr(html_page, STATUS_PLACEHOLDER, status);
		_append_body(client, html_page, "html");
	}
}

void	Webserv::_append_body(Client *client, const std::string &body, const std::string &file_extension)
{
	const std::string	&mime_type = _mime_types[file_extension];

	client->response.append("Content-Type: ");
	if (mime_type.empty())
		client->response.append(MIME_TYPE_DEFAULT);
	else
	{
		client->response.append(mime_type);
		if (mime_type.find("text/") != NPOS)
			client->response.append("; charset=UTF-8");
	}
	client->response.append(CRLF);

	client->response.append("Content-Length: ");
	std::string	tmp = ::itos(body.size());
	client->response.append(tmp);
	client->response.append(CRLF);

	client->response.append(CRLF);
	client->response.append(body);
}

