import { NextAuthOptions } from "next-auth";
import GoogleProvider from "next-auth/providers/google";
import AzureADB2CProvider from "next-auth/providers/azure-ad-b2c";
import EmailProvider from "next-auth/providers/email";
import DrizzleEmailAdapter from "./DrizzleEmailAdapter";
import { MagicLinksEmail } from "@/app/email_templates/magic_link";

import { db } from "@/app/db/db";
import {
  user as users,
  accounts,
  verificationTokens,
} from "@/app/db/schema/users";
import { eq, and } from "drizzle-orm";

// ------------------------------------------------------------------
// Environment variables
// ------------------------------------------------------------------
const {
  GOOGLE_CLIENT_ID = "",
  GOOGLE_CLIENT_SECRET = "",
  AZURE_AD_B2C_TENANT_NAME = "",
  AZURE_AD_B2C_CLIENT_ID = "",
  AZURE_AD_B2C_CLIENT_SECRET = "",
  AZURE_AD_B2C_PRIMARY_USER_FLOW = "",
  EMAIL_SERVER_HOST = "",
  EMAIL_SERVER_PORT = "",
  EMAIL_SERVER_USER = "",
  EMAIL_SERVER_PASSWORD = "",
  EMAIL_FROM = "",
} = process.env;

type OauthProvider = "google" | "azure-ad-b2c";

// ------------------------------------------------------------------
// NextAuth configuration
// ------------------------------------------------------------------
export const AuthOptions: NextAuthOptions = {
  adapter: DrizzleEmailAdapter(db, {
    user: users,
    accounts,
    verificationTokens,
  }),

  providers: [
    // ------------------ Google ------------------
    GoogleProvider({
      clientId: GOOGLE_CLIENT_ID,
      clientSecret: GOOGLE_CLIENT_SECRET,
      authorization: {
        params: {
          access_type: "offline",
          prompt: "consent",
          response_type: "code",
        },
      },
    }),

    // ------------------ Azure AD B2C ------------------
    AzureADB2CProvider({
      tenantId: AZURE_AD_B2C_TENANT_NAME,
      clientId: AZURE_AD_B2C_CLIENT_ID,
      clientSecret: AZURE_AD_B2C_CLIENT_SECRET,
      primaryUserFlow: AZURE_AD_B2C_PRIMARY_USER_FLOW,
      authorization: {
        params: {
          scope: "openid profile offline_access",
          prompt: "login",
        },
      },
    }),

    // ------------------ Email (SES Magic Link) ------------------
    EmailProvider({
      server: {
        host: EMAIL_SERVER_HOST,
        port: Number(EMAIL_SERVER_PORT),
        auth: {
          user: EMAIL_SERVER_USER,
          pass: EMAIL_SERVER_PASSWORD,
        },
      },
      from: EMAIL_FROM, // noreply email
      maxAge: 60 * 60, // magic link valid for 1 hour
      // Slightly extended magic link email sender, to log sends
      sendVerificationRequest: async ({ identifier, url, provider }) => {
        console.log("EMAIL MAGIC LINK SENT:", {
          email: identifier,
          url,
          timestamp: new Date().toISOString(),
        });

        await MagicLinksEmail({ identifier, url, provider });
      },
    }),
  ],

  // ------------------------------------------------------------------
  // Pages
  // ------------------------------------------------------------------
  pages: {
    signIn: "/", // your landing/login page
    verifyRequest: "/auth/verify-request",
    error: "/api/auth/error",
  },

  // ------------------------------------------------------------------
  // Callbacks
  // ------------------------------------------------------------------
  callbacks: {
    /**
     * Sign in callback — ensures user exists and links OAuth provider if needed
     */
    async signIn({ user, account, profile }) {
      try {
        if (!user?.email) return false;
        const normalisedEmail = user.email.toLowerCase();

        console.log("SignIn attempt:", {
          email: normalisedEmail,
          provider: account?.provider,
          type: account?.type,
          timestamp: new Date().toISOString(),
        });

        // Fetch the user (NextAuth will have created them already if new)
        const [dbUser] = await db
          .select()
          .from(users)
          .where(eq(users.email, normalisedEmail));

        // New user - next auth will handle
        if (!dbUser) {
          console.log("New user created through sign-in:", {
            email: normalisedEmail,
            provider: account?.provider,
          });
          return true;
        }

        // Auto-link provider if same verified email but account not linked yet
        if (account?.provider && account.type === "oauth") {
          const existingLink = await db
            .select()
            .from(accounts)
            .where(
              and(
                eq(accounts.userId, dbUser.id),
                eq(accounts.provider, account.provider),
              ),
            );

          const emailVerified =
            (profile as any)?.email_verified ?? account.provider === "google";

          if (existingLink.length === 0 && emailVerified) {
            // This handles the case where we had not set up accounts but
            // signed up users with oauth
            console.log(
              `Linking ${account.provider} account for user ${normalisedEmail}`,
            );

            await db
              .insert(accounts)
              .values({
                userId: dbUser.id,
                type: account.type,
                provider: account.provider,
                providerAccountId: account.providerAccountId,
                access_token: account.access_token,
                id_token: account.id_token,
                refresh_token: account.refresh_token,
                expires_at: account.expires_at,
              })
              .onConflictDoNothing();
          }
        }

        // Link OAuth ID if missing (helps for older accounts)
        if (account && !dbUser.oauthId) {
          console.log("Backfilling OAuth ID:", {
            email: normalisedEmail,
            provider: account.provider,
          });
          const provider = account.provider as OauthProvider;
          await db
            .update(users)
            .set({ oauthId: user.id, oauthProvider: provider })
            .where(eq(users.email, normalisedEmail));
        }

        // Always update last login timestamp
        await db
          .update(users)
          .set({ lastLogin: new Date() })
          .where(eq(users.id, dbUser.id));

        // Pass bigint ID into NextAuth session/jwt
        user.dbId = dbUser.id.toString();
        user.onboarded = dbUser.onboarded ?? false;
        return true;
      } catch (error) {
        console.error("Error during sign-in:", error);
        return false;
      }
    },

    /**
     * Persist dbId in the JWT so it’s available in sessions
     */
    async jwt({ token, user: userFromLogin }) {
      // Initial sign-in: attach DB fields
      if (userFromLogin) {
        console.log("JWT initial login:", {
          email: userFromLogin.email,
          timestamp: new Date().toISOString(),
        });
        const existing = await db.query.user.findFirst({
          where: eq(users.email, userFromLogin.email!),
        });
        if (existing) {
          token.onboarded = existing.onboarded;
        }
      } else if (token.email) {
        // On subsequent calls, keep token synced from DB
        const existing = await db.query.user.findFirst({
          where: eq(users.email, token.email),
        });
        if (existing) {
          token.onboarded = existing.onboarded;
        }
      }

      if (userFromLogin?.dbId) {
        token.dbId = userFromLogin.dbId;
      }
      return token;
    },

    /**
     * Attach dbId to session.user
     */
    async session({ session, token }) {
      if (session.user && token.dbId) {
        session.user.dbId = token.dbId;
      }
      return session;
    },

    /**
     * Redirect users after login
     */
    async redirect({ url, baseUrl }) {
      // If the user has not onboarded, send them to onboarding
      // This logging is too noisy
      // console.log("Redirect triggered:", {
      //   from: url,
      //   to: `${baseUrl}/home`,
      //   timestamp: new Date().toISOString(),
      // });
      return `${baseUrl}/home`;
    },
  },

  // ------------------------------------------------------------------
  // General options
  // ------------------------------------------------------------------
  session: {
    strategy: "jwt", // lightweight, avoids DB session writes
  },
  jwt: {
    maxAge: 60 * 60 * 24 * 30, // 30 days
  },
  debug: false,
};
