#include "ast/generic_copy_option.hpp"
#include "duckdb/parser/expression/cast_expression.hpp"
#include "duckdb/parser/expression/columnref_expression.hpp"
#include "duckdb/parser/expression/operator_expression.hpp"
#include "duckdb/parser/statement/attach_statement.hpp"
#include "transformer/peg_transformer.hpp"

namespace duckdb {

unique_ptr<SQLStatement> PEGTransformerFactory::TransformAttachStatement(PEGTransformer &transformer,
                                                                         optional_ptr<ParseResult> parse_result) {
	auto result = make_uniq<AttachStatement>();
	auto info = make_uniq<AttachInfo>();

	auto &list_pr = parse_result->Cast<ListParseResult>();
	auto or_replace = list_pr.Child<OptionalParseResult>(1).HasResult();
	auto if_not_exists = list_pr.Child<OptionalParseResult>(2).HasResult();

	if (or_replace && if_not_exists) {
		throw ParserException("Cannot specify both OR REPLACE and IF NOT EXISTS at the same time");
	}

	if (or_replace) {
		info->on_conflict = OnCreateConflict::REPLACE_ON_CONFLICT;
	} else if (if_not_exists) {
		info->on_conflict = OnCreateConflict::IGNORE_ON_CONFLICT;
	} else {
		info->on_conflict = OnCreateConflict::ERROR_ON_CONFLICT;
	}

	info->path = transformer.Transform<string>(list_pr.Child<ListParseResult>(4));
	transformer.TransformOptional<string>(list_pr, 5, info->name);
	vector<GenericCopyOption> copy_options;
	transformer.TransformOptional<vector<GenericCopyOption>>(list_pr, 6, copy_options);
	for (auto &copy_option : copy_options) {
		if (copy_option.expression) {
			info->parsed_options[copy_option.name] = std::move(copy_option.expression);
			continue;
		}
		if (copy_option.children.empty()) {
			info->options[copy_option.name] = Value(true);
		} else if (copy_option.children.size() == 1) {
			auto val = copy_option.children[0];
			if (val.IsNull()) {
				throw BinderException("NULL is not supported as a valid option for ATTACH option \"%s\"",
				                      copy_option.name);
			}
			info->options[copy_option.name] = std::move(copy_option.children[0]);
		} else {
			throw ParserException("Option %s can only have one argument", copy_option.name);
		}
	}
	result->info = std::move(info);
	return std::move(result);
}

string PEGTransformerFactory::TransformAttachAlias(PEGTransformer &transformer,
                                                   optional_ptr<ParseResult> parse_result) {
	auto &list_pr = parse_result->Cast<ListParseResult>();
	auto &col_id = list_pr.Child<ListParseResult>(1).Child<ChoiceParseResult>(0);
	if (col_id.result->type == ParseResultType::IDENTIFIER) {
		return col_id.result->Cast<IdentifierParseResult>().identifier;
	}
	return transformer.Transform<string>(col_id.result);
}

vector<GenericCopyOption> PEGTransformerFactory::TransformAttachOptions(PEGTransformer &transformer,
                                                                        optional_ptr<ParseResult> parse_result) {
	auto &list_pr = parse_result->Cast<ListParseResult>();
	return transformer.Transform<vector<GenericCopyOption>>(list_pr.Child<ListParseResult>(0));
}

vector<GenericCopyOption>
PEGTransformerFactory::TransformGenericCopyOptionList(PEGTransformer &transformer,
                                                      optional_ptr<ParseResult> parse_result) {
	vector<GenericCopyOption> result;
	auto &list_pr = parse_result->Cast<ListParseResult>();
	auto extract_parens = ExtractResultFromParens(list_pr.Child<ListParseResult>(0));
	auto option_list = ExtractParseResultsFromList(extract_parens);
	for (auto &option : option_list) {
		result.push_back(transformer.Transform<GenericCopyOption>(option));
	}
	return result;
}

GenericCopyOption PEGTransformerFactory::TransformGenericCopyOption(PEGTransformer &transformer,
                                                                    optional_ptr<ParseResult> parse_result) {
	GenericCopyOption copy_option;

	auto &list_pr = parse_result->Cast<ListParseResult>();
	copy_option.name = StringUtil::Lower(list_pr.Child<IdentifierParseResult>(0).identifier);
	auto &optional_expression = list_pr.Child<OptionalParseResult>(1);
	if (optional_expression.HasResult()) {
		auto expression = transformer.Transform<unique_ptr<ParsedExpression>>(optional_expression.optional_result);
		if (expression->GetExpressionType() == ExpressionType::VALUE_CONSTANT) {
			copy_option.children.push_back(Value(expression->Cast<ConstantExpression>().value));
		} else if (expression->GetExpressionType() == ExpressionType::COLUMN_REF) {
			copy_option.children.push_back(Value(expression->Cast<ColumnRefExpression>().GetColumnName()));
		} else if (expression->GetExpressionType() == ExpressionType::PLACEHOLDER) {
			auto &op_expr = expression->Cast<OperatorExpression>();
			for (auto &child : op_expr.children) {
				if (child->GetExpressionClass() == ExpressionClass::CONSTANT) {
					copy_option.children.push_back(Value(child->Cast<ConstantExpression>().value));
				} else if (child->GetExpressionClass() == ExpressionClass::COLUMN_REF) {
					copy_option.children.push_back(Value(child->Cast<ColumnRefExpression>().GetColumnName()));
				} else {
					throw InternalException("Unexpected expression type %s encountered for GenericCopyOption",
					                        ExpressionClassToString(child->GetExpressionClass()));
				}
			}
		} else if (expression->GetExpressionType() == ExpressionType::FUNCTION) {
			copy_option.expression = std::move(expression);
		} else if (expression->GetExpressionType() == ExpressionType::STAR) {
			copy_option.children.push_back(Value("*"));
		} else if (expression->GetExpressionType() == ExpressionType::OPERATOR_CAST) {
			auto &cast_expr = expression->Cast<CastExpression>();
			if (cast_expr.child->GetExpressionClass() == ExpressionClass::CONSTANT) {
				auto &const_expr = cast_expr.child->Cast<ConstantExpression>();
				if (const_expr.value.GetValue<string>() == "t") {
					copy_option.children.push_back(Value(true));
				} else if (const_expr.value.GetValue<string>() == "f") {
					copy_option.children.push_back(Value(false));
				} else {
					copy_option.expression = std::move(expression);
				}
			} else {
				copy_option.expression = std::move(expression);
			}
		} else {
			throw NotImplementedException("Unrecognized expression type %s",
			                              ExpressionTypeToString(expression->GetExpressionType()));
		}
	}
	return copy_option;
}

string PEGTransformerFactory::TransformDatabasePath(PEGTransformer &transformer,
                                                    optional_ptr<ParseResult> parse_result) {
	auto &list_pr = parse_result->Cast<ListParseResult>();
	return transformer.Transform<string>(list_pr.Child<StringLiteralParseResult>(0));
}

} // namespace duckdb
