import { NextAuthOptions } from "next-auth";
import CredentialsProvider from "next-auth/providers/credentials";
import GoogleProvider from "next-auth/providers/google";
import { PrismaAdapter } from "@next-auth/prisma-adapter";
import { PrismaClient } from "@prisma/client";
import { query } from "@/lib/db-postgres";
import { ensureWorkspaceForUser } from "@/lib/workspaces";

const prisma = new PrismaClient();

const nextAuthUrl = (process.env.NEXTAUTH_URL ?? "").trim();
const isLocalNextAuth =
  nextAuthUrl.startsWith("http://localhost") ||
  nextAuthUrl.startsWith("http://127.0.0.1") ||
  (process.env.NODE_ENV === "development" && !nextAuthUrl);

/** Set in .env.local (server + client): one email for local dev bypass. */
const devLocalEmail = (
  process.env.NEXT_PUBLIC_DEV_LOCAL_AUTH_EMAIL ?? ""
).trim();
const devLocalSecret = (process.env.DEV_LOCAL_AUTH_SECRET ?? "").trim();
const devLocalAuthEnabled =
  process.env.NODE_ENV === "development" && devLocalEmail.length > 0;

function isLocalhostHost(host: string): boolean {
  const h = host.split(":")[0]?.toLowerCase() ?? "";
  return h === "localhost" || h === "127.0.0.1" || h === "[::1]" || h === "::1";
}

export const authOptions: NextAuthOptions = {
  /** Verbose adapter/session logs; enable with NEXTAUTH_DEBUG=true when needed */
  debug: process.env.NEXTAUTH_DEBUG === "true",
  adapter: PrismaAdapter(prisma),
  providers: [
    ...(devLocalAuthEnabled
      ? [
          CredentialsProvider({
            id: "dev-local",
            name: "Local dev",
            credentials: {
              password: { label: "Dev secret", type: "password" },
            },
            async authorize(credentials, req) {
              const headers = (req as { headers?: Headers } | undefined)
                ?.headers;
              const host =
                headers && typeof headers.get === "function"
                  ? (headers.get("host") ?? "")
                  : "";

              if (devLocalSecret) {
                if ((credentials?.password ?? "") !== devLocalSecret) {
                  return null;
                }
              } else if (!isLocalhostHost(host)) {
                return null;
              }

              const name =
                (process.env.DEV_LOCAL_AUTH_NAME ?? "").trim() || "Local dev";

              const user = await prisma.user.upsert({
                where: { email: devLocalEmail },
                create: {
                  email: devLocalEmail,
                  name,
                  emailVerified: new Date(),
                },
                update: { name },
              });

              return {
                id: user.id,
                email: user.email,
                name: user.name,
                image: user.image,
              };
            },
          }),
        ]
      : []),
    GoogleProvider({
      clientId: process.env.GOOGLE_CLIENT_ID || "",
      clientSecret: process.env.GOOGLE_CLIENT_SECRET || "",
    }),
  ],
  pages: {
    signIn: "/signin",
    error: "/signin",
  },
  callbacks: {
    async session({ session, user }) {
      if (session.user && "id" in user && user.id) {
        (session.user as { id: string }).id = user.id;
      }
      return session;
    },
    async signIn({ user }) {
      if (!user?.email) return true;
      try {
        const workspace =
          user.email
            .split("@")[0]
            .toLowerCase()
            .replace(/[^a-z0-9]+/g, "-") + "-account";
        const data = JSON.stringify({
          email: user.email,
          name: user.name,
          image: user.image,
          workspace,
        });

        // Two-step upsert avoids relying on ON CONFLICT expression matching
        const existing = await query<{ id: string }>(
          `SELECT id FROM fs_users WHERE data->>'email' = $1 LIMIT 1`,
          [user.email],
        );
        let fsUserId: string;
        if (existing.length === 0) {
          const inserted = await query<{ id: string }>(
            `INSERT INTO fs_users (id, user_id, data)
             VALUES (gen_random_uuid()::text, $1, $2::jsonb)
             RETURNING id`,
            [user.id, data],
          );
          fsUserId = inserted[0].id;
        } else {
          await query(
            `UPDATE fs_users SET user_id = $1, data = data || $2::jsonb, updated_at = NOW() WHERE id = $3`,
            [user.id, data, existing[0].id],
          );
          fsUserId = existing[0].id;
        }

        // Ensure a Vibn workspace exists for this user. We DO NOT
        // provision Coolify/Gitea here — that happens lazily on first
        // project create so signin stays fast and resilient to outages.
        try {
          await ensureWorkspaceForUser({
            userId: fsUserId,
            email: user.email,
            displayName: user.name ?? null,
          });
        } catch (wsErr) {
          console.error("[signIn] Failed to ensure workspace:", wsErr);
        }
      } catch (e) {
        console.error("[signIn] Failed to upsert fs_user:", e);
      }
      return true;
    },
  },
  session: {
    strategy: "database",
    maxAge: 30 * 24 * 60 * 60, // 30 days
  },
  secret: process.env.NEXTAUTH_SECRET,
  cookies: {
    sessionToken: {
      // __Secure- prefix requires Secure; localhost HTTP needs plain name + secure: false
      name: isLocalNextAuth
        ? "next-auth.session-token"
        : "__Secure-next-auth.session-token",
      options: {
        httpOnly: true,
        sameSite: "lax",
        path: "/",
        secure: !isLocalNextAuth,
        ...(isLocalNextAuth ? {} : { domain: ".vibnai.com" }),
      },
    },
  },
};
