#include "PgAttribute.h"
#include "QStringBuilder"
#include "SqlFormattingUtils.h"
#include "PgClass.h"
#include "PgDatabaseCatalog.h"
#include "PgSequenceContainer.h"
#include "PgTypeContainer.h"
#include "PgCollation.h"
#include "PgCollationContainer.h"
#include "Pgsql_oids.h"


PgAttribute::Identity PgAttribute::getIdentity() const
{
    switch (identity)
    {
        case '\0': return Identity::None;
        case 'a': return Identity::Always;
        case 'd': return Identity::ByDefault;
    }
    assert(false); // we shouldn't get here
    return {};
}

PgAttribute::Generated PgAttribute::getGenerated() const
{
    switch (generated)
    {
        case '\0': return Generated::None;
        case 's': return Generated::Stored;
    }
    assert(false); // we shouldn't get here
    return {};
}

QString PgAttribute::columnDefinition(const PgDatabaseCatalog &cat) const
{
	// create: column_name data_type [ COLLATE collation ] [ column_constraint [ ... ]
	// alter:  column_name data_type [ COLLATE collation ] [ column_constraint [ ... ]
	// constraints NULL/NOT NULL, DEFAULT, GENERATED other constraints will be ignored here a
	QString type_str;
    if (isSerial() && identity == '\0') {
		if (typid == Pgsql::int4_oid)
			type_str = "SERIAL";
		else if (typid == Pgsql::int8_oid)
			type_str = "BIGSERIAL";
	}
	else {
        type_str = getTypeDisplayString(cat, typid, typmod);
	}

	QString sql = quoteIdent(name) % " " % type_str;
	if (collation != InvalidOid) {
		auto&& col = cat.collations()->getByKey(collation);
		QString oname = col->objectName();
		if (oname != "default")
			sql += " COLLATE " % quoteIdent(oname);
	}

	if (notnull)
		sql += " NOT NULL";

    auto identity = getIdentity();
    auto generated = getGenerated();
    if (identity != Identity::None) {
		sql += " GENERATED ";
        if (identity == Identity::Always)
            sql += "ALWAYS";
        else if (identity == Identity::ByDefault)
            sql += "BY DEFAULT";
		sql += " AS IDENTITY";

        auto sequence = cat.sequences()->getByObjectNsAndName(serschema, sername);
        auto options_string = sequence->nonDefaultOptionsString();
        if (!options_string.isEmpty())
            sql += "(" % options_string % ")";
	}
    else if (generated != Generated::None)
    {
        if (generated == Generated::Stored)
            sql += " GENERATED ALWAYS AS " % defaultValue % " STORED";
    }
    else
    {
        if (hasdef && !isSerial())
            sql += " DEFAULT " % defaultValue;
    }

	return sql;
}

QString PgAttribute::alterTableAddColumn(const PgDatabaseCatalog &cat, const PgClass &table) const
{
	QString sql = "ALTER TABLE " % table.fullyQualifiedQuotedObjectName()
			% " ADD COLUMN " % columnDefinition(cat) % ";";
	return sql;
}

QString PgAttribute::alterTableDropColumn(const PgDatabaseCatalog &cat, const PgClass &table) const
{
	QString sql = "ALTER TABLE " % table.fullyQualifiedQuotedObjectName()
			% " DROP COLUMN " % quoteIdent(name) % ";";
	return sql;
}

QString PgAttribute::commentStatement(const PgDatabaseCatalog &cat, const PgClass &table) const
{
    if (description.isEmpty())
        return {};

    return "COMMENT ON COLUMN " % table.fullyQualifiedQuotedObjectName() % "."
                    % quoteIdent(name) % " IS " % escapeLiteral(description) % ";";
}
