import CredentialsProvider from 'next-auth/providers/credentials';
import { AuthOptions, getServerSession } from 'next-auth';
import bcrypt from 'bcryptjs';
import { PrismaAdapter } from '@auth/prisma-adapter';
import { db } from '@/lib/db';
import { DefaultSession } from 'next-auth';
import { UserRole } from '@prisma/client';

declare module 'next-auth' {
  interface User {
    id: string;
    vercelAccessToken?: string;
    role?: UserRole;
    username?: string;
  }

  interface Session {
    user: User &
      DefaultSession['user'] & {
        role: UserRole;
      };
  }
}

declare module 'next-auth/jwt' {
  interface JWT {
    user: {
      id: string;
      vercelAccessToken?: string;
      role: UserRole;
      username: string;
    };
  }
}

const authOptions: AuthOptions = {
  adapter: PrismaAdapter(db),
  secret: process.env.NEXTAUTH_SECRET,
  providers: [
    CredentialsProvider({
      name: 'credentials',
      credentials: {
        email: { label: 'Email', type: 'text', placeholder: 'you@example.com' },
        username: { label: 'Username', type: 'text', placeholder: 'username' },
        password: { label: 'Password', type: 'password' },
      },
      async authorize(credentials) {
        if (!credentials) return null;

        const { email, password, username } = credentials;

        const user = await db.user.findUnique({
          where: email ? { email } : { username },
        });

        if (!user) {
          return null;
        }

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

        if (!isValid) {
          return null;
        }

        return {
          id: user.id,
          email: user.email,
          username: user.username,
          vercelAccessToken: user.vercelAccessToken ?? undefined,
          role: user.role,
        };
      },
    }),
  ],
  session: {
    strategy: 'jwt',
  },
  pages: {
    signIn: '/login',
  },
  callbacks: {
    async jwt({ token, user, session, trigger }) {
      if (trigger === 'update' && session?.user) {
        const refetchedUser = await db.user.findUnique({
          where: {
            email: token.email ?? '',
          },
        });

        if (refetchedUser) {
          return {
            ...token,
            vercelAccessToken: refetchedUser.vercelAccessToken,
          };
        }
      }
      if (user) {
        return {
          ...token,
          vercelAccessToken: user.vercelAccessToken,
          id: user.id,
          role: user.role,
          username: user.username,
        };
      }
      return token;
    },
    async session({ session, token }) {
      if (session.user) {
        session.user.id = token.id as string;
        session.user.vercelAccessToken = token.vercelAccessToken as string;
        session.user.role = token.role as UserRole;
        session.user.username = token.username as string;
      }
      return session;
    },
  },
};

const getSession = () => getServerSession(authOptions);

export { authOptions, getSession };
