import { betterAuth } from "better-auth";
import { Pool } from "pg";
import { nextCookies } from "better-auth/next-js";
import { emailOTP } from "better-auth/plugins";
import { parseSSLConfig, stripSSLParams } from "./db.js";
import { EmailFactory } from "./email/index.js";
import {
  sendVerifyEmail,
  sendResetPasswordEmail,
  sendOtpVerificationEmail
} from "./email/send.js";
import { I18N_CONFIG, USER_ROLES_CONFIG, TEAMS_CONFIG, AUTH_CONFIG, APP_CONFIG_MERGED } from "./config/index.js";
import { getUserFlags } from "./services/user-flags.service.js";
import { TeamService } from "./services/team.service.js";
import { TeamMemberService } from "./services/team-member.service.js";
import { shouldSkipTeamCreation } from "./auth-context.js";
import { isDomainAllowed } from "./auth/registration-helpers.js";
import { registrationGuardPlugin } from "./auth/registration-guard-plugin.js";
import { getCorsOrigins } from "./utils/cors.js";
const isProd = process.env.NODE_ENV === "production";
const baseUrl = process.env.BETTER_AUTH_URL || process.env.NEXT_PUBLIC_APP_URL || "http://localhost:5173";
const emailService = EmailFactory.create();
const databaseUrl = process.env.DATABASE_URL;
const cleanUrl = stripSSLParams(databaseUrl);
const connectionString = cleanUrl.includes("?") ? `${cleanUrl}&pgbouncer=true` : `${cleanUrl}?pgbouncer=true`;
const pool = new Pool({
  connectionString,
  ssl: parseSSLConfig(databaseUrl),
  connectionTimeoutMillis: 1e4,
  idleTimeoutMillis: 3e4,
  max: 20
});
const auth = betterAuth({
  database: pool,
  secret: process.env.BETTER_AUTH_SECRET,
  user: {
    modelName: "users",
    // Use plural table name instead of default "user"
    additionalFields: {
      firstName: {
        type: "string",
        required: false,
        input: true
        // Allow user to provide this during signup
      },
      lastName: {
        type: "string",
        required: false,
        input: true
        // Allow user to provide this during signup
      },
      language: {
        type: "string",
        required: false,
        input: true,
        // Allow user to provide this during signup
        defaultValue: I18N_CONFIG.defaultLocale
        // Set default language
      },
      role: {
        type: "string",
        required: false,
        input: false,
        // Don't allow users to set their own role
        defaultValue: USER_ROLES_CONFIG.defaultRole
        // Default role from config
      }
    },
    // Enable Better Auth's change-email endpoint so `POST /api/auth/change-email`
    // is accepted. Without this the endpoint returns 400
    // `{"message":"Change email is disabled"}` and any theme-level
    // post-success handlers (e.g. notifying the old address that the email
    // was changed) cannot fire end-to-end.
    //
    // We intentionally omit `sendChangeEmailVerification` — the change is
    // processed immediately on POST, and themes can extend their own
    // post-success hooks for double-opt-in or notification flows.
    changeEmail: {
      enabled: true
    }
  },
  emailAndPassword: {
    enabled: true,
    requireEmailVerification: true,
    minPasswordLength: 8,
    maxPasswordLength: 128,
    resetPasswordTokenExpiresIn: 60 * 60,
    // 1 hour
    sendResetPassword: async ({ user, url, token }) => {
      try {
        const resetUrl = `${url}?token=${token}`;
        const template = await sendResetPasswordEmail({
          userName: user.firstName || "",
          resetUrl,
          appName: process.env.NEXT_PUBLIC_APP_NAME || "Your App",
          expiresIn: "1 hour"
        }, I18N_CONFIG.defaultLocale);
        const response = await emailService.send({
          to: user.email,
          ...template
        });
        if (!response.success) {
          console.error("Failed to send reset password email:", response.error);
          throw new Error("Failed to send reset password email");
        }
      } catch (error) {
        console.error("Error sending reset password email:", error);
        throw error;
      }
    }
  },
  emailVerification: {
    // Controlled by AUTH_CONFIG.sendVerificationEmailOnSignup (default: true).
    // Themes opt out by setting `auth.sendVerificationEmailOnSignup: false`
    // in their app.config.ts when they verify email ownership through other
    // means (OTP, invitation token, claim-account flow, etc.).
    sendOnSignUp: AUTH_CONFIG.sendVerificationEmailOnSignup ?? true,
    sendVerificationEmail: async ({ user, token }) => {
      try {
        const verifyUrl = `${baseUrl}/api/auth/verify-email?token=${token}`;
        const template = await sendVerifyEmail({
          userName: user.firstName || "",
          verificationUrl: verifyUrl,
          appName: process.env.NEXT_PUBLIC_APP_NAME || "Your App"
        }, I18N_CONFIG.defaultLocale);
        const response = await emailService.send({
          to: user.email,
          ...template
        });
        if (!response.success) {
          console.error("Failed to send verification email:", response.error);
          throw new Error("Failed to send verification email");
        }
      } catch (error) {
        console.error("Error sending verification email:", error);
        throw error;
      }
    },
    verifyTokenExpiresIn: 60 * 60 * 24
    // 24 hours
  },
  socialProviders: {
    google: {
      clientId: process.env.GOOGLE_CLIENT_ID,
      clientSecret: process.env.GOOGLE_CLIENT_SECRET,
      mapProfileToUser: (profile) => {
        const firstName = profile.given_name || profile.name.split(" ")[0] || "";
        const lastName = profile.family_name || profile.name.split(" ").slice(1).join(" ") || "";
        return {
          email: profile.email,
          name: profile.name,
          // Better Auth expects 'name' field
          firstName,
          // Use given_name if available, otherwise split name
          lastName,
          // Use family_name if available, otherwise split name
          language: I18N_CONFIG.defaultLocale,
          // Assign default language for Google OAuth
          role: USER_ROLES_CONFIG.defaultRole,
          // Assign default role for Google OAuth users
          image: profile.picture,
          emailVerified: profile.email_verified || false
        };
      }
    }
  },
  baseURL: baseUrl,
  // Use unified CORS configuration from app.config.ts + theme extensions + env vars
  trustedOrigins: getCorsOrigins(APP_CONFIG_MERGED),
  // Redirect auth errors to our custom error page instead of Better Auth's default
  onAPIError: {
    errorURL: "/auth-error"
  },
  plugins: [
    registrationGuardPlugin(),
    // Intercept OAuth signup attempts
    emailOTP({
      async sendVerificationOTP({ email, otp, type }) {
        const template = await sendOtpVerificationEmail({
          email,
          otp,
          type,
          appName: process.env.NEXT_PUBLIC_APP_NAME || "Your App"
        }, I18N_CONFIG.defaultLocale);
        await emailService.send({ to: email, ...template });
      },
      otpLength: 6,
      expiresIn: 300,
      // 5 minutes
      sendVerificationOnSignUp: false,
      disableSignUp: false
      // auto-create user on first OTP sign-in
    }),
    nextCookies()
    // MUST be the last plugin for Next.js cookie handling
  ],
  session: {
    expiresIn: 60 * 60 * 24 * 7,
    // 1 week
    updateAge: 60 * 60 * 24,
    // 1 day
    cookieCache: {
      enabled: true,
      maxAge: 60 * 5
      // 5 minutes
    }
  },
  // Database hooks for user lifecycle events (Better Auth API)
  databaseHooks: {
    user: {
      create: {
        // Validate registration mode before creating user
        before: async (user) => {
          var _a, _b, _c, _d;
          const registrationMode = ((_b = (_a = AUTH_CONFIG) == null ? void 0 : _a.registration) == null ? void 0 : _b.mode) ?? "open";
          if (registrationMode === "invitation-only") {
            if (!shouldSkipTeamCreation()) {
              const existingTeam = await TeamService.getGlobal();
              if (existingTeam) {
                throw new Error("SIGNUP_RESTRICTED: Registration requires an invitation. Contact an administrator.");
              }
            }
          }
          if (registrationMode === "domain-restricted" || registrationMode === "domain-open") {
            const allowedDomains = ((_d = (_c = AUTH_CONFIG) == null ? void 0 : _c.registration) == null ? void 0 : _d.allowedDomains) ?? [];
            if (allowedDomains.length > 0 && !isDomainAllowed(user.email, allowedDomains)) {
              console.log(`[Auth] Blocked registration for ${user.email}: domain not in allowedDomains (allowed: ${allowedDomains.join(", ")})`);
              throw new Error(`DOMAIN_NOT_ALLOWED: Email domain not authorized. Please use an email from: ${allowedDomains.join(", ")}`);
            }
          }
          return user;
        },
        // Create team when a new user signs up (email/password or OAuth)
        // Team type depends on configured teams mode
        after: async (user) => {
          try {
            if (shouldSkipTeamCreation()) {
              console.log(`[Teams] Skipping team creation for user ${user.id} (invite flow)`);
              return;
            }
            const teamsMode = TEAMS_CONFIG.mode;
            switch (teamsMode) {
              case "single-user":
              case "multi-tenant":
                await TeamService.create(user.id);
                console.log(`[Teams] Team created for user ${user.id} (mode: ${teamsMode})`);
                break;
              case "single-tenant":
                const existingTeam = await TeamService.getGlobal();
                if (!existingTeam) {
                  const teamName = user.name ? `${user.name}'s Company` : "Company";
                  await TeamService.create(user.id, teamName);
                  console.log(`[Teams] Global team created for first user ${user.id}`);
                } else {
                  await TeamMemberService.addToGlobal(user.id, "member");
                  console.log(`[Teams] User ${user.id} auto-joined global team (single-tenant mode)`);
                }
                break;
              default:
                console.warn(`[Teams] Unknown teams mode: ${teamsMode}`);
            }
          } catch (error) {
            console.error(`[Teams] Failed to create team for user ${user.id}:`, error);
          }
        }
      }
    },
    session: {
      create: {
        // Enforce domain restrictions on EVERY login (not just signup)
        before: async (session) => {
          var _a, _b, _c, _d, _e;
          const registrationMode = ((_b = (_a = AUTH_CONFIG) == null ? void 0 : _a.registration) == null ? void 0 : _b.mode) ?? "open";
          if (registrationMode === "domain-restricted" || registrationMode === "domain-open") {
            const allowedDomains = ((_d = (_c = AUTH_CONFIG) == null ? void 0 : _c.registration) == null ? void 0 : _d.allowedDomains) ?? [];
            if (allowedDomains.length > 0) {
              const result = await pool.query(
                "SELECT email FROM users WHERE id = $1 LIMIT 1",
                [session.userId]
              );
              const email = (_e = result.rows[0]) == null ? void 0 : _e.email;
              if (email && !isDomainAllowed(email, allowedDomains)) {
                console.log(`[Auth] Blocked sign-in for ${email}: domain not in allowedDomains`);
                return false;
              }
            }
          }
          return session;
        }
      }
    }
  },
  callbacks: {
    session: {
      // Add user flags to session data
      async onSignIn({ user, session }) {
        try {
          const flags = await getUserFlags(user.id);
          return {
            user: {
              ...user,
              flags
            },
            session
          };
        } catch (error) {
          console.error("Error loading user flags in session:", error);
          return {
            user: {
              ...user,
              flags: []
            },
            session
          };
        }
      },
      // Ensure flags are available in session retrieval
      async onSessionUpdate({ user, session }) {
        try {
          const flags = await getUserFlags(user.id);
          return {
            user: {
              ...user,
              flags
            },
            session
          };
        } catch (error) {
          console.error("Error loading user flags in session update:", error);
          return {
            user: {
              ...user,
              flags: user.flags || []
            },
            session
          };
        }
      }
    }
  },
  // Removed additionalFields - will use standard name and image fields
  advanced: {
    crossSubDomainCookies: {
      enabled: false
    },
    useSecureCookies: isProd,
    defaultCookieAttributes: {
      httpOnly: true,
      secure: isProd,
      sameSite: "lax",
      path: "/"
    }
  }
});
async function getTypedSession(headers) {
  const session = await auth.api.getSession({ headers });
  if (!session) return null;
  return session;
}
export {
  auth,
  getTypedSession
};
