import { betterAuth } from 'better-auth';
import { bearer, oidcProvider } from 'better-auth/plugins';
import { sso } from '@better-auth/sso';
import { credentials } from 'better-auth-credentials-plugin';
import { authenticate } from 'ldap-authentication';
import { z } from 'zod';
import { db } from './db.js';

const PORT = parseInt(process.env.PORT ?? '3014', 10);
const baseUrl =
  process.env.BETTER_AUTH_URL || process.env.AUTH_SERVICE_URL || `http://localhost:${PORT}`;

export const auth = betterAuth({
  database: db,
  basePath: '/api/auth',
  baseURL: baseUrl,
  telemetry: { enabled: false },
  trustedOrigins: [
    baseUrl,
    'http://localhost:3000',
    'http://localhost:3014',
    `http://localhost:${PORT}`,
    ...(process.env.TRUSTED_ORIGINS || '').split(',').filter(Boolean),
  ],
  emailAndPassword: {
    enabled: true,
  },
  plugins: [
    bearer(),
    sso(),
    oidcProvider({
      loginPage: '/sign-in',
      allowDynamicClientRegistration: true,
      trustedClients: (() => {
        try {
          if (process.env.TRUSTED_CLIENTS) {
            return JSON.parse(process.env.TRUSTED_CLIENTS);
          }
        } catch {
          /* ignore */
        }
        return [
          {
            clientId: process.env.DEFAULT_CLIENT_ID || 'gooseek',
            clientSecret: process.env.DEFAULT_CLIENT_SECRET || 'gooseek-secret-change-me',
            name: 'GooSeek',
            type: 'web' as const,
            redirectUrls: [
              'http://localhost:3000/api/auth/callback/better-auth',
              `${baseUrl}/api/auth/callback/better-auth`,
              'https://gooseek.ru/api/auth/callback/better-auth',
              'https://www.gooseek.ru/api/auth/callback/better-auth',
            ],
            disabled: false,
            skipConsent: true,
          },
        ];
      })(),
    }),
    ...(process.env.LDAP_URL
      ? [
          // @ts-expect-error — credentials plugin has deep generic instantiation
          credentials({
            autoSignUp: true,
            linkAccountIfExisting: true,
            providerId: 'ldap',
            path: '/sign-in/ldap',
            inputSchema: z.object({
              credential: z.string().min(1, 'Username or DN required'),
              password: z.string().min(1, 'Password required'),
            }),
            async callback(_ctx, parsed) {
              const ldapResult = await authenticate({
                ldapOpts: {
                  url: process.env.LDAP_URL!,
                  connectTimeout: 5000,
                  ...(process.env.LDAP_URL!.startsWith('ldaps://')
                    ? { tlsOptions: { minVersion: 'TLSv1.2' } }
                    : {}),
                },
                adminDn: process.env.LDAP_BIND_DN || '',
                adminPassword: process.env.LDAP_PASSWORD || '',
                userSearchBase: process.env.LDAP_BASE_DN || '',
                usernameAttribute: process.env.LDAP_USERNAME_ATTR || 'uid',
                username: parsed.credential,
                userPassword: parsed.password,
              });

              const uid = ldapResult[process.env.LDAP_USERNAME_ATTR || 'uid'];
              const email =
                (Array.isArray(ldapResult.mail) ? ldapResult.mail[0] : ldapResult.mail) ||
                `${uid}@local`;

              return {
                email,
                name: ldapResult.displayName || ldapResult.cn || String(uid),
              };
            },
          }),
        ]
      : []),
  ],
});
