import { eq } from 'drizzle-orm'
import { boolean, pgTable, text, timestamp, varchar } from 'drizzle-orm/pg-core'
import { betterAuth } from 'better-auth'
import { parseSetCookieHeader } from 'better-auth/cookies'
import { drizzleAdapter } from 'better-auth/adapters/drizzle'
import { jwt } from 'better-auth/plugins'
import type { TransactionalDatabase } from '@platform-modules/db'
import { EngineMismatchError, type AuthEngine, type Principal, type SignInResult } from './index.js'

export const platformAccounts = pgTable('platform_accounts', {
  id: text('id').primaryKey(),
  email: varchar('email', { length: 255 }).notNull().unique(),
  name: varchar('name', { length: 255 }),
  emailVerified: boolean('emailVerified').default(false),
  avatarUrl: text('avatar_url'),
  createdAt: timestamp('created_at', { withTimezone: true }),
  updatedAt: timestamp('updated_at', { withTimezone: true }),
})

export const baSession = pgTable('session', {
  id: text('id').primaryKey(),
  token: text('token').notNull().unique(),
  userId: text('userId').notNull(),
  expiresAt: timestamp('expiresAt', { withTimezone: true }).notNull(),
  createdAt: timestamp('createdAt', { withTimezone: true }).notNull(),
  updatedAt: timestamp('updatedAt', { withTimezone: true }).notNull(),
  ipAddress: text('ipAddress'),
  userAgent: text('userAgent'),
})

export const baAccount = pgTable('account', {
  id: text('id').primaryKey(),
  userId: text('userId').notNull(),
  accountId: text('accountId').notNull(),
  providerId: text('providerId').notNull(),
  accessToken: text('accessToken'),
  refreshToken: text('refreshToken'),
  idToken: text('idToken'),
  expiresAt: timestamp('expiresAt', { withTimezone: true }),
  password: text('password'),
  createdAt: timestamp('createdAt', { withTimezone: true }).notNull(),
  updatedAt: timestamp('updatedAt', { withTimezone: true }).notNull(),
  accessTokenExpiresAt: timestamp('accessTokenExpiresAt', { withTimezone: true }),
  refreshTokenExpiresAt: timestamp('refreshTokenExpiresAt', { withTimezone: true }),
  scope: text('scope'),
})

export const baVerification = pgTable('verification', {
  id: text('id').primaryKey(),
  identifier: text('identifier').notNull(),
  value: text('value').notNull(),
  expiresAt: timestamp('expiresAt', { withTimezone: true }).notNull(),
  createdAt: timestamp('createdAt', { withTimezone: true }),
  updatedAt: timestamp('updatedAt', { withTimezone: true }),
})

export const baJwks = pgTable('jwks', {
  id: text('id').primaryKey(),
  publicKey: text('publicKey').notNull(),
  privateKey: text('privateKey').notNull(),
  createdAt: timestamp('createdAt', { withTimezone: true }).notNull(),
})

export const betterAuthSchema = {
  platformAccounts,
  baSession,
  baAccount,
  baVerification,
  baJwks,
}

export type BetterAuthEngineConfig = {
  db: TransactionalDatabase<typeof betterAuthSchema>
  secret: string
  baseURL?: string
  cookiePrefix?: string
}

function readSessionCookieValue(setCookieHeader: string | null, cookiePrefix: string): string | null {
  if (!setCookieHeader) return null
  const name = `${cookiePrefix}.session_token`
  return parseSetCookieHeader(setCookieHeader).get(name)?.value ?? null
}

/**
 * Better-auth adapter (session-cookie + JWT, bcrypt KDF).
 * NOT wire-compatible with engine-custom — different KDF + session tables.
 */
export function createBetterAuthEngine(config: BetterAuthEngineConfig): AuthEngine {
  const cookiePrefix = config.cookiePrefix ?? 'platform'
  const sessionCookieName = `${cookiePrefix}.session_token`

  const auth = betterAuth({
    database: drizzleAdapter(config.db, {
      provider: 'pg',
      schema: {
        user: platformAccounts,
        session: baSession,
        account: baAccount,
        verification: baVerification,
        jwks: baJwks,
      },
    }),
    secret: config.secret,
    baseURL: config.baseURL ?? 'http://localhost',
    emailAndPassword: {
      enabled: true,
      requireEmailVerification: false,
    },
    session: {
      strategy: 'jwt' as const,
      maxAge: 30 * 24 * 60 * 60,
    } as Record<string, unknown>,
    plugins: [
      jwt({
        jwt: { expirationTime: '30d' },
      }),
    ],
    advanced: {
      generateId: () => crypto.randomUUID(),
      cookiePrefix: config.cookiePrefix ?? 'platform',
    } as Record<string, unknown>,
  })

  function toPrincipal(userId: string, sessionId: string, roles: string[] = ['user']): Principal {
    return { userId, sessionId, roles }
  }

  return {
    async signIn({ email, password }): Promise<SignInResult> {
      const res = await auth.api.signInEmail({
        body: { email, password },
        returnHeaders: true,
      })
      const setCookie = res.headers.get('set-cookie')
      const cookieValue = readSessionCookieValue(setCookie, cookiePrefix)
      if (!cookieValue) throw new Error('invalid credentials')

      const session = await auth.api.getSession({
        headers: new Headers({ cookie: `${sessionCookieName}=${cookieValue}` }),
      })
      if (!session?.user) throw new Error('invalid credentials')

      const principal = toPrincipal(session.user.id, session.session.id)
      return {
        principal,
        accessToken: cookieValue,
        refreshToken: session.session.token,
      }
    },

    async signOut(sessionId: string): Promise<void> {
      // auth.api.signOut needs the caller's cookie header — with none it revokes nothing
      // (silent fail-open). Session-row delete is the authoritative revoke better-auth honors.
      await config.db.delete(baSession).where(eq(baSession.id, sessionId))
    },

    async verifySession(token: string): Promise<Principal | null> {
      const session = await auth.api.getSession({
        headers: new Headers({ cookie: `${sessionCookieName}=${token}` }),
      })
      if (!session?.user) return null
      return toPrincipal(session.user.id, session.session.id)
    },

    async refresh(refreshToken: string) {
      const session = await auth.api.getSession({
        headers: new Headers({ cookie: `${sessionCookieName}=${refreshToken}` }),
      })
      if (!session?.user) return null
      return {
        principal: toPrincipal(session.user.id, session.session.id),
        accessToken: refreshToken,
      }
    },

    async createUser({ email, password, roles }) {
      if (roles !== undefined) {
        throw new EngineMismatchError(
          'createUser roles not supported by engine-better-auth — assign roles via a better-auth admin plugin',
        )
      }
      const res = await auth.api.signUpEmail({
        body: { email, password, name: email.split('@')[0] ?? 'user' },
      })
      if (!res?.user?.id) throw new Error('create failed')
      return { userId: res.user.id }
    },

    async setPassword(_userId: string, _password: string): Promise<void> {
      throw new EngineMismatchError(
        'setPassword not supported by engine-better-auth — use the better-auth change-password handler',
      )
    },

    async verifyPassword(_password: string, _storedHash: string): Promise<boolean> {
      throw new EngineMismatchError(
        'verifyPassword not supported by engine-better-auth — route credentials through the better-auth sign-in handler',
      )
    },
  }
}

export { betterAuth }
