import { boolean, index, integer, jsonb, numeric, pgTable, text, timestamp, uniqueIndex } from 'drizzle-orm/pg-core'; type FilingMetrics = { revenue: number | null; netIncome: number | null; totalAssets: number | null; cash: number | null; debt: number | null; }; type FilingAnalysis = { provider?: string; model?: string; text?: string; legacyInsights?: string; }; const authDateColumn = { withTimezone: true, mode: 'date' } as const; const appDateColumn = { withTimezone: true, mode: 'string' } as const; export const user = pgTable('user', { id: text('id').primaryKey().notNull(), name: text('name').notNull(), email: text('email').notNull(), emailVerified: boolean('emailVerified').notNull().default(false), image: text('image'), createdAt: timestamp('createdAt', authDateColumn).notNull(), updatedAt: timestamp('updatedAt', authDateColumn).notNull(), role: text('role'), banned: boolean('banned').default(false), banReason: text('banReason'), banExpires: timestamp('banExpires', authDateColumn) }, (table) => ({ userEmailUnique: uniqueIndex('user_email_uidx').on(table.email) })); export const organization = pgTable('organization', { id: text('id').primaryKey().notNull(), name: text('name').notNull(), slug: text('slug').notNull(), logo: text('logo'), createdAt: timestamp('createdAt', authDateColumn).notNull(), metadata: text('metadata') }, (table) => ({ organizationSlugUnique: uniqueIndex('organization_slug_uidx').on(table.slug) })); export const session = pgTable('session', { id: text('id').primaryKey().notNull(), expiresAt: timestamp('expiresAt', authDateColumn).notNull(), token: text('token').notNull(), createdAt: timestamp('createdAt', authDateColumn).notNull(), updatedAt: timestamp('updatedAt', authDateColumn).notNull(), ipAddress: text('ipAddress'), userAgent: text('userAgent'), userId: text('userId').notNull().references(() => user.id, { onDelete: 'cascade' }), impersonatedBy: text('impersonatedBy'), activeOrganizationId: text('activeOrganizationId') }, (table) => ({ sessionTokenUnique: uniqueIndex('session_token_uidx').on(table.token), sessionUserIdIndex: index('session_userId_idx').on(table.userId) })); export const account = pgTable('account', { id: text('id').primaryKey().notNull(), accountId: text('accountId').notNull(), providerId: text('providerId').notNull(), userId: text('userId').notNull().references(() => user.id, { onDelete: 'cascade' }), accessToken: text('accessToken'), refreshToken: text('refreshToken'), idToken: text('idToken'), accessTokenExpiresAt: timestamp('accessTokenExpiresAt', authDateColumn), refreshTokenExpiresAt: timestamp('refreshTokenExpiresAt', authDateColumn), scope: text('scope'), password: text('password'), createdAt: timestamp('createdAt', authDateColumn).notNull(), updatedAt: timestamp('updatedAt', authDateColumn).notNull() }, (table) => ({ accountUserIdIndex: index('account_userId_idx').on(table.userId) })); export const verification = pgTable('verification', { id: text('id').primaryKey().notNull(), identifier: text('identifier').notNull(), value: text('value').notNull(), expiresAt: timestamp('expiresAt', authDateColumn).notNull(), createdAt: timestamp('createdAt', authDateColumn).notNull(), updatedAt: timestamp('updatedAt', authDateColumn).notNull() }, (table) => ({ verificationIdentifierIndex: index('verification_identifier_idx').on(table.identifier) })); export const member = pgTable('member', { id: text('id').primaryKey().notNull(), organizationId: text('organizationId').notNull().references(() => organization.id, { onDelete: 'cascade' }), userId: text('userId').notNull().references(() => user.id, { onDelete: 'cascade' }), role: text('role').notNull().default('member'), createdAt: timestamp('createdAt', authDateColumn).notNull() }, (table) => ({ memberOrganizationIdIndex: index('member_organizationId_idx').on(table.organizationId), memberUserIdIndex: index('member_userId_idx').on(table.userId) })); export const invitation = pgTable('invitation', { id: text('id').primaryKey().notNull(), organizationId: text('organizationId').notNull().references(() => organization.id, { onDelete: 'cascade' }), email: text('email').notNull(), role: text('role'), status: text('status').notNull().default('pending'), expiresAt: timestamp('expiresAt', authDateColumn).notNull(), createdAt: timestamp('createdAt', authDateColumn).notNull(), inviterId: text('inviterId').notNull().references(() => user.id, { onDelete: 'cascade' }) }, (table) => ({ invitationOrganizationIdIndex: index('invitation_organizationId_idx').on(table.organizationId), invitationEmailIndex: index('invitation_email_idx').on(table.email) })); export const watchlistItem = pgTable('watchlist_item', { id: integer('id').generatedAlwaysAsIdentity().primaryKey(), user_id: text('user_id').notNull().references(() => user.id, { onDelete: 'cascade' }), ticker: text('ticker').notNull(), company_name: text('company_name').notNull(), sector: text('sector'), created_at: timestamp('created_at', appDateColumn).notNull() }, (table) => ({ watchlistUserTickerUnique: uniqueIndex('watchlist_user_ticker_uidx').on(table.user_id, table.ticker), watchlistUserCreatedIndex: index('watchlist_user_created_idx').on(table.user_id, table.created_at) })); export const holding = pgTable('holding', { id: integer('id').generatedAlwaysAsIdentity().primaryKey(), user_id: text('user_id').notNull().references(() => user.id, { onDelete: 'cascade' }), ticker: text('ticker').notNull(), shares: numeric('shares', { precision: 30, scale: 6 }).notNull(), avg_cost: numeric('avg_cost', { precision: 30, scale: 6 }).notNull(), current_price: numeric('current_price', { precision: 30, scale: 6 }), market_value: numeric('market_value', { precision: 30, scale: 2 }).notNull(), gain_loss: numeric('gain_loss', { precision: 30, scale: 2 }).notNull(), gain_loss_pct: numeric('gain_loss_pct', { precision: 30, scale: 2 }).notNull(), last_price_at: timestamp('last_price_at', appDateColumn), created_at: timestamp('created_at', appDateColumn).notNull(), updated_at: timestamp('updated_at', appDateColumn).notNull() }, (table) => ({ holdingUserTickerUnique: uniqueIndex('holding_user_ticker_uidx').on(table.user_id, table.ticker), holdingUserIndex: index('holding_user_idx').on(table.user_id) })); export const filing = pgTable('filing', { id: integer('id').generatedAlwaysAsIdentity().primaryKey(), ticker: text('ticker').notNull(), filing_type: text('filing_type').$type<'10-K' | '10-Q' | '8-K'>().notNull(), filing_date: text('filing_date').notNull(), accession_number: text('accession_number').notNull(), cik: text('cik').notNull(), company_name: text('company_name').notNull(), filing_url: text('filing_url'), submission_url: text('submission_url'), primary_document: text('primary_document'), metrics: jsonb('metrics').$type(), analysis: jsonb('analysis').$type(), created_at: timestamp('created_at', appDateColumn).notNull(), updated_at: timestamp('updated_at', appDateColumn).notNull() }, (table) => ({ filingAccessionUnique: uniqueIndex('filing_accession_uidx').on(table.accession_number), filingTickerDateIndex: index('filing_ticker_date_idx').on(table.ticker, table.filing_date), filingDateIndex: index('filing_date_idx').on(table.filing_date) })); export const filingLink = pgTable('filing_link', { id: integer('id').generatedAlwaysAsIdentity().primaryKey(), filing_id: integer('filing_id').notNull().references(() => filing.id, { onDelete: 'cascade' }), link_type: text('link_type').notNull(), url: text('url').notNull(), source: text('source').notNull().default('sec'), created_at: timestamp('created_at', appDateColumn).notNull() }, (table) => ({ filingLinkUnique: uniqueIndex('filing_link_unique_uidx').on(table.filing_id, table.url), filingLinkFilingIndex: index('filing_link_filing_idx').on(table.filing_id) })); export const taskRun = pgTable('task_run', { id: text('id').primaryKey().notNull(), user_id: text('user_id').notNull().references(() => user.id, { onDelete: 'cascade' }), task_type: text('task_type').$type<'sync_filings' | 'refresh_prices' | 'analyze_filing' | 'portfolio_insights'>().notNull(), status: text('status').$type<'queued' | 'running' | 'completed' | 'failed'>().notNull(), priority: integer('priority').notNull(), payload: jsonb('payload').$type>().notNull(), result: jsonb('result').$type | null>(), error: text('error'), attempts: integer('attempts').notNull(), max_attempts: integer('max_attempts').notNull(), workflow_run_id: text('workflow_run_id'), created_at: timestamp('created_at', appDateColumn).notNull(), updated_at: timestamp('updated_at', appDateColumn).notNull(), finished_at: timestamp('finished_at', appDateColumn) }, (table) => ({ taskUserCreatedIndex: index('task_user_created_idx').on(table.user_id, table.created_at), taskStatusIndex: index('task_status_idx').on(table.status), taskWorkflowRunUnique: uniqueIndex('task_workflow_run_uidx').on(table.workflow_run_id) })); export const portfolioInsight = pgTable('portfolio_insight', { id: integer('id').generatedAlwaysAsIdentity().primaryKey(), user_id: text('user_id').notNull().references(() => user.id, { onDelete: 'cascade' }), provider: text('provider').notNull(), model: text('model').notNull(), content: text('content').notNull(), created_at: timestamp('created_at', appDateColumn).notNull() }, (table) => ({ insightUserCreatedIndex: index('insight_user_created_idx').on(table.user_id, table.created_at) })); export const authSchema = { user, session, account, verification, organization, member, invitation }; export const appSchema = { watchlistItem, holding, filing, filingLink, taskRun, portfolioInsight }; export const schema = { ...authSchema, ...appSchema };