import { betterAuth } from 'better-auth'
import { admin, magicLink } from 'better-auth/plugins'
import { actor, fabric } from '@pikku/better-auth'
import { pikkuBetterAuth } from '#pikku/pikku-types.gen.js'
import { UserRoles, UserTypes } from '@heygermany/sdk'
import type { EmailTemplateName } from '../../.pikku/email/pikku-emails.gen.js'

const DEV_BETTER_AUTH_SECRET = 'dev-better-auth-secret-change-me'

const isLocalDomain = (domain: string) => {
  return !domain || domain.includes('localhost') || domain.includes('127.0.0.1')
}

const getOrigin = (domain: string) => {
  if (isLocalDomain(domain)) {
    return 'http://localhost:3000'
  }
  return `https://api.${domain}`
}

export const auth = pikkuBetterAuth(async ({ kysely, secrets, variables, config, emailService }) => {
  const betterAuthSecret =
    (await variables.get('BETTER_AUTH_SECRET')) ||
    await secrets.getSecret('BETTER_AUTH_SECRET').catch(() => DEV_BETTER_AUTH_SECRET)

  if (process.env.NODE_ENV === 'production' && betterAuthSecret === DEV_BETTER_AUTH_SECRET) {
    throw new Error('BETTER_AUTH_SECRET must be configured in production')
  }

  // Genuinely optional: unset simply disables /api/auth/sign-in/actor (scenarios
  // off for this deployment) — the actor plugin refuses all sign-ins without it.
  const SCENARIO_ACTOR_SECRET = await secrets
    .getSecret('SCENARIO_ACTOR_SECRET')
    .catch(() => undefined)
  // Fabric operator admin: the RSA public key the control plane's token is
  // verified against. The Fabric deployer pushes FABRIC_AUTH_PUBLIC_KEY onto
  // every stage; locally it's simply absent, which disables /sign-in/fabric.
  const FABRIC_AUTH_PUBLIC_KEY = await variables.get('FABRIC_AUTH_PUBLIC_KEY')

  return betterAuth({
    secret: betterAuthSecret,
    baseURL: getOrigin(config.domain),
    trustedOrigins: [
      'http://localhost:3001',
      'http://localhost:3000',
      'http://127.0.0.1:3001',
      'http://127.0.0.1:3000',
      `https://${config.domain}`,
      `https://api.${config.domain}`,
      // Fabric deploys every runtime to a *.pikkufabric.dev subdomain; the
      // request Origin is that dynamic host, so trust it or auth 403s
      // INVALID_ORIGIN (baseURL is pinned to the real api.<domain>).
      'https://*.pikkufabric.dev',
    ],
    advanced: {
      database: {
        generateId: 'uuid',
      },
      crossSubDomainCookies: {
        enabled: !isLocalDomain(config.domain),
        domain: isLocalDomain(config.domain) ? undefined : config.domain,
      },
    },
    database: {
      db: kysely as any,
      type: 'sqlite',
    },
    user: {
      modelName: 'authUser',
      fields: {
        emailVerified: 'emailVerified',
        createdAt: 'createdAt',
        updatedAt: 'updatedAt',
      },
      additionalFields: {
        role: {
          type: 'string',
          required: true,
          defaultValue: UserRoles.OWNER,
          input: true,
        },
        type: {
          type: 'string',
          required: true,
          defaultValue: UserTypes.CANDIDATE,
          input: true,
        },
      },
    },
    session: {
      modelName: 'authSession',
      fields: {
        expiresAt: 'expiresAt',
        createdAt: 'createdAt',
        updatedAt: 'updatedAt',
        ipAddress: 'ipAddress',
        userAgent: 'userAgent',
        userId: 'userId',
      },
    },
    account: {
      modelName: 'authAccount',
      fields: {
        accountId: 'accountId',
        providerId: 'providerId',
        userId: 'userId',
        accessToken: 'accessToken',
        refreshToken: 'refreshToken',
        idToken: 'idToken',
        accessTokenExpiresAt: 'accessTokenExpiresAt',
        refreshTokenExpiresAt: 'refreshTokenExpiresAt',
        createdAt: 'createdAt',
        updatedAt: 'updatedAt',
      },
    },
    verification: {
      modelName: 'authVerification',
      fields: {
        expiresAt: 'expiresAt',
        createdAt: 'createdAt',
        updatedAt: 'updatedAt',
      },
    },
    emailAndPassword: {
      enabled: true,
    },
    emailVerification: {
      sendVerificationEmail: async ({ user, url }) => {
        await emailService?.send({
          to: user.email,
          template: {
            name: 'candidate-double-opt-in',
            locale: 'en',
            data: {
              firstname: user.name,
              verificationLink: url,
            },
          },
        })
      },
    },
    // admin(): list/setRole/ban/impersonate users (db/sqlite/0005-admin.sql).
    // actor(): synthetic scenario users (db/sqlite/0004-user-actor.sql).
    // fabric(): Fabric console operator sign-in (db/sqlite/0006-fabric.sql).
    plugins: [
      actor({ secret: SCENARIO_ACTOR_SECRET }),
      admin(),
      fabric({ publicKey: FABRIC_AUTH_PUBLIC_KEY }),
      magicLink({
        disableSignUp: true,
        sendMagicLink: async ({ email: to, token, metadata }) => {
          const user = await kysely
            .selectFrom('authUser')
            .select(['id', 'name'])
            .where('email', '=', to)
            .executeTakeFirst()

          const name = user?.name || to.split('@')[0]
          const templateName = getMagicLinkTemplateName(metadata?.template)
          const magicLink = buildMagicLink(config.domain, token)

          await emailService?.send({
            to,
            template: {
              name: templateName,
              locale: 'en',
              data: {
                firstname: name,
                magicLink,
              },
            },
          })
        },
      }),
    ],
  })
})

const buildMagicLink = (domain: string, token: string) => {
  const host = isLocalDomain(domain) ? `http://${domain}` : `https://${domain}`
  return `${host}/auth/magic-link?token=${token}&redirect=/jobs/application`
}

const MAGIC_LINK_TEMPLATE_NAMES = new Set<EmailTemplateName>([
  'candidate-magic-link',
  'candidate-qualified',
  'candidate-invalid-documents',
  'candidate-process-incomplete',
])

const getMagicLinkTemplateName = (template: unknown): EmailTemplateName => {
  if (typeof template === 'string' && MAGIC_LINK_TEMPLATE_NAMES.has(template as EmailTemplateName)) {
    return template as EmailTemplateName
  }

  return 'candidate-magic-link'
}
