const express = require("express");
const bodyParser = require("body-parser");
const Sequelize = require("sequelize");
const jwt = require("jsonwebtoken");
const { Op } = Sequelize;
const { graphqlHTTP } = require("express-graphql");
const { buildSchema } = require("graphql");
const cors = require("cors");
const expressJwt = require("express-jwt");
// const mailer = require("./smtpGmail");
const app = express();

app.use(bodyParser.json());
app.use(express.static("myproject"));
// app.use(errorHandler);
app.use(bodyParser.urlencoded({ extended: false }));
app.use(cors());

const sequelize = new Sequelize("emrada2", "root", "Emmanuil2228125%", {
  timezone: "+03:00",
  host: "localhost",
  dialect: "mysql",
});

class User extends Sequelize.Model {
  get messages() {
    return (async () => {
      let autorForMessage = await this.getAutor();
      let partnerForMessage = await this.getPartner();
      return [...autorForMessage, ...partnerForMessage];
    })();
  }
  get chat_groups() {
    return (async () => {
      let autorForChatGroup = await this.getAutor();
      let partnerForChatGroup = await this.getPartner();
      return [...autorForChatGroup, ...partnerForChatGroup];
    })();
  }
}

User.init(
  {
    email: Sequelize.STRING,
    password: Sequelize.STRING,
    login: Sequelize.STRING,
  },
  { sequelize, modelName: "user" }
);

class Message extends Sequelize.Model {
  get user() {
    return this.User();
  }
}

Message.init(
  { message: Sequelize.STRING },
  { sequelize, modelName: "message" }
);

class ChatGroup extends Sequelize.Model {}

ChatGroup.init({}, { sequelize, modelName: "chat_group" });

User.hasMany(Message, { as: "autorForMessage", foreignKey: "autorId" });
User.hasMany(Message, { as: "partnerForMessage", foreignKey: "partnerId" });

Message.belongsTo(User, { as: "autor", sourceKey: "autorId" });
Message.belongsTo(User, { as: "partner", sourceKey: "partnerId" });

User.hasMany(ChatGroup, { as: "autorForChatGroup", foreignKey: "autorId" });
User.hasMany(ChatGroup, { as: "partnerForChatGroup", foreignKey: "partnerId" });

ChatGroup.belongsTo(User, { as: "autor", sourceKey: "autorId" });
ChatGroup.belongsTo(User, { as: "partner", sourceKey: "partnerId" });

const secret = `7.!BMB?Y+Bc2vZE-Hb5YuCT6QvE^FN,JWN6M?_VtFXeC5dLtB!`;
const secretPass = "JcFWhuLkpaK9aB3Gtbvo2Y0BApdw5q1tUyAyJeD8fJXs78d7zR";

const authenticate = async ({ login, password }) => {
  const user = await User.findOne({ where: { login, password } });
  if (user) {
    const token = jwt.sign(
      { sub: { id: user.id, email: user.email, login: user.login } },
      secret
    );
    return token;
  }
};

var schema = buildSchema(`
  type Query {
    getLogin(login: String, password: String): String
    getUser(email: String): User
    getAllUsers: [User]
    getMessage(id: ID!): Message
    getAllMessages: [Message]
    getAllMessagesOneUser(id: ID!): [Message]
    getAllChatGroups: [ChatGroup]
    getAllChatGroupsOneUser(id: ID!): [ChatGroup]
  }
  type Mutation {
    createUser(email: String, password: String, login: String): User
    changePassword(email: String, password: String): User
    createMessage(message: String, autorId: String, partnerId: String): Message
    changeMessage(id: ID!, message: String): Message
    deleteMessage(id: ID!): String
    createChatGroup(autorId: String, partnerId: String): Message
    deleteChatGroup(id: ID!): String
  }
  type User {
    id: Int
    createdAt: String
    email: String
    login: String
  }
  type Message {
    id: Int
    createdAt: String
    message: String
    autorId: User
    partnerId: User
  }
  type ChatGroup {
    id: Int
    createdAt: String
    autorId: User
    partnerId: User
  }
`);

const getUser = async ({ email }) => await User.findOne({ where: { email } });

const getLogin = async ({ login, password }) => {
  const userFind = await User.findOne({ where: { login, password } });
  return authenticate(userFind);
};

const createUser = async ({ email, password, login }) => {
  const wasUserCreated = await User.findOne({ where: { email } });
  if (!wasUserCreated) {
    // const passwordModification = jwt.sign(password + secretPass, secret);
    const user = { email, password, login };
    const newUser = new User(user);
    await newUser.save();
    return await User.findOne({ where: { login } });
  } else console.error("error");
};

const changePassword = async ({ email, password }) => {
  var userFind = await User.findOne({ where: { email } });
  if (userFind) {
    await User.update({ password }, { where: { email } });
    userFind.email = "The password has been change";
    return userFind;
  } else {
    userFind = { email: "Email not found" };
    return userFind;
  }
};

const getAllUsers = async () => await User.findAll();

const getAllMessages = async () => {
  const foundAllMessages = await Message.findAll();
  for (var allMessages of foundAllMessages) {
    const autor = await User.findAll({ where: { id: allMessages.autorId } });
    const partner = await User.findAll({
      where: { id: allMessages.partnerId },
    });
    allMessages.autorId = autor[0];
    allMessages.partnerId = partner[0];
  }
  return foundAllMessages;
};

const getMessage = async ({ id }) => {
  const message = await Message.findOne({ where: { id } });
  const autor = await User.findOne({ where: { id: message.autorId } });
  const partner = await User.findOne({ where: { id: message.partnerId } });
  message.autorId = autor;
  message.partnerId = partner;
  return message;
};

const getAllMessagesOneUser = async ({ id }) => {
  const allMess = await Message.findAll();
  for (var key of allMess) {
    const autor = await User.findAll({ where: { id: key.autorId } });
    const partner = await User.findAll({ where: { id: key.partnerId } });
    const ma = autor[0].id === +id || partner[0].id === +id;

    if (ma === true) {
      const y = await Message.findAll({ where: { autorId: id } });
      const u = await Message.findAll({ where: { partnerId: id } });
      console.log(y);

      if ((y.length && u.length) !== 0) {
        const p = [...y, ...u];
        for (var key of p) {
          const r = await User.findAll({ where: { id: key.autorId } });
          const r1 = await User.findAll({ where: { id: key.partnerId } });
          key.autorId = r[0];
          key.partnerId = r1[0];
        }
        return p;
      } else if (y.length !== 0) {
        const pt = [...y];
        for (var key of pt) {
          const r = await User.findAll({ where: { id: key.partnerId } });
          key.autorId = autor[0];
          key.partnerId = r[0];
        }
        return pt;
      } else {
        const ptu = [...u];
        for (var key of ptu) {
          const r = await User.findAll({ where: { id: key.autorId } });
          key.autorId = r[0];
          key.partnerId = partner[0];
        }
        return ptu;
      }
    }
  }
};

const createMessage = async (obj) => {
  try {
    return await Message.create({ ...obj });
  } catch (e) {
    console.log(e);
  }
};

const changeMessage = async ({ id, message }) => {
  const putMess = await Message.findByPk(id);
  if (putMess) {
    await Message.update({ message }, { where: { id } });
    return await Message.findByPk(id);
  } else return { id: "Message not find" };
};

const deleteMessage = async ({ id }) => {
  const messFind = await Message.findByPk(id);
  if (messFind) {
    await Message.destroy({ where: { id } });
    return "Message deleted";
  } else return "Message not find";
};

const getAllChatGroups = async () => {
  const foundAllChatGroups = await ChatGroup.findAll();
  for (var allUsers of foundAllChatGroups) {
    const autor = await User.findAll({ where: { id: allUsers.autorId } });
    const partner = await User.findAll({ where: { id: allUsers.partnerId } });
    allUsers.autorId = autor[0];
    allUsers.partnerId = partner[0];
  }
  return foundAllChatGroups;
};

const getAllChatGroupsOneUser = async ({ id }) => {
  const foundAllChatGroups = await ChatGroup.findAll();
  for (var allUsers of foundAllChatGroups) {
    const autor = await User.findAll({ where: { id: allUsers.autorId } });
    const partner = await User.findAll({ where: { id: allUsers.partnerId } });
    const ma = autor[0].id === +id || partner[0].id === +id;

    if (ma === true) {
      const y = await ChatGroup.findAll({ where: { autorId: id } });
      const u = await ChatGroup.findAll({ where: { partnerId: id } });

      if ((y.length && u.length) !== 0) {
        const p = [...y, ...u];
        for (var key of p) {
          const r = await User.findAll({ where: { id: key.autorId } });
          const r1 = await User.findAll({ where: { id: key.partnerId } });
          key.autorId = r[0];
          key.partnerId = r1[0];
        }
        return p;
      } else if (y.length !== 0) {
        const pt = [...y];
        for (var key of pt) {
          const r = await User.findAll({ where: { id: key.partnerId } });
          key.autorId = autor[0];
          key.partnerId = r[0];
        }
        return pt;
      } else {
        const ptu = [...u];
        for (var key of ptu) {
          const r = await User.findAll({ where: { id: key.autorId } });
          key.autorId = r[0];
          key.partnerId = partner[0];
        }
        return ptu;
      }
    } else console.log("bye");
  }
};

const createChatGroup = async (obj) => {
  try {
    const oneUserId = await ChatGroup.findOne({
      where: { autorId: obj.autorId, partnerId: obj.partnerId },
    });
    const twoUserId = await ChatGroup.findOne({
      where: { autorId: obj.partnerId, partnerId: obj.autorId },
    });
    if (!oneUserId && !twoUserId) {
      var newChatGroup = await ChatGroup.create({ ...obj });
      const autor = await User.findAll({ where: { id: newChatGroup.autorId } });
      const partner = await User.findAll({
        where: { id: newChatGroup.partnerId },
      });
      newChatGroup.autorId = autor[0];
      newChatGroup.partnerId = partner[0];
      return newChatGroup;
    }
  } catch (e) {
    return;
  }
};

const deleteChatGroup = async ({ id }) => {
  const messFind = await ChatGroup.findByPk(id);
  if (messFind) {
    await ChatGroup.destroy({ where: { id } });
    return "ChatGroup deleted";
  } else return "ChatGroup not find";
};

var root = {
  getUser,
  getLogin,
  createUser,
  getAllUsers,
  getMessage,
  changePassword,
  createMessage,
  changeMessage,
  deleteMessage,
  getAllMessages,
  getAllMessagesOneUser,
  getAllChatGroups,
  getAllChatGroupsOneUser,
  createChatGroup,
  deleteChatGroup,
};

app.use(
  "/graphql",
  graphqlHTTP({
    schema: schema,
    rootValue: root,
    graphiql: true,
  })
);

app.get("/users", async (req, res) => res.send(await User.findAll()));
app.get("/message", async (req, res) => res.send(await Message.findAll()));
app.get("/chat-group", async (req, res) => res.send(await ChatGroup.findAll()));

// app.post("/users", async (req, res) => {
//   // const twoUsers = async () => {
//   //   const userEmail = await User.findOne({ where: { email: req.body.email } });
//   //   if (userEmail !== null) console.log(err);
//   //   else {
//   //     console.log("hi");
//   var newUser = new User(req.body);
//   // const message = {
//   //   to: req.body.email,
//   //   subject: "Registered",
//   //   text: `Отлично. Вот ваши данные:
//   //   login: ${req.body.email}
//   //   password: ${req.body.password}

//   //   Перейдите по ссылке, чтобы войти в свой аккаунт
//   //   url: http://localhost:3335/sign_in`,
//   // };
//   // mailer(message);
//   await newUser.save();
//   res.status(201).send(newUser);
//   // }
//   // };
//   // twoUsers();
// });

// function errorHandler(err, req, res, next) {
//   if (typeof err === "string") {
//     return res.status(400).json({ message: err });
//   }

//   if (err.name === "UnauthorizedError") {
//     return res.status(401).json({ message: "Invalid Token" });
//   }

//   return res.status(500).json({ message: err.message });
// }

app.post("/users/authenticate", async (req, res, next) => {
  authenticate(req.body)
    .then((user) => {
      user
        ? res.json(user)
        : res
            .status(400)
            .json({ message: "Username or password is incorrect" });
    })
    .catch((err) => next(err));
});

// app.use(jwtWare());

app.get("/a", (req, res, next) => {
  console.log(req.headers.authorization);
  const token1 = req.headers.authorization;
  console.log(token1);
  // if (token) {
  const data = jwt.verify(token1, secret);
  console.log(data);
  // if (data) {
  console.log(data.sub.login);
  res.send(`<h1>Hello ${data.sub.login}</h1>`);
  // } else {
  //   res.send(`<h1>Hello haker</h1>`);
  // }
  // }
});

// (async () => {
//   let persone =
// //   await User.findOne({ where: { login: "B" } }) ||
//     (await User.create({ email: "ljkd@gfd.gfd", password: "1", login: "A" }))
// })()

//   createMessage({message: "Hello 1", autorId: 1, partnerId: 2})
//  createChatGroup({ autorId: 1, partnerId: 2})

sequelize.sync();

app.listen(3330, () => console.log("The server started on port 3330"));
