#include "duckdb/parser/statement/update_statement.hpp"
#include "duckdb/parser/expression/function_expression.hpp"
#include "duckdb/parser/transformer.hpp"

#include <algorithm>

namespace duckdb {

unique_ptr<UpdateSetInfo> Transformer::TransformUpdateSetInfo(duckdb_libpgquery::PGList *target_list,
                                                              duckdb_libpgquery::PGNode *where_clause) {
	auto result = make_unique<UpdateSetInfo>();

	auto root = target_list;
	for (auto cell = root->head; cell != nullptr; cell = cell->next) {
		auto target = (duckdb_libpgquery::PGResTarget *)(cell->data.ptr_value);
		result->columns.emplace_back(target->name);
		result->expressions.push_back(TransformExpression(target->val));
	}
	result->condition = TransformExpression(where_clause);
	return result;
}

unique_ptr<UpdateStatement> Transformer::TransformUpdate(duckdb_libpgquery::PGNode *node) {
	auto stmt = reinterpret_cast<duckdb_libpgquery::PGUpdateStmt *>(node);
	D_ASSERT(stmt);

	auto result = make_unique<UpdateStatement>();
	if (stmt->withClause) {
		TransformCTE(reinterpret_cast<duckdb_libpgquery::PGWithClause *>(stmt->withClause), result->cte_map);
	}

	result->table = TransformRangeVar(stmt->relation);
			
	if (stmt->fromClause) {
		result->from_table = TransformFrom(stmt->fromClause);
	}

	result->set_info = TransformUpdateSetInfo(stmt->targetList, stmt->whereClause);

	// Grab and transform the returning columns from the parser.
	if (stmt->returningList) {
		Transformer::TransformExpressionList(*(stmt->returningList), result->returning_list);
	}

	if (context){
		if (result->table->type==TableReferenceType::BASE_TABLE) {
			auto unauthorized_columns  = context->authorizer->GetUnauthorizedColumns((BaseTableRef &)*result->table);
			for (auto &col:result->set_info->columns) {
				if (std::find(unauthorized_columns.begin(), unauthorized_columns.end(), col) != unauthorized_columns.end()){
					throw PermissionException("Access Denied on column %s !", col);
				}
			}
			context->authorizer->AuthorizeWhereClause(stmt->whereClause, unauthorized_columns);
		} else if (!context->authorizer->IsSuperuser())
			throw PermissionException("TODO: this Update statement is not authorised!");

		result->set_info->condition = context->authorizer->AddFortressExpression((TableRef &)*result->table, std::move(result->set_info->condition), duckdb_libpgquery::PG_PRIVILEGE_UPDATE);
	}

	return result;
}

} // namespace duckdb
