import { sqliteTable, text, integer, primaryKey, foreignKey, real, unique, index, uniqueIndex } from 'drizzle-orm/sqlite-core';
import { sql } from 'drizzle-orm';

export const users = sqliteTable('users', {
  id: text('id').primaryKey(),
  username: text('username').unique().notNull(),
  displayName: text('display_name'),
  passwordHash: text('password_hash').notNull(),
  avatar: text('avatar'),
  status: text('status').default('offline'),
  customStatus: text('custom_status'),
  isAdmin: integer('is_admin').default(0),
  homeInstance: text('home_instance'),
  homeUserId: text('home_user_id'),
  replicatedInstances: text('replicated_instances').default('[]'),
  banner: text('banner'),
  accentColor: text('accent_color'),
  avatarColor: text('avatar_color'),
  bio: text('bio'),
  isDeleted: integer('is_deleted').default(0),
  discoverable: integer('discoverable').default(1),
  profileUpdatedAt: integer('profile_updated_at'),
  passwordChangedAt: integer('password_changed_at'),
  showActivity: integer('show_activity').notNull().default(1),
  federationRegistryUpdatedAt: integer('federation_registry_updated_at').default(0),
  createdAt: integer('created_at').notNull(),
});

export const spaces = sqliteTable('spaces', {
  id: text('id').primaryKey(),
  name: text('name').notNull(),
  icon: text('icon'),
  banner: text('banner'),
  avatarColor: text('avatar_color'),
  ownerId: text('owner_id').notNull().references(() => users.id),
  inviteCode: text('invite_code').unique(),
  visibility: text('visibility').default('private'),
  description: text('description'),
  createdAt: integer('created_at').notNull(),
});

export const spaceMembers = sqliteTable('space_members', {
  spaceId: text('space_id').notNull().references(() => spaces.id, { onDelete: 'cascade' }),
  userId: text('user_id').notNull().references(() => users.id, { onDelete: 'cascade' }),
  nickname: text('nickname'),
  joinedAt: integer('joined_at').notNull(),
}, (table) => ({
  pk: primaryKey({ columns: [table.spaceId, table.userId] }),
  userIdx: index('idx_space_members_user_id').on(table.userId),
}));

export const channelCategories = sqliteTable('channel_categories', {
  id: text('id').primaryKey(),
  spaceId: text('space_id').notNull().references(() => spaces.id, { onDelete: 'cascade' }),
  name: text('name').notNull(),
  position: integer('position').default(0),
  createdAt: integer('created_at').notNull(),
}, (table) => ({
  spaceIdx: index('idx_channel_categories_space_id').on(table.spaceId),
}));

export const channels = sqliteTable('channels', {
  id: text('id').primaryKey(),
  spaceId: text('space_id').notNull().references(() => spaces.id, { onDelete: 'cascade' }),
  name: text('name').notNull(),
  type: text('type').notNull(),
  topic: text('topic'),
  position: integer('position').default(0),
  categoryId: text('category_id'),
  createdAt: integer('created_at').notNull(),
}, (table) => ({
  spaceIdx: index('idx_channels_space_id').on(table.spaceId),
}));

export const messages = sqliteTable('messages', {
  id: text('id').primaryKey(),
  channelId: text('channel_id').notNull().references(() => channels.id, { onDelete: 'cascade' }),
  userId: text('user_id').notNull().references(() => users.id),
  replyToId: text('reply_to_id'),
  content: text('content'),
  editedAt: integer('edited_at'),
  createdAt: integer('created_at').notNull(),
}, (table) => ({
  replyToFk: foreignKey({
    columns: [table.replyToId],
    foreignColumns: [table.id],
  }).onDelete('set null'),
  channelIdx: index('idx_messages_channel_id').on(table.channelId),
  userIdx: index('idx_messages_user_id').on(table.userId),
}));

export const attachments = sqliteTable('attachments', {
  id: text('id').primaryKey(),
  messageId: text('message_id').references(() => messages.id, { onDelete: 'cascade' }),
  dmMessageId: text('dm_message_id').references(() => dmMessages.id, { onDelete: 'cascade' }),
  uploaderId: text('uploader_id'),
  filename: text('filename').notNull(),
  originalName: text('original_name').notNull(),
  mimetype: text('mimetype').notNull(),
  size: integer('size').notNull(),
  thumbnailFilename: text('thumbnail_filename'),
  width: integer('width'),
  height: integer('height'),
  duration: real('duration'),
  sourceUrl: text('source_url'),
  federationStatus: text('federation_status'),
  federationMeta: text('federation_meta'),
  createdAt: integer('created_at').notNull(),
}, (table) => ({
  messageIdx: index('idx_attachments_message_id').on(table.messageId),
  dmMessageIdx: index('idx_attachments_dm_message_id').on(table.dmMessageId),
}));

export const embeds = sqliteTable('embeds', {
  id: text('id').primaryKey(),
  messageId: text('message_id').references(() => messages.id, { onDelete: 'cascade' }),
  dmMessageId: text('dm_message_id').references(() => dmMessages.id, { onDelete: 'cascade' }),
  url: text('url').notNull(),
  embedType: text('embed_type').notNull(),
  provider: text('provider'),
  title: text('title'),
  description: text('description'),
  image: text('image'),
  embedUrl: text('embed_url'),
  width: integer('width'),
  height: integer('height'),
  color: text('color'),
  createdAt: integer('created_at').notNull(),
}, (table) => ({
  messageIdx: index('idx_embeds_message_id').on(table.messageId),
  dmMessageIdx: index('idx_embeds_dm_message_id').on(table.dmMessageId),
}));

export const dmChannels = sqliteTable('dm_channels', {
  id: text('id').primaryKey(),
  ownerId: text('owner_id'),
  federatedId: text('federated_id'),
  ownerHomeUserId: text('owner_home_user_id'),
  ownerHomeInstance: text('owner_home_instance'),
  deletedAt: integer('deleted_at'),
  createdAt: integer('created_at').notNull(),
}, (table) => ({
  federatedIdx: uniqueIndex('idx_dm_federated')
    .on(table.federatedId)
    .where(sql`federated_id IS NOT NULL`),
}));

export const dmMembers = sqliteTable('dm_members', {
  dmChannelId: text('dm_channel_id').notNull().references(() => dmChannels.id, { onDelete: 'cascade' }),
  userId: text('user_id').notNull().references(() => users.id, { onDelete: 'cascade' }),
  closed: integer('closed').default(0),
}, (table) => ({
  pk: primaryKey({ columns: [table.dmChannelId, table.userId] }),
  userIdx: index('idx_dm_members_user_id').on(table.userId),
}));

export const dmMessages = sqliteTable('dm_messages', {
  id: text('id').primaryKey(),
  dmChannelId: text('dm_channel_id').notNull().references(() => dmChannels.id, { onDelete: 'cascade' }),
  userId: text('user_id').notNull().references(() => users.id),
  replyToId: text('reply_to_id'),
  content: text('content'),
  type: text('type').notNull().default('user'),
  editedAt: integer('edited_at'),
  sourceInstance: text('source_instance'),
  sourceMessageId: text('source_message_id'),
  encryptionVersion: integer('encryption_version').default(0),
  createdAt: integer('created_at').notNull(),
}, (table) => ({
  replyToFk: foreignKey({
    columns: [table.replyToId],
    foreignColumns: [table.id],
  }).onDelete('set null'),
  channelIdx: index('idx_dm_messages_dm_channel_id').on(table.dmChannelId),
  userIdx: index('idx_dm_messages_user_id').on(table.userId),
  sourceUniqueIdx: uniqueIndex('idx_dm_messages_source_unique')
    .on(table.sourceInstance, table.sourceMessageId)
    .where(sql`source_instance IS NOT NULL`),
}));

export const friends = sqliteTable('friends', {
  userId: text('user_id').notNull().references(() => users.id, { onDelete: 'cascade' }),
  friendId: text('friend_id').notNull().references(() => users.id, { onDelete: 'cascade' }),
  createdAt: integer('created_at').notNull(),
}, (table) => ({
  pk: primaryKey({ columns: [table.userId, table.friendId] }),
  userIdx: index('idx_friends_user_id').on(table.userId),
  friendIdx: index('idx_friends_friend_id').on(table.friendId),
}));

export const friendRequests = sqliteTable('friend_requests', {
  id: text('id').primaryKey(),
  fromId: text('from_id').notNull().references(() => users.id, { onDelete: 'cascade' }),
  toId: text('to_id').notNull().references(() => users.id, { onDelete: 'cascade' }),
  status: text('status').default('pending'), // 'pending', 'accepted', 'declined'
  createdAt: integer('created_at').notNull(),
}, (table) => ({
  toIdx: index('idx_friend_requests_to_id').on(table.toId),
  fromIdx: index('idx_friend_requests_from_id').on(table.fromId),
}));

export const reactions = sqliteTable('reactions', {
  id: text('id').primaryKey(),
  messageId: text('message_id').notNull().references(() => messages.id, { onDelete: 'cascade' }),
  userId: text('user_id').notNull().references(() => users.id, { onDelete: 'cascade' }),
  emoji: text('emoji').notNull(),
  createdAt: integer('created_at').notNull(),
}, (table) => ({
  messageIdx: index('idx_reactions_message_id').on(table.messageId),
}));

export const dmReactions = sqliteTable('dm_reactions', {
  id: text('id').primaryKey(),
  dmMessageId: text('dm_message_id').notNull().references(() => dmMessages.id, { onDelete: 'cascade' }),
  userId: text('user_id').notNull().references(() => users.id, { onDelete: 'cascade' }),
  emoji: text('emoji').notNull(),
  createdAt: integer('created_at').notNull(),
}, (table) => ({
  dmMessageIdx: index('idx_dm_reactions_dm_message_id').on(table.dmMessageId),
}));

export const roles = sqliteTable('roles', {
  id: text('id').primaryKey(),
  spaceId: text('space_id').notNull().references(() => spaces.id, { onDelete: 'cascade' }),
  name: text('name').notNull(),
  color: text('color').default('#b9bbbe'),
  position: integer('position').default(0),
  permissions: text('permissions'), // JSON string of permission keys
  createdAt: integer('created_at').notNull(),
}, (table) => ({
  spaceIdx: index('idx_roles_space_id').on(table.spaceId),
}));

export const memberRoles = sqliteTable('member_roles', {
  spaceId: text('space_id').notNull().references(() => spaces.id, { onDelete: 'cascade' }),
  userId: text('user_id').notNull().references(() => users.id, { onDelete: 'cascade' }),
  roleId: text('role_id').notNull().references(() => roles.id, { onDelete: 'cascade' }),
}, (table) => ({
  pk: primaryKey({ columns: [table.spaceId, table.userId, table.roleId] }),
  userSpaceIdx: index('idx_member_roles_user_id_space_id').on(table.userId, table.spaceId),
}));

export const channelOverrides = sqliteTable('channel_overrides', {
  channelId: text('channel_id').notNull().references(() => channels.id, { onDelete: 'cascade' }),
  targetType: text('target_type').notNull(), // 'role' | 'member'
  targetId: text('target_id').notNull(), // role ID or user ID
  allow: text('allow').notNull().default('0'), // BigInt decimal string
  deny: text('deny').notNull().default('0'), // BigInt decimal string
}, (table) => ({
  pk: primaryKey({ columns: [table.channelId, table.targetType, table.targetId] }),
  channelIdx: index('idx_channel_overrides_channel_id').on(table.channelId),
}));

export const categoryOverrides = sqliteTable('category_overrides', {
  categoryId: text('category_id').notNull().references(() => channelCategories.id, { onDelete: 'cascade' }),
  targetType: text('target_type').notNull(),
  targetId: text('target_id').notNull(),
  allow: text('allow').notNull().default('0'),
  deny: text('deny').notNull().default('0'),
}, (table) => ({
  pk: primaryKey({ columns: [table.categoryId, table.targetType, table.targetId] }),
  categoryIdx: index('idx_category_overrides_category_id').on(table.categoryId),
}));

export const readStates = sqliteTable('read_states', {
  userId: text('user_id').notNull().references(() => users.id, { onDelete: 'cascade' }),
  channelId: text('channel_id').notNull(),
  lastReadMessageId: text('last_read_message_id').notNull(),
  updatedAt: integer('updated_at').notNull(),
}, (table) => ({
  pk: primaryKey({ columns: [table.userId, table.channelId] }),
  userIdx: index('idx_read_states_user_id').on(table.userId),
}));

export const spaceFolders = sqliteTable('space_folders', {
  id: text('id').primaryKey(),
  userId: text('user_id').notNull().references(() => users.id, { onDelete: 'cascade' }),
  name: text('name'),
  color: text('color'),
  position: integer('position').default(0),
  createdAt: integer('created_at').notNull(),
});

export const spaceFolderMembers = sqliteTable('space_folder_members', {
  folderId: text('folder_id').notNull().references(() => spaceFolders.id, { onDelete: 'cascade' }),
  spaceId: text('space_id').notNull(), // No FK — federated space IDs don't exist locally
  position: integer('position').default(0),
}, (table) => ({
  pk: primaryKey({ columns: [table.folderId, table.spaceId] }),
}));

export const userSpaceLayout = sqliteTable('user_space_layout', {
  userId: text('user_id').primaryKey().references(() => users.id, { onDelete: 'cascade' }),
  layout: text('layout').notNull().default('[]'),
  updatedAt: integer('updated_at').notNull(),
});

export const instanceSettings = sqliteTable('instance_settings', {
  id: integer('id').primaryKey().default(1),
  instanceName: text('instance_name').default('Backspace'),
  workerId: integer('worker_id'),
  discoveryEnabled: integer('discovery_enabled').notNull().default(1),
  maxBitrateKbps: integer('max_bitrate_kbps').notNull().default(20000),
  minBitrateKbps: integer('min_bitrate_kbps').notNull().default(500),
  bitrateStepKbps: integer('bitrate_step_kbps').notNull().default(500),
  allowedResolutions: text('allowed_resolutions').notNull().default('540,720,1080'),
  allowedFramerates: text('allowed_framerates').notNull().default('30,45,60'),
  maxResolution: integer('max_resolution').notNull().default(1080),
  maxFramerate: integer('max_framerate').notNull().default(60),
  registrationOpen: integer('registration_open'),  // null = use env var default, 0/1 = explicit
  gifApiKey: text('gif_api_key'),
  bitrateMatrixOverrides: text('bitrate_matrix_overrides'),
  allowCustomBitrate: integer('allow_custom_bitrate').notNull().default(1),
  maxUploadSizeBytes: integer('max_upload_size_bytes'),
  federationRelayEnabled: integer('federation_relay_enabled').notNull().default(1),
  federationRelayTtlDays: integer('federation_relay_ttl_days').notNull().default(30),
  defaultAutoRotateIntervalDays: integer('default_auto_rotate_interval_days').notNull().default(90),
  autoAcceptPeering: integer('auto_accept_peering').notNull().default(1),
  updatedAt: integer('updated_at').notNull(),
});

export const bans = sqliteTable('bans', {
  spaceId: text('space_id').notNull().references(() => spaces.id, { onDelete: 'cascade' }),
  userId: text('user_id').notNull().references(() => users.id, { onDelete: 'cascade' }),
  reason: text('reason'),
  bannedBy: text('banned_by').references(() => users.id),
  createdAt: integer('created_at').notNull(),
}, (table) => ({
  pk: primaryKey({ columns: [table.spaceId, table.userId] }),
  spaceIdx: index('idx_bans_space_id').on(table.spaceId),
}));

export const joinRequests = sqliteTable('join_requests', {
  id: text('id').primaryKey(),
  spaceId: text('space_id').notNull().references(() => spaces.id, { onDelete: 'cascade' }),
  userId: text('user_id').notNull().references(() => users.id, { onDelete: 'cascade' }),
  message: text('message'),
  status: text('status').notNull().default('pending'),
  decidedBy: text('decided_by').references(() => users.id),
  createdAt: integer('created_at').notNull(),
  decidedAt: integer('decided_at'),
}, (table) => ({
  spaceStatusIdx: index('idx_join_requests_space_id_status').on(table.spaceId, table.status),
}));

export const voiceRestrictions = sqliteTable('voice_restrictions', {
  spaceId: text('space_id').notNull().references(() => spaces.id, { onDelete: 'cascade' }),
  userId: text('user_id').notNull().references(() => users.id, { onDelete: 'cascade' }),
  restrictionType: text('restriction_type').notNull(), // 'mute' | 'deafen'
  moderatorId: text('moderator_id').references(() => users.id),
  createdAt: integer('created_at').notNull(),
}, (table) => ({
  pk: primaryKey({ columns: [table.spaceId, table.userId, table.restrictionType] }),
  spaceIdx: index('idx_voice_restrictions_space_id').on(table.spaceId),
}));

export const federationPeers = sqliteTable('federation_peers', {
  id: text('id').primaryKey(),
  origin: text('origin').notNull().unique(),
  instanceName: text('instance_name'),
  hmacSecret: text('hmac_secret').notNull(),
  status: text('status').notNull().default('active'),
  lastSeenAt: integer('last_seen_at'),
  lastFailureAt: integer('last_failure_at'),
  consecutiveFailures: integer('consecutive_failures').notNull().default(0),
  consecutiveAuthFailures: integer('consecutive_auth_failures').notNull().default(0),
  lastSyncedAt: integer('last_synced_at').default(0),
  remoteMaxUploadSize: integer('remote_max_upload_size'),
  nonceSupported: integer('nonce_supported').notNull().default(0),
  pendingHmacSecret: text('pending_hmac_secret'),
  secretRotationAt: integer('secret_rotation_at'),
  secretRotatedAt: integer('secret_rotated_at'),
  autoRotateIntervalDays: integer('auto_rotate_interval_days').notNull().default(90),
  createdAt: integer('created_at').notNull(),
});

export const peerApprovalRequests = sqliteTable('peer_approval_requests', {
  id: text('id').primaryKey(),
  origin: text('origin').notNull().unique(),
  instanceName: text('instance_name'),
  hmacSecret: text('hmac_secret').notNull(),
  requestedAt: integer('requested_at').notNull(),
  expiresAt: integer('expires_at').notNull(),
});

export const federationOutbox = sqliteTable('federation_outbox', {
  id: text('id').primaryKey(),
  peerId: text('peer_id').notNull().references(() => federationPeers.id, { onDelete: 'cascade' }),
  contextId: text('context_id').notNull(),
  entityId: text('entity_id').notNull(),
  contextType: text('context_type').notNull().default('dm'),
  eventType: text('event_type').notNull(),
  payload: text('payload').notNull(),
  encryptionVersion: integer('encryption_version').default(0),
  attempts: integer('attempts').default(0),
  nextRetryAt: integer('next_retry_at').notNull(),
  expiresAt: integer('expires_at').notNull(),
  createdAt: integer('created_at').notNull(),
}, (table) => ({
  uniquePerPeerMessage: unique().on(table.peerId, table.entityId),
  retryIdx: index('idx_outbox_retry').on(table.nextRetryAt),
}));

export const federationFileQueue = sqliteTable('federation_file_queue', {
  id: text('id').primaryKey(),
  peerOrigin: text('peer_origin').notNull(),
  dmMessageId: text('dm_message_id').notNull(),
  sourceUrl: text('source_url').notNull(),
  targetFilename: text('target_filename'),
  originalName: text('original_name').notNull(),
  mimetype: text('mimetype').notNull(),
  size: integer('size').notNull(),
  status: text('status').notNull().default('pending'),
  rejectionReason: text('rejection_reason'),
  attempts: integer('attempts').default(0),
  nextRetryAt: integer('next_retry_at').notNull(),
  expiresAt: integer('expires_at').notNull(),
  createdAt: integer('created_at').notNull(),
});

export const federationMutationLog = sqliteTable('federation_mutation_log', {
  id: text('id').primaryKey(),
  entityId: text('entity_id').notNull(),
  contextId: text('context_id').notNull(),
  contextType: text('context_type').notNull().default('dm'),
  mutationType: text('mutation_type').notNull(),
  mutatedAt: integer('mutated_at').notNull(),
  payload: text('payload'),
}, (table) => ({
  timeIdx: index('idx_mutation_log_time').on(table.mutatedAt),
}));

export const userFederationRegistry = sqliteTable('user_federation_registry', {
  userId: text('user_id').notNull().references(() => users.id, { onDelete: 'cascade' }),
  origin: text('origin').notNull(),
  label: text('label').notNull().default(''),
  username: text('username').notNull().default(''),
  remoteUserId: text('remote_user_id').notNull().default(''),
  status: text('status').notNull().default('connected'),
  addedAt: integer('added_at').notNull(),
  lastConnectedAt: integer('last_connected_at'),
  disconnectedAt: integer('disconnected_at'),
  errorMessage: text('error_message'),
}, (table) => ({
  pk: primaryKey({ columns: [table.userId, table.origin] }),
}));
