import { relations, sql } from 'drizzle-orm';
import {
	mysqlTable,
	int,
	text,
	tinyint,
	datetime,
	varchar,
	unique,
	timestamp,
	mysqlEnum,
	index,
	type AnyMySqlColumn
} from 'drizzle-orm/mysql-core';

export const auditLog = mysqlTable('audit_log', {
	id: int('id').autoincrement().notNull(),
	action: text('action').notNull(),
	content: text('content'),
	actor_ip: text('actor_ip'),
	actor_ua: text('actor_ua'),
	flagged: tinyint('flagged').default(0).notNull(),
	created_at: datetime('created_at', { mode: 'date', fsp: 6 })
		.default(sql`current_timestamp(6)`)
		.notNull(),
	actorId: int('actorId').references(() => user.id, { onDelete: 'set null' })
});

export const document = mysqlTable('document', {
	id: int('id').autoincrement().notNull(),
	title: text('title').notNull(),
	slug: text('slug').notNull(),
	body: text('body').notNull(),
	authorId: int('authorId').references(() => user.id),
	created_at: datetime('created_at', { mode: 'date', fsp: 6 })
		.default(sql`current_timestamp(6)`)
		.notNull(),
	updated_at: datetime('updated_at', { mode: 'date', fsp: 6 })
		.default(sql`current_timestamp(6)`)
		.notNull()
});

export const oauth2Client = mysqlTable(
	'o_auth2_client',
	{
		id: int('id').autoincrement().notNull(),
		client_id: varchar('client_id', { length: 36 }).notNull(),
		client_secret: text('client_secret').notNull(),
		title: varchar('title', { length: 255 }).notNull(),
		description: text('description'),
		scope: text('scope'),
		grants: text('grants').default('authorization_code').notNull(),
		activated: tinyint('activated').default(0).notNull(),
		verified: tinyint('verified').default(0).notNull(),
		pictureId: int('pictureId').references(() => upload.id, { onDelete: 'set null' }),
		ownerId: int('ownerId').references(() => user.id, { onDelete: 'set null' }),
		created_at: datetime('created_at', { mode: 'date', fsp: 6 })
			.default(sql`current_timestamp(6)`)
			.notNull(),
		updated_at: datetime('updated_at', { mode: 'date', fsp: 6 })
			.default(sql`current_timestamp(6)`)
			.notNull()
	},
	(table) => {
		return {
			IDX_e9d16c213910ad57bd05e97b42: unique('IDX_e9d16c213910ad57bd05e97b42').on(table.client_id)
		};
	}
);

export type OAuth2Client = typeof oauth2Client.$inferSelect;
export type NewOAuth2Client = typeof oauth2Client.$inferInsert;

export const oauth2ClientAuthorization = mysqlTable('o_auth2_client_authorization', {
	id: int('id').autoincrement().notNull(),
	scope: text('scope'),
	expires_at: timestamp('expires_at', { mode: 'date' })
		.default(sql`current_timestamp()`)
		.notNull(),
	clientId: int('clientId').references(() => oauth2Client.id, { onDelete: 'cascade' }),
	userId: int('userId').references(() => user.id, { onDelete: 'cascade' }),
	created_at: datetime('created_at', { mode: 'date', fsp: 6 })
		.default(sql`current_timestamp(6)`)
		.notNull()
});

export type OAuth2ClientAuthorization = typeof oauth2ClientAuthorization.$inferSelect;
export type NewOAuth2ClientAuthorization = typeof oauth2ClientAuthorization.$inferInsert;

export const oauth2ClientUrl = mysqlTable('o_auth2_client_url', {
	id: int('id').autoincrement().notNull(),
	url: varchar('url', { length: 255 }).notNull(),
	type: mysqlEnum('type', ['redirect_uri', 'terms', 'privacy', 'website']).notNull(),
	created_at: timestamp('created_at', { fsp: 6, mode: 'date' })
		.default(sql`current_timestamp(6)`)
		.notNull(),
	updated_at: timestamp('updated_at', { fsp: 6, mode: 'date' })
		.default(sql`current_timestamp(6)`)
		.notNull(),
	clientId: int('clientId').references(() => oauth2Client.id, { onDelete: 'cascade' })
});

export type OAuth2ClientUrl = typeof oauth2ClientUrl.$inferSelect;
export type NewOAuth2ClientUrl = typeof oauth2ClientUrl.$inferInsert;

export const oauth2Token = mysqlTable('o_auth2_token', {
	id: int('id').autoincrement().notNull(),
	type: mysqlEnum('type', ['code', 'access_token', 'refresh_token']).notNull(),
	token: text('token').notNull(),
	scope: text('scope'),
	expires_at: timestamp('expires_at', { mode: 'date' })
		.default(sql`current_timestamp()`)
		.notNull(),
	userId: int('userId').references(() => user.id, { onDelete: 'cascade' }),
	clientId: int('clientId').references(() => oauth2Client.id, { onDelete: 'cascade' }),
	nonce: text('nonce'),
	created_at: datetime('created_at', { mode: 'date', fsp: 6 })
		.default(sql`current_timestamp(6)`)
		.notNull(),
	updated_at: datetime('updated_at', { mode: 'date', fsp: 6 })
		.default(sql`current_timestamp(6)`)
		.notNull(),
	pcke: text('pcke')
});

export type OAuth2Token = typeof oauth2Token.$inferSelect;
export type NewOAuth2Token = typeof oauth2Token.$inferInsert;

export const privilege = mysqlTable('privilege', {
	id: int('id').autoincrement().notNull(),
	name: text('name').notNull()
});

export const upload = mysqlTable('upload', {
	id: int('id').autoincrement().notNull(),
	original_name: varchar('original_name', { length: 255 }).notNull(),
	mimetype: varchar('mimetype', { length: 255 }).notNull(),
	file: varchar('file', { length: 255 }).notNull(),
	uploaderId: int('uploaderId').references((): AnyMySqlColumn => user.id, {
		onDelete: 'set null',
		onUpdate: 'cascade'
	}),
	created_at: datetime('created_at', { mode: 'date', fsp: 6 })
		.default(sql`current_timestamp(6)`)
		.notNull(),
	updated_at: datetime('updated_at', { mode: 'date', fsp: 6 })
		.default(sql`current_timestamp(6)`)
		.notNull()
});

export type Upload = typeof upload.$inferSelect;

export const user = mysqlTable(
	'user',
	{
		id: int('id').autoincrement().notNull(),
		uuid: varchar('uuid', { length: 36 }).notNull(),
		username: varchar('username', { length: 26 }).notNull(),
		email: varchar('email', { length: 255 }).notNull(),
		display_name: varchar('display_name', { length: 32 }).notNull(),
		password: text('password'),
		activated: tinyint('activated').default(0).notNull(),
		activity_at: timestamp('activity_at', { mode: 'date' })
			.default(sql`current_timestamp()`)
			.notNull(),
		pictureId: int('pictureId').references((): AnyMySqlColumn => upload.id, {
			onDelete: 'set null',
			onUpdate: 'cascade'
		}),
		created_at: datetime('created_at', { mode: 'date', fsp: 6 })
			.default(sql`current_timestamp(6)`)
			.notNull(),
		updated_at: datetime('updated_at', { mode: 'date', fsp: 6 })
			.default(sql`current_timestamp(6)`)
			.notNull()
	},
	(table) => {
		return {
			IDX_a95e949168be7b7ece1a2382fe: unique('IDX_a95e949168be7b7ece1a2382fe').on(table.uuid),
			IDX_78a916df40e02a9deb1c4b75ed: unique('IDX_78a916df40e02a9deb1c4b75ed').on(table.username),
			IDX_e12875dfb3b1d92d7d7c5377e2: unique('IDX_e12875dfb3b1d92d7d7c5377e2').on(table.email)
		};
	}
);

export type User = typeof user.$inferSelect;
export type NewUser = typeof user.$inferInsert;

export const userPrivilegesPrivilege = mysqlTable(
	'user_privileges_privilege',
	{
		userId: int('userId')
			.notNull()
			.references(() => user.id, { onDelete: 'cascade', onUpdate: 'cascade' }),
		privilegeId: int('privilegeId')
			.notNull()
			.references(() => privilege.id, { onDelete: 'cascade', onUpdate: 'cascade' })
	},
	(table) => {
		return {
			IDX_0664a7ff494a1859a09014c0f1: index('IDX_0664a7ff494a1859a09014c0f1').on(table.userId),
			IDX_e71171f4ed20bc8564a1819d0b: index('IDX_e71171f4ed20bc8564a1819d0b').on(table.privilegeId)
		};
	}
);

export const userToken = mysqlTable('user_token', {
	id: int('id').autoincrement().notNull(),
	token: text('token').notNull(),
	type: mysqlEnum('type', [
		'generic',
		'activation',
		'deactivation',
		'password',
		'login',
		'gdpr',
		'totp',
		'public_key',
		'recovery'
	]).notNull(),
	expires_at: timestamp('expires_at', { mode: 'date' }),
	userId: int('userId').references(() => user.id, { onDelete: 'cascade' }),
	nonce: text('nonce'),
	created_at: datetime('created_at', { mode: 'date', fsp: 6 })
		.default(sql`current_timestamp(6)`)
		.notNull()
});

export type UserToken = typeof userToken.$inferSelect;

export const auditLogRelations = relations(auditLog, ({ one }) => ({
	user: one(user, {
		fields: [auditLog.actorId],
		references: [user.id]
	})
}));

export const userRelations = relations(user, ({ one, many }) => ({
	audit_logs: many(auditLog),
	documents: many(document),
	o_auth2_clients: many(oauth2Client),
	o_auth2_client_authorizations: many(oauth2ClientAuthorization),
	o_auth2_tokens: many(oauth2Token),
	uploads: many(upload, {
		relationName: 'upload_uploaderId_user_id'
	}),
	upload: one(upload, {
		fields: [user.pictureId],
		references: [upload.id],
		relationName: 'user_pictureId_upload_id'
	}),
	user_privileges_privileges: many(userPrivilegesPrivilege),
	user_tokens: many(userToken)
}));

export const documentRelations = relations(document, ({ one }) => ({
	user: one(user, {
		fields: [document.authorId],
		references: [user.id]
	})
}));

export const oauth2ClientRelations = relations(oauth2Client, ({ one, many }) => ({
	user: one(user, {
		fields: [oauth2Client.ownerId],
		references: [user.id]
	}),
	upload: one(upload, {
		fields: [oauth2Client.pictureId],
		references: [upload.id]
	}),
	o_auth2_client_authorizations: many(oauth2ClientAuthorization),
	o_auth2_client_urls: many(oauth2ClientUrl),
	o_auth2_tokens: many(oauth2Token)
}));

export const uploadRelations = relations(upload, ({ one, many }) => ({
	o_auth2_clients: many(oauth2Client),
	user: one(user, {
		fields: [upload.uploaderId],
		references: [user.id],
		relationName: 'upload_uploaderId_user_id'
	}),
	users: many(user, {
		relationName: 'user_pictureId_upload_id'
	})
}));

export const oauth2ClientAuthorizationRelations = relations(
	oauth2ClientAuthorization,
	({ one }) => ({
		user: one(user, {
			fields: [oauth2ClientAuthorization.userId],
			references: [user.id]
		}),
		o_auth2_client: one(oauth2Client, {
			fields: [oauth2ClientAuthorization.clientId],
			references: [oauth2Client.id]
		})
	})
);

export const oauth2ClientUrlRelations = relations(oauth2ClientUrl, ({ one }) => ({
	o_auth2_client: one(oauth2Client, {
		fields: [oauth2ClientUrl.clientId],
		references: [oauth2Client.id]
	})
}));

export const oauth2TokenRelations = relations(oauth2Token, ({ one }) => ({
	o_auth2_client: one(oauth2Client, {
		fields: [oauth2Token.clientId],
		references: [oauth2Client.id]
	}),
	user: one(user, {
		fields: [oauth2Token.userId],
		references: [user.id]
	})
}));

export const userPrivilegesPrivilegeRelations = relations(userPrivilegesPrivilege, ({ one }) => ({
	user: one(user, {
		fields: [userPrivilegesPrivilege.userId],
		references: [user.id]
	}),
	privilege: one(privilege, {
		fields: [userPrivilegesPrivilege.privilegeId],
		references: [privilege.id]
	})
}));

export const privilegeRelations = relations(privilege, ({ many }) => ({
	user_privileges_privileges: many(userPrivilegesPrivilege)
}));

export const userTokenRelations = relations(userToken, ({ one }) => ({
	user: one(user, {
		fields: [userToken.userId],
		references: [user.id]
	})
}));