import { betterAuth } from 'better-auth'
import { admin, phoneNumber, emailOTP } from 'better-auth/plugins'
import { APIError } from 'better-auth/api'
import Database from 'better-sqlite3'
import { getDbPath } from '@/lib/db'
import { userRepo } from '@/lib/database/repositories/user.repo'
import { claimLegacyDataForUser, provisionDefaultWorkspace } from './provision'
import { sendAliyunSMS } from '@/lib/sms/aliyun'
import { sendResendOTP } from '@/lib/email/resend'

const dbPath = getDbPath()
const isDomestic = process.env.AUTH_MODE === 'zh'

function providerConfig() {
  const providers: Record<string, Record<string, unknown>> = {}
  if (process.env.GITHUB_CLIENT_ID && process.env.GITHUB_CLIENT_SECRET) {
    providers.github = {
      clientId: process.env.GITHUB_CLIENT_ID,
      clientSecret: process.env.GITHUB_CLIENT_SECRET,
      scope: ['read:user', 'user:email'],
      mapProfileToUser: (profile: {
        id: number | string
        email?: string | null
        login?: string
        name?: string | null
        avatar_url?: string | null
      }) => ({
        email:
            profile.email ??
            `${profile.login ?? profile.id}@users.noreply.github.com`,
        name: profile.name ?? profile.login ?? `github-${profile.id}`,
        image: profile.avatar_url ?? undefined,
      }),
    } as (typeof providers)[string]
  }
  if (process.env.GOOGLE_CLIENT_ID && process.env.GOOGLE_CLIENT_SECRET) {
    providers.google = {
      clientId: process.env.GOOGLE_CLIENT_ID,
      clientSecret: process.env.GOOGLE_CLIENT_SECRET,
    }
  }
  return providers
}

export const auth = betterAuth({
  appName: 'Forge',
  baseURL: process.env.BETTER_AUTH_URL || process.env.FORGE_PUBLIC_URL || 'http://localhost:3000',
  secret: process.env.BETTER_AUTH_SECRET || process.env.AUTH_SECRET || 'forge-dev-auth-secret-change-me-in-production',
  database: new Database(dbPath),
  emailAndPassword: {
    // Disabled in favor of email+OTP (international) / phone+OTP (domestic).
    // The previous email+password flow could not verify the email's validity.
    enabled: false,
  },
  socialProviders: providerConfig(),
  account: {
    accountLinking: {
      enabled: true,
      trustedProviders: ['github', 'google'],
    },
  },
  user: {
    additionalFields: {
      role: {
        type: 'string',
        required: true,
        defaultValue: 'user',
        input: false,
      },
    },
  },
  plugins: [
    admin({
      adminRoles: ['admin'],
    }),
    ...(isDomestic
      ? [
          phoneNumber({
            sendOTP: async ({ phoneNumber, code }) => {
              if (process.env.NODE_ENV === 'development') {
                console.log(`[SMS OTP] ${phoneNumber}: ${code}`)
              }
              const result = await sendAliyunSMS(phoneNumber, code)
              if (!result.success) {
                throw new APIError('INTERNAL_SERVER_ERROR', {
                  message: result.error || '短信发送失败，请稍后重试',
                })
              }
            },
            signUpOnVerification: {
              getTempEmail: (phone) => `phone.${crypto.randomUUID().slice(0, 8)}@forge.internal`,
              getTempName: (phone) => `用户${phone.replace(/\D/g, '').slice(-4)}`,
            },
          }),
        ]
      : [
          emailOTP({
            // Verifying the OTP also signs the user in (and auto-creates an
            // account on first use), giving us the email+OTP symmetry of the
            // domestic phone+OTP flow.
            sendVerificationOTP: async ({ email, otp, type }) => {
              if (process.env.NODE_ENV === 'development') {
                console.log(`[Email OTP] ${type} ${email}: ${otp}`)
              }
              const result = await sendResendOTP(email, otp, type)
              if (!result.success) {
                throw new APIError('INTERNAL_SERVER_ERROR', {
                  message: result.error || 'Failed to send email, please try again',
                })
              }
            },
          }),
        ]),
  ],
  databaseHooks: {
    user: {
      create: {
        before: async (user) => {
          const count = userRepo.count()
          return {
            data: {
              ...user,
              role: count === 0 ? 'admin' : 'user',
            },
          }
        },
        after: async (user) => {
          claimLegacyDataForUser(user.id)
          provisionDefaultWorkspace({
            id: user.id,
            email: user.email,
            name: user.name,
            image: user.image,
            role: String(user.role || 'user'),
          })
        },
      },
    },
  },
})

export type ForgeAuthSession = typeof auth.$Infer.Session
