/**
 * NextAuth Configuration with Prisma Database
 *
 * Security improvements:
 * - PostgreSQL database for persistent user storage
 * - Bcrypt password hashing (12 rounds)
 * - Session timeout and refresh
 * - Account lockout tracking
 * - Audit logging integration
 * - IP tracking with geolocation
 * - Login history tracking
 * - Account suspension enforcement
 */

import NextAuth, { NextAuthConfig } from "next-auth";
import { skipCSRFCheck } from "@auth/core";
import Google from "next-auth/providers/google";
import Credentials from "next-auth/providers/credentials";
import bcrypt from "bcryptjs";
import { prisma } from "./db";
import { logger } from "./logger";
import { getLocationFromIP, getIPFromRequest } from "./geolocation";
import { createId } from "@paralleldrive/cuid2";
import { headers, cookies } from "next/headers";
import { createSession as createUserSession } from "./services/session-manager";

// Note: Google OAuth credentials validation moved to runtime
// Build-time validation was causing Docker build failures

export const authConfig: NextAuthConfig = {
  providers: [
    Google({
      clientId: process.env.GOOGLE_CLIENT_ID || "",
      clientSecret: process.env.GOOGLE_CLIENT_SECRET || "",
      authorization: {
        params: {
          prompt: "consent",
          access_type: "offline", // Required for refresh token
          scope: [
            "openid",
            "email",
            "profile",
            "https://www.googleapis.com/auth/contacts.readonly", // Read contacts
            "https://www.googleapis.com/auth/contacts.other.readonly", // Read other contacts
          ].join(" "),
        },
      },
    }),
    Credentials({
      id: "staff-credentials",
      name: "Staff Login",
      credentials: {
        username: { label: "Username", type: "text" },
        password: { label: "Password", type: "password" },
      },
      async authorize(credentials) {
        if (!credentials?.username || !credentials?.password) {
          throw new Error("MISSING_CREDENTIALS");
        }

        try {
          // Find staff user by username
          const user = await prisma.users.findUnique({
            where: { username: credentials.username as string },
          });

          if (!user) {
            logger.info("Staff login attempt for non-existent username", {
              username: credentials.username,
            });
            throw new Error("INVALID_CREDENTIALS");
          }

          // Check if user is staff/admin
          if (user.role === "CUSTOMER") {
            logger.warn("Customer attempted staff login", {
              username: credentials.username,
            });
            throw new Error("UNAUTHORIZED_ROLE");
          }

          // Check if account is active
          if (!user.isActive) {
            logger.warn("Inactive staff account login attempt", {
              username: credentials.username,
            });
            throw new Error("ACCOUNT_INACTIVE");
          }

          // Check account lockout
          if (user.lockedUntil && new Date() < user.lockedUntil) {
            const remainingMinutes = Math.ceil(
              (user.lockedUntil.getTime() - Date.now()) / (60 * 1000),
            );
            logger.info("Staff account locked", {
              username: credentials.username,
              remainingMinutes,
            });
            throw new Error(`ACCOUNT_LOCKED:${remainingMinutes}`);
          }

          // Verify password
          if (!user.password) {
            throw new Error("NO_PASSWORD_SET");
          }

          const isValid = await bcrypt.compare(
            credentials.password as string,
            user.password,
          );

          if (!isValid) {
            // Increment failed attempts (stricter for staff - 3 attempts)
            const newAttempts = user.failedAttempts + 1;
            const updates: { failedAttempts: number; lockedUntil?: Date } = {
              failedAttempts: newAttempts,
            };

            // Lock after 3 failed attempts for 1 hour (stricter than customer)
            if (newAttempts >= 3) {
              updates.lockedUntil = new Date(Date.now() + 60 * 60 * 1000);
              logger.warn("Staff account locked", {
                username: credentials.username,
                attempts: newAttempts,
              });
            }

            await prisma.users.update({
              where: { id: user.id },
              data: updates,
            });

            // Log failed attempt

            if (newAttempts >= 3) {
              throw new Error("ACCOUNT_LOCKED:60");
            }

            throw new Error(`INVALID_PASSWORD:${3 - newAttempts}`);
          }

          // Success - update login details
          await prisma.users.update({
            where: { id: user.id },
            data: {
              failedAttempts: 0,
              lockedUntil: null,
              lastLoginAt: new Date(),
            },
          });

          // Log successful staff login

          logger.info("Staff login successful", {
            username: credentials.username,
            role: user.role,
          });

          return {
            id: user.id,
            email: user.email,
            name: user.name,
            role: user.role,
          };
        } catch (error) {
          if (error instanceof Error) {
            throw error;
          }
          logger.error("Staff authorization error", error);
          throw new Error("AUTH_ERROR");
        }
      },
    }),
    Credentials({
      name: "credentials",
      credentials: {
        email: { label: "Email", type: "email" },
        password: { label: "Password", type: "password" },
      },
      async authorize(credentials) {
        if (!credentials?.email || !credentials?.password) {
          throw new Error("MISSING_CREDENTIALS");
        }

        try {
          // Find user in database
          const user = await prisma.users.findUnique({
            where: { email: credentials.email as string },
          });

          // 🎯 CUSTOMIZED ERROR: User not found
          if (!user) {
            logger.info("Login attempt for non-existent user", {
              email: credentials.email,
            });
            throw new Error("USER_NOT_FOUND");
          }

          // 🔐 SPECIAL CASE: OTP-verified login (passwordless)
          // When OTP is verified on frontend, it calls signIn with special flag
          if (credentials.password === "__OTP_VERIFIED__") {
            logger.info("OTP-verified login - creating NextAuth session", {
              email: user.email,
            });

            // Note: headers() can fail in server action context, handle gracefully
            let ipAddress = "0.0.0.0";
            let userAgent = "Unknown";

            try {
              const headersList = await headers();
              ipAddress =
                headersList.get("x-real-ip") ||
                headersList.get("x-forwarded-for")?.split(",")[0] ||
                "0.0.0.0";
              userAgent = headersList.get("user-agent") || "Unknown";
            } catch (error) {
              // headers() not available in this context (server action)
              // This is OK - session was already created in verify-login-otp endpoint
              logger.info(
                "Headers not available in OTP signIn context (expected in server action)",
              );
            }

            // Update last login (only if we got IP - otherwise it was already updated)
            if (ipAddress !== "0.0.0.0") {
              await prisma.users.update({
                where: { id: user.id },
                data: {
                  lastLoginAt: new Date(),
                  failedAttempts: 0,
                  lockedUntil: null,
                  lastLoginIp: ipAddress,
                },
              });
            }

            // Return user for session creation
            // Note: No need to create session token here - already done in verify-login-otp
            return {
              id: user.id,
              email: user.email,
              name: user.name,
            };
          }

          // 🎯 CUSTOMIZED ERROR: No password set (passwordless account trying password login)
          if (!user.password) {
            logger.info("Login attempt for passwordless account", {
              email: user.email,
            });
            throw new Error("NO_PASSWORD_SET");
          }

          // 🎯 CUSTOMIZED ERROR: Account locked
          if (user.lockedUntil && new Date() < user.lockedUntil) {
            const remainingMinutes = Math.ceil(
              (user.lockedUntil.getTime() - Date.now()) / (60 * 1000),
            );
            logger.info("Account locked", {
              email: user.email,
              remainingMinutes,
            });
            throw new Error(`ACCOUNT_LOCKED:${remainingMinutes}`);
          }

          // Verify password
          const isValid = await bcrypt.compare(
            credentials.password as string,
            user.password,
          );

          // 🎯 CUSTOMIZED ERROR: Invalid password
          if (!isValid) {
            // Increment failed attempts
            const newAttempts = user.failedAttempts + 1;
            const updates: { failedAttempts: number; lockedUntil?: Date } = {
              failedAttempts: newAttempts,
            };

            // Lock account after 5 failed attempts for 30 minutes
            if (newAttempts >= 5) {
              updates.lockedUntil = new Date(Date.now() + 30 * 60 * 1000);
              logger.warn("Account locked due to failed attempts", {
                email: user.email,
                attempts: newAttempts,
              });
            }

            await prisma.users.update({
              where: { id: user.id },
              data: updates,
            });

            // Check if this triggers account lock
            if (newAttempts >= 5) {
              throw new Error("ACCOUNT_LOCKED:30");
            }

            // Return remaining attempts before lockout
            const remainingAttempts = 5 - newAttempts;
            logger.info("Invalid password attempt", {
              email: user.email,
              remainingAttempts,
            });
            throw new Error(`INVALID_PASSWORD:${remainingAttempts}`);
          }

          // Success - clear failed attempts and update last login
          await prisma.users.update({
            where: { id: user.id },
            data: {
              failedAttempts: 0,
              lockedUntil: null,
              lastLoginAt: new Date(),
            },
          });

          logger.info("Successful login", {
            email: user.email,
            userId: user.id,
          });

          return {
            id: user.id,
            email: user.email,
            name: user.name,
          };
        } catch (error) {
          // Re-throw our custom errors
          if (
            (error instanceof Error &&
              error.message.startsWith("USER_NOT_FOUND")) ||
            (error instanceof Error &&
              error.message.startsWith("INVALID_PASSWORD")) ||
            (error instanceof Error &&
              error.message.startsWith("ACCOUNT_LOCKED")) ||
            (error instanceof Error &&
              error.message.startsWith("NO_PASSWORD_SET")) ||
            (error instanceof Error &&
              error.message.startsWith("MISSING_CREDENTIALS"))
          ) {
            throw error;
          }

          // Log unexpected errors
          logger.error("Authorization error", error);
          throw new Error("AUTH_ERROR");
        }
      },
    }),
  ],
  // Note: Removed hardcoded pages config to support dynamic [lang] routing
  // NextAuth will use default behavior and redirect properly after authentication
  callbacks: {
    async signIn({ user, account }) {
      try {
        // Extract IP address from headers (set by middleware or API route)
        const headersList = await headers();
        const ipAddress =
          headersList.get("x-real-ip") ||
          headersList.get("x-forwarded-for")?.split(",")[0] ||
          "0.0.0.0";
        const userAgent = headersList.get("user-agent") || undefined;
        const cookieStore = cookies();

        // For Google OAuth
        if (account?.provider === "google" && user.email) {
          try {
            // 🆕 CHECK IF THIS IS A SIGNUP FLOW (not login)
            // During signup, we handle OTP verification before account creation
            // The frontend callback page will manage this flow
            // Check the callback URL from cookies (NextAuth stores it there)
            let isSignupFlow = false;
            let callbackUrlCookie = "";

            try {
              const { cookies: getCookies } = await import("next/headers");
              const cookieStore = await getCookies();
              // Check all possible callback URL cookie names (varies by environment/HTTPS)
              callbackUrlCookie =
                cookieStore.get("__Secure-next-auth.callback-url")?.value ||
                cookieStore.get("next-auth.callback-url")?.value ||
                cookieStore.get("authjs.callback-url")?.value ||
                "";
              isSignupFlow = callbackUrlCookie.includes(
                "/signup/google-callback",
              );

              logger.info("Google OAuth callback URL check", {
                email: user.email,
                callbackUrlCookie,
                isSignupFlow,
                allCookies: Array.from(cookieStore.getAll()).map((c) => c.name),
              });
            } catch (cookieError) {
              logger.warn("Could not read callback URL cookie", cookieError);
            }

            // If this is signup flow from callback page, allow temporary sign-in
            // The callback page will handle OTP and account creation
            if (isSignupFlow) {
              logger.info(
                "Google OAuth signup flow detected - allowing temporary session",
                {
                  email: user.email,
                  callbackUrl: callbackUrlCookie,
                },
              );
              return true; // Allow temporary session for callback page to extract data
            }

            // Check if user exists
            const existingUser = await prisma.users.findFirst({
              where: {
                OR: [
                  { email: user.email },
                  { googleId: account.providerAccountId },
                ],
              },
            });

            // ✅ CHECK ACCOUNT SUSPENSION BEFORE ALLOWING LOGIN
            if (existingUser?.isSuspended) {
              logger.warn("Suspended account login attempt", {
                email: user.email,
                userId: existingUser.id,
              });

              // Create failed login history
              await createLoginHistory({
                userId: existingUser.id,
                ipAddress,
                userAgent,
                provider: "google",
                success: false,
                failReason: "Account suspended",
              });

              return false; // Block login
            }

            let userId: string;
            let isNewUser = false;
            let isAccountLinked = false;

            if (!existingUser) {
              // 🔐 SECURITY: For login flow, do NOT auto-create users
              // Users must sign up first through the proper signup flow with OTP verification
              // This prevents unauthorized account creation via login page
              logger.warn("Google login attempted for non-existent user", {
                email: user.email,
                googleId: account.providerAccountId,
              });

              // Return false to deny login - user needs to sign up first
              // The error page will show "Access Denied" which we'll handle in the frontend
              return false;
            } else if (!existingUser.googleId) {
              // Link Google account to existing email user
              const updatedUser = await prisma.users.update({
                where: { id: existingUser.id },
                data: {
                  googleId: account.providerAccountId,
                  emailVerified: new Date(), // Ensure verified when linking Google
                  lastLoginAt: new Date(),
                },
              });
              userId = updatedUser.id;
              isAccountLinked = true;

              logger.info("Google account linked to existing user", {
                email: user.email,
                userId,
              });
            } else {
              // Existing Google user - update last login
              userId = existingUser.id;
              await prisma.users.update({
                where: { id: userId },
                data: { lastLoginAt: new Date() },
              });

              logger.info("Existing Google user logged in", {
                email: user.email,
                userId,
              });
            }

            // ✅ CREATE LOGIN HISTORY WITH GEOLOCATION
            const loginHistoryId = await createLoginHistory({
              userId,
              ipAddress,
              userAgent,
              provider: "google",
              success: true,
            });

            // ✅ CREATE USER SESSION RECORD (always issue fresh token per login)
            const sessionToken = createId();
            await createUserSession(
              userId,
              sessionToken,
              ipAddress,
              userAgent || "Unknown",
              loginHistoryId || undefined,
            );

            cookieStore.set("auth_token", sessionToken, {
              httpOnly: true,
              secure: process.env.NODE_ENV === "production",
              sameSite: "lax",
              maxAge: 7 * 24 * 60 * 60, // 7 days
              path: "/",
            });

            // 🆕 STORE GOOGLE OAUTH TOKENS FOR API ACCESS (Contacts, Calendar, etc.)
            if (account.access_token) {
              try {
                const { createId } = await import("@paralleldrive/cuid2");
                await prisma.integrations.upsert({
                  where: {
                    userId_type: {
                      userId,
                      type: "google_oauth",
                    },
                  },
                  update: {
                    accessToken: account.access_token,
                    refreshToken: account.refresh_token || undefined,
                    tokenExpiry: account.expires_at
                      ? new Date(account.expires_at * 1000)
                      : undefined,
                    scope: account.scope || undefined,
                    status: "connected",
                    lastError: null,
                    updatedAt: new Date(),
                  },
                  create: {
                    id: createId(),
                    userId,
                    type: "google_oauth",
                    accessToken: account.access_token,
                    refreshToken: account.refresh_token || undefined,
                    tokenExpiry: account.expires_at
                      ? new Date(account.expires_at * 1000)
                      : undefined,
                    scope: account.scope || undefined,
                    status: "connected",
                    updatedAt: new Date(),
                  },
                });
                logger.info("Google OAuth tokens stored", {
                  userId,
                  hasRefreshToken: !!account.refresh_token,
                });
              } catch (tokenError) {
                logger.error("Failed to store Google OAuth tokens", tokenError);
                // Don't block login on token storage failure
              }
            }

            // 🆕 LOG OAUTH EVENT TO AUDIT LOGS (Dual-Database) - wrapped in try-catch to not block login
            try {
              const auditType = isNewUser
                ? "oauth_registration"
                : isAccountLinked
                  ? "oauth_account_linked"
                  : "oauth_login";
            } catch (auditError) {
              logger.error(
                "Failed to log OAuth event to audit logs",
                auditError,
              );
              // Don't block login on audit log failure
            }
          } catch (error) {
            logger.error("Google sign-in error", error);

            // 🆕 LOG FAILED OAUTH ATTEMPT
            try {
            } catch (logError) {
              logger.error("Failed to log OAuth error", logError);
            }

            return false;
          }
        }

        // For credentials login (email/password)
        if (account?.provider === "credentials" && user.id) {
          try {
            // Check if account is suspended
            const dbUser = await prisma.users.findUnique({
              where: { id: user.id },
              select: { isSuspended: true },
            });

            if (dbUser?.isSuspended) {
              logger.warn("Suspended account login attempt", {
                userId: user.id,
              });

              // Create failed login history
              await createLoginHistory({
                userId: user.id,
                ipAddress,
                userAgent,
                provider: "credentials",
                success: false,
                failReason: "Account suspended",
              });

              return false; // Block login
            }

            // ✅ CREATE LOGIN HISTORY WITH GEOLOCATION
            const loginHistoryId = await createLoginHistory({
              userId: user.id,
              ipAddress,
              userAgent,
              provider: "credentials",
              success: true,
            });

            // ✅ CREATE USER SESSION RECORD (always issue fresh token per login)
            const sessionToken = createId();
            await createUserSession(
              user.id,
              sessionToken,
              ipAddress,
              userAgent || "Unknown",
              loginHistoryId || undefined,
            );

            cookieStore.set("auth_token", sessionToken, {
              httpOnly: true,
              secure: process.env.NODE_ENV === "production",
              sameSite: "lax",
              maxAge: 7 * 24 * 60 * 60, // 7 days
              path: "/",
            });

            logger.info("Credentials login successful", { userId: user.id });
          } catch (error) {
            logger.error("Credentials sign-in tracking error", error);
            // Don't block login on tracking error
          }
        }

        return true;
      } catch (error) {
        logger.error("SignIn callback error", error);
        return false;
      }
    },
    async jwt({ token, user, trigger }) {
      if (user) {
        token.id = user.id;
        token.name = user.name;
        token.email = user.email;
        // @ts-ignore - role exists on our custom user object
        token.role = user.role || "CUSTOMER";
        // @ts-ignore - persist Stripe customer ID when available on the user object
        token.stripeCustomerId = (user as any).stripeCustomerId ?? null;
      }

      // Always ensure we have the latest user data from the database
      // For OAuth users, user.id might be the provider ID, so we also lookup by email
      if (token.id || token.email) {
        try {
          // First try by ID (works for credentials login)
          let dbUser = await prisma.users.findUnique({
            where: { id: token.id as string },
            select: {
              id: true,
              stripeCustomerId: true,
              role: true,
              name: true,
              organizationMemberships: {
                select: {
                  role: true,
                  organizationId: true,
                },
                take: 1,
              },
            },
          });

          // If not found by ID, try by email (for OAuth users where user.id is the provider ID)
          if (!dbUser && token.email) {
            dbUser = await prisma.users.findUnique({
              where: { email: token.email as string },
              select: {
                id: true,
                stripeCustomerId: true,
                role: true,
                name: true,
                organizationMemberships: {
                  select: {
                    role: true,
                    organizationId: true,
                  },
                  take: 1,
                },
              },
            });
          }

          if (dbUser) {
            // Use the database user ID (important for OAuth users)
            token.id = dbUser.id;
            // @ts-ignore - custom token fields
            token.stripeCustomerId = dbUser.stripeCustomerId ?? null;
            // Always update name from database to ensure it's current
            token.name = dbUser.name || token.name;
            // Preserve any role set earlier but fall back to the db value
            // @ts-ignore
            token.role = token.role || dbUser.role || "CUSTOMER";

            // Add organization info if user is a member
            if (
              dbUser.organizationMemberships &&
              dbUser.organizationMemberships.length > 0
            ) {
              const membership = dbUser.organizationMemberships[0];
              // @ts-ignore - custom token fields
              token.organizationId = membership.organizationId;
              // @ts-ignore - custom token fields
              token.orgRole = membership.role;

              // Import allowed tabs based on role
              const {
                STAFF_ALLOWED_TABS,
                ADMIN_ALLOWED_TABS,
                OWNER_ALLOWED_TABS,
              } = await import("@/lib/types/organization.types");

              let allowedTabs: string[] = [];
              switch (membership.role) {
                case "OWNER":
                  allowedTabs = OWNER_ALLOWED_TABS;
                  break;
                case "ADMIN":
                  allowedTabs = ADMIN_ALLOWED_TABS;
                  break;
                case "STAFF":
                  allowedTabs = STAFF_ALLOWED_TABS;
                  break;
              }
              // @ts-ignore - custom token fields
              token.allowedTabs = allowedTabs;
            } else {
              // Clear organization fields if no membership
              // @ts-ignore
              token.organizationId = null;
              // @ts-ignore
              token.orgRole = null;
              // @ts-ignore
              token.allowedTabs = null;
            }
          }
        } catch (error) {
          logger.error("Failed to load user for JWT enrichment", {
            userId: token.id,
            email: token.email,
            error,
          });
        }
      }

      // Refresh token on activity (session extension)
      if (trigger === "update") {
        token.exp = Math.floor(Date.now() / 1000) + 2 * 60 * 60; // 2 hours
      }

      // Check if token is expired
      if (token.exp && Date.now() >= token.exp * 1000) {
        return {}; // Clear expired token
      }

      return token;
    },
    async session({ session, token }) {
      if (session.user && token.id) {
        session.user.id = token.id as string;
        // Ensure name is set from token (which gets it from database)
        session.user.name = (token.name as string) || session.user.name || null;
        // @ts-ignore - adding role to session
        session.user.role = token.role || "CUSTOMER";
        // @ts-ignore - expose Stripe customer ID to session consumers (billing APIs)
        session.user.stripeCustomerId = (token as any).stripeCustomerId || null;
        // @ts-ignore - adding organization info to session
        session.user.organizationId = (token as any).organizationId || null;
        // @ts-ignore - adding organization role to session
        session.user.orgRole = (token as any).orgRole || null;
        // @ts-ignore - adding allowed tabs to session
        session.user.allowedTabs = (token as any).allowedTabs || null;
      }
      return session;
    },
    async redirect({ url, baseUrl }) {
      // 🔒 SECURITY: Ensure redirects use the correct protocol
      // In development, always use HTTP to avoid SSL errors
      // In production, use HTTPS

      // If the URL starts with the base URL, it's internal - allow it
      if (url.startsWith(baseUrl)) {
        return url;
      }

      // If it's a relative URL, prepend the base URL
      if (url.startsWith("/")) {
        return `${baseUrl}${url}`;
      }

      // For absolute URLs, ensure we use the correct protocol for localhost
      try {
        const urlObj = new URL(url);
        const baseUrlObj = new URL(baseUrl);

        // If the host matches our base URL host, use our base URL protocol
        if (
          urlObj.host === baseUrlObj.host ||
          urlObj.hostname === "localhost"
        ) {
          urlObj.protocol = baseUrlObj.protocol;
          return urlObj.toString();
        }
      } catch {
        // Invalid URL, return base URL
        return baseUrl;
      }

      // Default: return base URL for safety
      return baseUrl;
    },
  },
  session: {
    strategy: "jwt",
    maxAge: 2 * 60 * 60, // 2 hours
    updateAge: 30 * 60, // Refresh every 30 minutes
  },
  pages: {
    signIn: "/staff/login", // Custom sign-in page for staff
  },
  trustHost: true,
  debug: process.env.NODE_ENV === "development", // Enable debug logs
  // Skip CSRF check in development (NextAuth v5 requires the symbol from @auth/core)
  // This is necessary because custom OAuth flows may not have proper CSRF token handling
  ...(process.env.NODE_ENV === "development" ? { skipCSRFCheck } : {}),
};

export const { handlers, auth, signIn, signOut } = NextAuth(authConfig);

// Backwards compatibility alias for getServerSession usage
export const authOptions = authConfig;

/**
 * Register a new user with email/password
 * Uses Prisma database for persistent storage
 */
export async function registerUser(
  email: string,
  password: string,
  name: string,
  selectedPackage?: string,
): Promise<{ success: boolean; error?: string; userId?: string }> {
  try {
    // Check if user already exists
    const existingUser = await prisma.users.findUnique({
      where: { email },
    });

    if (existingUser) {
      return { success: false, error: "User already exists" };
    }

    // Hash password with 12 rounds (secure)
    const hashedPassword = await bcrypt.hash(password, 12);

    // Create user in database
    const newUserId = createId();
    const newUser = await prisma.users.create({
      data: {
        id: newUserId,
        email,
        name,
        password: hashedPassword,
        selectedPackage: selectedPackage || null, // Store selected package
        emailVerified: new Date(), // Mark as verified after OTP confirmation
      },
    });

    return { success: true, userId: newUser.id };
  } catch (error) {
    logger.error("Registration error", error);
    return { success: false, error: "Failed to register user" };
  }
}

/**
 * Get user by email (for lookups)
 */
export async function getUserByEmail(email: string) {
  try {
    return await prisma.users.findUnique({
      where: { email },
      select: {
        id: true,
        email: true,
        name: true,
        emailVerified: true,
        createdAt: true,
        lastLoginAt: true,
      },
    });
  } catch (error) {
    logger.error("Get user error", error);
    return null;
  }
}

/**
 * Update user's last login timestamp and IP
 */
export async function updateLastLogin(userId: string, ip: string) {
  try {
    await prisma.users.update({
      where: { id: userId },
      data: {
        lastLoginAt: new Date(),
        lastLoginIp: ip,
      },
    });
  } catch (error) {
    logger.error("Update last login error", error);
  }
}

/**
 * Create login history record with IP tracking and geolocation
 */
export async function createLoginHistory(params: {
  userId: string;
  ipAddress: string;
  userAgent?: string;
  provider?: string;
  success: boolean;
  failReason?: string;
}) {
  try {
    const {
      userId,
      ipAddress,
      userAgent,
      provider = "credentials",
      success,
      failReason,
    } = params;

    // Get geolocation from IP (async, non-blocking)
    const location = await getLocationFromIP(ipAddress);

    // Create login history record
    const loginHistory = await prisma.login_history.create({
      data: {
        userId,
        ipAddress,
        userAgent: userAgent || null,
        country: location?.country || null,
        countryCode: location?.countryCode || null,
        city: location?.city || null,
        region: location?.region || null,
        latitude: location?.latitude
          ? parseFloat(String(location.latitude))
          : null,
        longitude: location?.longitude
          ? parseFloat(String(location.longitude))
          : null,
        timezone: location?.timezone || null,
        provider,
        success,
        failReason: failReason || null,
      },
    });

    // If successful login, update user's last login location
    if (success) {
      await prisma.users.update({
        where: { id: userId },
        data: {
          lastLoginAt: new Date(),
          lastLoginIp: ipAddress,
          lastLoginCountry: location?.country || null,
          lastLoginCity: location?.city || null,
          loginCount: { increment: 1 },
        },
      });
    }

    logger.info("Login history created", {
      userId,
      ipAddress,
      country: location?.country,
      city: location?.city,
      success,
    });

    return loginHistory.id;
  } catch (error) {
    // Don't fail login on history error
    logger.error("Failed to create login history", error);
    return null;
  }
}
