import { ApolloServer } from 'apollo-server';
import { PrismaClient } from '@prisma/client';
import * as fs from 'fs';  
import * as path from 'path';  
import bcrypt from 'bcryptjs';
import jwt from 'jsonwebtoken';

// Get __dirname equivalent in ES Modules
const __dirname = path.dirname(new URL(import.meta.url).pathname);

// Load schema.graphql
const typeDefs = fs.readFileSync(path.join(__dirname, 'schema.graphql'), 'utf8');

// Initialize Prisma
const prisma = new PrismaClient();

const JWT_SECRET = 'your-jwt-secret'; // You should store this in environment variables

// Generate JWT token
function generateToken(user) {
  return jwt.sign({ userId: user.id }, JWT_SECRET, { expiresIn: '1h' });
}

// GraphQL Resolvers
const resolvers = {
  Query: {
    info: () => `This is the API of a Hackernews Clone`,
    feed: async (parent, args, context) => {
      // Check if the user is authenticated
      if (!context.user) {
        throw new Error('Not authenticated');
      }

      // If authenticated, return the feed
      return context.prisma.link.findMany();
    },
  },
  Mutation: {
    signup: async (parent, args, context) => {
      const { email, password, name } = args;
      
      // Check if the user already exists
      const existingUser = await context.prisma.User.findUnique({
        where: { email },
      });
      if (existingUser) {
        throw new Error('User already exists');
      }

      // Hash the password before storing it
      const hashedPassword = await bcrypt.hash(password, 10);

      // Create a new user
      const user = await context.prisma.User.create({
        data: {
          email,
          password: hashedPassword,
          name,
        },
      });

      const token = generateToken(user);

      return {
        token,
        user,
      };
    },

    login: async (parent, args, context) => {
      const { email, password } = args;

      // Find the user by email
      const user = await context.prisma.user.findUnique({
        where: { email },
      });
      if (!user) {
        throw new Error('User not found');
      }

      // Check if the password is correct
      const isValid = await bcrypt.compare(password, user.password);
      if (!isValid) {
        throw new Error('Invalid password');
      }

      const token = generateToken(user);

      return {
        token,
        user,
      };
    },

    post: (parent, args, context) => {
      if (!context.user) {
        throw new Error('Not authenticated');
      }

      const newLink = context.prisma.link.create({
        data: {
          url: args.url,
          description: args.description,
          postedById: context.user.id,
        },
      });

      return newLink;
    },

    updatePost: async (parent, args, context) => {
      if (!context.user) {
        throw new Error('Not authenticated');
      }

      const { id, description, url } = args;

      const updatedLink = await context.prisma.link.update({
        where: { id },
        data: {
          description,
          url,
        },
      });

      return updatedLink;
    },

    deletePost: async (parent, args, context) => {
      if (!context.user) {
        throw new Error('Not authenticated');
      }

      const { id } = args;

      const link = await context.prisma.link.findUnique({
        where: { id },
      });

      if (!link) {
        throw new Error(`Link with ID ${id} not found`);
      }

      const deletedLink = await context.prisma.link.delete({
        where: { id },
      });

      return deletedLink;
    },
  },
};

// Middleware to authenticate users
const getUserFromToken = (token) => {
  try {
    return jwt.verify(token, JWT_SECRET);
  } catch (error) {
    return null;
  }
};

// Initialize Apollo Server
const server = new ApolloServer({
  typeDefs,
  resolvers,
  context: ({ req }) => {
    const token = req.headers.authorization || '';
    const user = token ? getUserFromToken(token.replace('Bearer ', '')) : null;
    return {
      prisma,
      user,
    };
  },
});

// Start the server
server.listen().then(({ url }) => {
  console.log(`🚀 Server is running at ${url}`);
});
