import { index, jsonb, pgTable, text, timestamp, uniqueIndex, uuid } from 'drizzle-orm/pg-core';
import { users } from './identity';

/** One least-privilege Google Sheets connection per PIG user. */
export const googleConnections = pgTable('google_connections', {
  userId: uuid('user_id')
    .primaryKey()
    .references(() => users.id, { onDelete: 'cascade' }),
  /** Purpose-bound AES-256-GCM envelopes. Tokens are never returned by the API. */
  refreshTokenEncrypted: text('refresh_token_encrypted').notNull(),
  accessTokenEncrypted: text('access_token_encrypted'),
  accessTokenExpiresAt: timestamp('access_token_expires_at', { withTimezone: true }),
  scopes: jsonb('scopes').$type<string[]>().notNull().default([]),
  connectedAt: timestamp('connected_at', { withTimezone: true }).notNull().defaultNow(),
  updatedAt: timestamp('updated_at', { withTimezone: true }).notNull().defaultNow(),
});

/**
 * Short-lived OAuth proof material stays server-side. State is hashed and the
 * PKCE verifier encrypted so a database read cannot complete an OAuth flow.
 */
export const googleOauthFlows = pgTable(
  'google_oauth_flows',
  {
    id: uuid('id').primaryKey().defaultRandom(),
    userId: uuid('user_id')
      .notNull()
      .references(() => users.id, { onDelete: 'cascade' }),
    stateHash: text('state_hash').notNull(),
    browserBindingHash: text('browser_binding_hash').notNull(),
    pkceVerifierEncrypted: text('pkce_verifier_encrypted').notNull(),
    expiresAt: timestamp('expires_at', { withTimezone: true }).notNull(),
    consumedAt: timestamp('consumed_at', { withTimezone: true }),
    createdAt: timestamp('created_at', { withTimezone: true }).notNull().defaultNow(),
  },
  (table) => [
    uniqueIndex('google_oauth_flows_state_hash_key').on(table.stateHash),
    index('google_oauth_flows_expiry_idx').on(table.expiresAt),
    index('google_oauth_flows_user_idx').on(table.userId),
  ],
);

export type GoogleConnection = typeof googleConnections.$inferSelect;
export type GoogleOauthFlow = typeof googleOauthFlows.$inferSelect;
