const userController = require("./controllers/userController");
const { User,Counter, generateBookingId,resetCounter,generateCustomerId,ProfilePicture} = require('./models/User')

//const tanksController = require("./controllers/tanksController");
const tankersController = require("./controllers/tankersController.js");
const createConnectionController = require("./controllers/createConnectionController");
const storeController = require("./controllers/storeController.js")
const boom = require("boom");
const bcrypt = require('bcrypt');
const {  ProfilePictureStore,generateinstallationId,Store} = require("./models/store");
const cors = require('fastify-cors');


//const cors = require("cors");
const swagger = require("./config/swagger");
const rawBody = require('raw-body')
const uuidv4 = require("uuid").v4;
const fastify = require("fastify")({
  logger: true,
  //disableRequestLogging: true,
  genReqId(req) {
    // you get access to the req here if you need it - must be a synchronous function
    return uuidv4();
  },
});

// const Fastify = require("fastify");

fastify.register(cors, {
  origin: 'http://localhost:3001', // Allow only your frontend URL
  methods: ['GET', 'POST', 'PUT', 'DELETE'], // Allowed HTTP methods
});


// const Fastify = require("fastify");
// const server = Fastify({
//   logger: true,
//   // ajv: { plugins: [ajvPlugin] },
//   genReqId(req) {
//     // you get access to the req here if you need it - must be a synchronous function
//     return uuidv4();
//   },
// });

// fastify.register(View).ready((err) => {
//   if (err) console.error(err);

//   console.log(fastify.config.PORT); // or fastify[options.confKey]
//   // output: { PORT: 3000 }
//   engine: {
//     ejs: require('ejs'),
//   },
//   root: join(__dirname, 'views/html'),
// });


const now = () => Date.now();

const fastifyEnv = require("fastify-env");

const schema = {
  type: "object",
  required: ["PORT"],
  properties: {
    PORT: {
      type: "string",
      default: 3000,
    },
    APIVERSION: {
      type: "string",
      default: "1.0.0",
    },
  },
};

const options = {
  confKey: "config", // optional, default: 'config'
  schema: schema,
  // data: data // optional, default: process.env
};
fastify.register(fastifyEnv, options).ready((err) => {
  if (err) console.error(err);

  console.log(fastify.config.PORT); // or fastify[options.confKey]
  // output: { PORT: 3000 }
  fastify.decorate("conf", {
    port: fastify.config.PORT,
    APIVERSION: fastify.config.APIVERSION,
  });
});



const apiversion = "1.0.0";
const path = require("path");

// Using static content for swagger documentation. Generated swagger UI is not user friendly.

fastify.register(require("fastify-static"), {
  root: path.join(__dirname, "api-docs"),
  prefix: "/api-docs", // optional: default '/'
});



fastify.register(require("fastify-swagger"), swagger.options);

const customJwtAuth = require("./customAuthJwt");
fastify.register(customJwtAuth);
//login route - accept user credentials and send a token with role . "user" role is required to use the app.

// support login using application/x-www-form-urlencoded so users can login via a web form in addition to api

// fastify.register(require("fastify-formbody"));
// fastify.register(require('fastify-multipart'))
// fastify.register(require("fastify-cors"), {
//   // put your options here
//   origin: [
//     new RegExp("http://localhost"),
//     new RegExp("http://simply-backoffice.true2air.com"),
//     new RegExp("http://localhost:3000"),
//   ],
//   credentials: true,
//   optionsSuccessStatus: 200,
// });

fastify.register((fastify, opts, done) => {
  fastify.addContentTypeParser(
    "application/json",
    { parseAs: "buffer" },
    function (_req, body, done) {
      try {
        done(null, body)
      } catch (error) {
        error.statusCode = 400
        done(error, undefined)
      }
    }
  )
  
  done(null)
})




fastify.register(require('point-of-view'), {
  engine: {
    nunjucks: require('nunjucks')
  },
  root: path.join(__dirname, "views"),
  includeViewExtension: true,

});

// * This is for login user as a simply user *

fastify.post("/api/login", {
  schema: {
    description: "This is for Login User",
    tags: ["Login"],
    summary: "This is for User Login",
    body: {
      type: "object",
      required: ["phone", "password"],
      properties: {
        phone: { type: "string" },
        password: { type: "string" },
        // fcmId: { type: "string" }, // Add this line
        fcmIds: {
          type: "array", // Change this to allow an array
          items: { type: "string" }, // Each item in the array is a string
          default: [], // Default value if not provided
        },
        deviceId: { type: "string" } // Add this line
      },
    },
  },
  async handler(req, reply) {
    // Pass fcmId and deviceId to the loginUser function
    const { phone, password, fcmIds, deviceId } = req.body;
    console.log(password,phone)
    const loginObject = await userController.loginUser(req, fcmIds, deviceId);

    if (loginObject.same) {
      console.log("entered 1st loop")
      const phoneVerified = loginObject.user.phoneVerified;
      const oneTimePasswordSetFlag = loginObject.user.oneTimePasswordSetFlag;
      console.log(
        "oneTimePasswordSetFlag is ......",
        oneTimePasswordSetFlag,
        typeof oneTimePasswordSetFlag,
        typeof phoneVerified
      );
      if (fcmIds && fcmIds.length > 0) {
        await User.updateOne(
          { customerId:  loginObject.user.customerId },
          { $addToSet: { fcmIds: { $each: fcmIds } } } // Add multiple FCM IDs, avoiding duplicates
        );
      }
      if (!phoneVerified) {
        reply.send({
          simplydata: {
            error: false,
            phoneVerified: false,
            phone: loginObject.user.phone,
            oneTimePasswordSetFlag: oneTimePasswordSetFlag,
            message: "Please Verify your phone number",
          },
        });
      } else if (oneTimePasswordSetFlag) {
        reply.send({
          simplydata: {
            error: false,
            phoneVerified: phoneVerified,
            phone: loginObject.user.phone,
            oneTimePasswordSetFlag: true,
            message: "Password must be reset",
          },
        });
      } else {
        const token = fastify.jwt.sign(
          {
            username: loginObject.user.username,
            userId: loginObject.user._id,
            roles: loginObject.user.profile.role,
          },
          { expiresIn: "30d" }
        );
        const arr = loginObject.user.profile.role;
        const arrayToString = JSON.stringify(Object.assign({}, arr)); // convert array to string
        const stringToJsonObject = JSON.parse(arrayToString); // convert string to json object
        const c_id = loginObject.user.customerId;
        const profilePicture = await ProfilePicture.findOne({ customerId: c_id });
        
        if (!profilePicture) {
          reply.send({
            simplydata: {
              error: false,
              apiversion: fastify.config.APIVERSION,
              access_token: token,
              buildingName: loginObject.user.buildingName,
              email: loginObject.user.emails,
              phone: loginObject.user.phone,
              customerId: loginObject.user.customerId,
              username: loginObject.user.username,
              address1: loginObject.user.profile.address1,
              address2: loginObject.user.profile.address2,
              phoneVerified: loginObject.user.phoneVerified,
              oneTimePasswordSetFlag: loginObject.user.oneTimePasswordSetFlag,
              latitude: loginObject.user.latitude,
              longitude: loginObject.user.longitude,
              type: loginObject.user.profile.role,
              typeasobj: stringToJsonObject,
            },
          });
        } else {
          reply.send({
            simplydata: {
              error: false,
              apiversion: fastify.config.APIVERSION,
              access_token: token,
              picture: profilePicture.picture,
              email: loginObject.user.emails,
              phone: loginObject.user.phone,
              buildingName: loginObject.user.buildingName,
              customerId: loginObject.user.customerId,
              username: loginObject.user.username,
              address1: loginObject.user.profile.address1,
              address2: loginObject.user.profile.address2,
              phoneVerified: loginObject.user.phoneVerified,
              oneTimePasswordSetFlag: loginObject.user.oneTimePasswordSetFlag,
              latitude: loginObject.user.latitude,
              longitude: loginObject.user.longitude,
              type: loginObject.user.profile.role,
              typeasobj: stringToJsonObject,
            },
          });
        }
      }
    } else {
      reply.send({
        simplydata: {
          error: true,
          code: 400,
          message: "Invalid UserId or Password supplied",
        },
      });
    }
  },
});


fastify.post("/api/installotplogin", {
  schema: {
    description: "This is for Login Otp Installation",
    tags: ["Install"],
    summary: "This is for Login Otp Installation",
    body: {
      type: "object",
      required: ["phone", "phoneVerificationCode"],
      properties: {
        phoneVerificationCode: { type: "string" },
        phone: { type: "string" },
      },
    },
  },
  async handler(req, reply) {
    try {
      const phone = req.body.phone;
      const phoneVerificationCode = req.body.phoneVerificationCode;

      const installationExists = await Install.findOne({
        phone: phone,
        phoneVerificationCode: phoneVerificationCode,
      });

      if (installationExists) {
        const filter = {
          phone: phone,
          phoneVerificationCode: phoneVerificationCode,
        };
        const update = { phoneVerified: true };
        await Install.findOneAndUpdate(filter, update);

        const loginObject = await userController.loginUserWithOTP(req);

        if (loginObject.same) {
          if (loginObject.user) {
            const { user } = loginObject;
            const phoneVerified = user.phoneVerified;
            const oneTimePasswordSetFlag = user.oneTimePasswordSetFlag;

            if (!phoneVerified) {
              reply.send({
                simplydata: {
                  error: false,
                  phoneVerified: false,
                  phone: user.phone,
                  oneTimePasswordSetFlag: oneTimePasswordSetFlag,
                  message: "Please Verify your phone number",
                },
              });
            } else if (oneTimePasswordSetFlag) {
              reply.send({
                simplydata: {
                  error: false,
                  phoneVerified: phoneVerified,
                  phone: user.phone,
                  oneTimePasswordSetFlag: true,
                  message: "Password must be reset",
                },
              });
            } else {
              const token = fastify.jwt.sign(
                {
                  firstName: user.firstName,
                },
                { expiresIn: "30d" }
              );

              const profilePicture = await ProfilePictureInstall.findOne({
                customerId: user._id,
              });
console.log(user)
              const responsePayload = {
                simplydata: {
                  error: false,
                  apiversion: fastify.config.APIVERSION,
                  access_token: token,
                  email: user.emails,
                  installationId: user.installationId,
                  phone: user.phone,
                  //name: user.name,
                  address1: user.address1,
                  address2: user.address2,
                  phoneVerified: user.phoneVerified,
                  oneTimePasswordSetFlag: user.oneTimePasswordSetFlag,
                  type: user.profile.role,
                  fcmIds: user.fcmIds,
                  team: user.team,
                  city: user.city,
                  manager: user.manager,
                  firstName: user.firstName,
                  lastName: user.lastName,
                  address: user.address,
                  alternativeNumber: user.alternativeNumber,
                },
              };

              if (profilePicture) {
                responsePayload.simplydata.picture = profilePicture.picture;
              }

              reply.send(responsePayload);
            }
          } else {
            reply.send({
              simplydata: {
                error: true,
                code: 400,
                message: "Invalid Details",
              },
            });
          }
        } else {
          reply.send({
            simplydata: {
              error: true,
              code: 400,
              message: "Invalid phone or phoneVerificationCode supplied",
            },
          });
        }
      } else {
        reply.send({
          armintatankdata: {
            error: true,
            code: 10005,
            message: "10005 - Verification code entered cannot be validated.",
          },
        });
      }
    } catch (err) {
      throw boom.boomify(err);
    }
  },
});


fastify.post("/api/storelogin", {
  schema: {
    description: "This is for Store Login",
    tags: ["Store-Data"],
    summary: "This is for  Store Login",
    body: {
      type: "object",
      required: ["phone", "password"],
      properties: {
        phone: { type: "string" },
        password: { type: "string" },
      },
    },
  },
  async handler(request, reply) {
    try {
      let store = await Store.findOne({ phone: request.body.phone });
      if (!store) {
        return reply.code(400).send({
          simplydata: {
            error: true,
            code: 400,
            message: "Invalid Phone or Password",
          },
        });
      }
  
      const isMatch = await bcrypt.compare(request.body.password, store.services.password.bcrypt);
      if (!isMatch) {
        return reply.code(400).send({
          simplydata: {
            error: true,
            code: 400,
            message: "Invalid Phone or Password",
          },
        });
      }
  
     

      const token = fastify.jwt.sign(
        {
          storename: store.storename,
          storeId: store._id,
          roles: store.profile.role,
        },
        { expiresIn: "30d" }
      );

  
      var profilePicture = await ProfilePictureStore.findOne({ storeId: store.storeId });
  
      if (!profilePicture) {
        reply.send({
          simplydata: {
            error: false,
            apiversion: fastify.config.APIVERSION,
            access_token: token,
            email: store.emails,
            phone: store.phone,
            storeId: store.storeId,
            storename: store.storename,
            office_address: store.profile.office_address,
            phoneVerified: store.phoneVerified,
            oneTimePasswordSetFlag: store.oneTimePasswordSetFlag,
            latitude: store.latitude,
            longitude: store.longitude,
            description: store.description,
            type: store.profile.role,
            typeasobj: JSON.stringify(Object.assign({}, store.profile.role)),
          },
        });
      } else {
        reply.send({
          simplydata: {
            error: false,
            apiversion: fastify.config.APIVERSION,
            access_token: token,
            picture: profilePicture.picture,
            email: store.emails,
            phone: store.phone,
            storeId: store.storeId,
            storename: store.storename,
            office_address: store.profile.office_address,
            phoneVerified: store.phoneVerified,
            oneTimePasswordSetFlag: store.oneTimePasswordSetFlag,
            latitude: store.latitude,
            longitude: store.longitude,
            description: store.description,
            type: store.profile.role,
            typeasobj: JSON.stringify(Object.assign({}, store.profile.role)),
          },
        });
      }
    }  catch (err) {
      throw boom.boomify(err);
    }
  },
});

fastify.get("/api/reset_token/:customerId", {
  
  schema: {
    description: "This is for Reset Token",
    tags: ["Login"],
    summary: "This is for Reset Token",
    params: {
      type: "object",
      properties: {
        customerId: {
          type: "string",
          description: "customerId",
        },
      },
    },
  },

  async handler(req, reply) {
    try {
      const customerId = req.params.customerId
      const get_user = await userController.getSingleUser(req);
      const token = fastify.jwt.sign(
        {
          customerId: get_user.customerId,
          userId: get_user._id,
          roles: get_user.profile.role,
        },
        { expiresIn: "30d" }
      );
      
      reply.send({ access_token: token, customerId: get_user.customerId });
    } catch (err) {
      console.log(err);
      error = {
        simplydata: {
          error: true,
          code: 400,
          message: "Reset Token failed",
        },
      };
      reply.status(401).send(error);
    }
  },
});



fastify.get('/testtemp', (req, reply) => {
  reply.view('layouts/main', {});
});

// const multipart = require('fastify-multipart');

// fastify.register(multipart);
//fastify-auth plugin is required so we can define routes in seperate files and verify jwt supplied in preHandlers for each request.
//const multer = require("fastify-multer");
fastify.register(require("fastify-auth"));
const dbConnection = require("./config/config");
fastify.register(dbConnection);
//fastify.register(multer.contentParser);
const { Schema } = require("mongoose");
// fastify.register(dbConnection);

fastify.register(require("./routes/usersRoute"));

fastify.register(require("./routes/tanksRoute"));
fastify.register(require("./routes/createConnectionsRoute"));
fastify.register(require("./routes/tankersRoute.js"));
fastify.register(require("./routes/supplierRoute"));
fastify.register(require("./routes/supplierOrdersRoutes"));
fastify.register(require("./routes/friendRequestRoute"));
fastify.register(require("./routes/adminRoute"));
fastify.register(require("./routes/storeRoute"));
fastify.register(require("./routes/departmentRoute.js"));


// Testing route allows for retrieving a user by phone so one can see what is the phone verification code sent for a given user's phone
// Also allows deletion of a user with a given phone number
fastify.register(require("./routes/forTestingRoute"));


 const fs = require('fs');


const {Storage} = require('@google-cloud/storage');
const { Supplier, profilePictureSupplier } = require("./models/supplier");
const multer = require('fastify-multer');
const { ProfilePictureInstall, Install } = require("./models/store.js");
fastify.register(require('fastify-formbody'));
// fastify.register(multer.contentParser);
// const multipart = require('fastify-multipart');

// fastify.register(multipart);

const gc = new Storage({
  keyFilename : path.join(__dirname, "../src/arminta-tank-keyFile.json"),
  projectId : 'arminta-tank'
})

const storage = new Storage({
  keyFilename : path.join(__dirname, "../src/arminta-tank-keyFile.json"),
  projectId : 'arminta-tank'
});
// console.log(storage)
// const cloudinary = require('cloudinary').v2;
// const FormData = require('form-data');
// const mv = require('mv');

// cloudinary.config({ 
//   cloud_name: 'dalqpseol', 
//   api_key: '121595628244491', 
//   api_secret: 'jnuny_0fMYovQS0eyvIVXQTl4RY' 
// });

// Register fastify-file-upload plugin
fastify.register(require('fastify-multipart'));



// fastify.post('/upload', async (request, reply) => {
//   try {
//     const data = await request.file();

//     // Generate a unique file name
//     const fileName = `${data.filename}`;

//     const filePath = `${fileName}`;

//     // Move the file to a temporary location
//     const writeStream = fs.createWriteStream(filePath);
//     data.file.pipe(writeStream);

//     writeStream.on('close', () => {
//       // Upload the image to Cloudinary
//       cloudinary.uploader.upload(filePath, (error, result) => {
//         if (error) {
//           reply.code(500).send({ error: 'Failed to upload file to Cloudinary' });
//         } else {
//           // Get the public URL of the uploaded image
//           const publicUrl = result.secure_url;

//           // Remove the temporary file
//           fs.unlinkSync(filePath);

//           // Return the public URL
//           reply.send({ url: publicUrl });
//         }
//       });
//     });

//     writeStream.on('error', (err) => {
//       reply.code(500).send({ error: 'Failed to move file' });
//     });
//   } catch (err) {
//     reply.code(500).send({ error: 'An error occurred' });
//   }
// });


// fastify.post('/upload/:supplierId', async (request, reply) => {
//   try {
//     const supplierId = request.params.supplierId;
//     const data = await request.file();

//     // Generate a unique file name
//     const fileName = `${data.filename}`;

//     const filePath = `${fileName}`;

//     // Move the file to a temporary location
//     const writeStream = fs.createWriteStream(filePath);
//     data.file.pipe(writeStream);

//     writeStream.on('close', () => {
//       // Upload the image to Cloudinary
//       cloudinary.uploader.upload(filePath, (error, result) => {
//         if (error) {
//           reply.code(500).send({ error: 'Failed to upload file to Cloudinary' });
//         } else {
//           // Get the public URL of the uploaded image
//           const publicUrl = result.secure_url;

//           // Remove the temporary file
//           fs.unlinkSync(filePath);

//           // Store the URL in the database
//           // Assuming you have a database connection and a ProfilePictureSupplier model
//           profilePictureSupplier.findOneAndUpdate(
//             { supplierId },
//             { picture: publicUrl },
//             { new: true, upsert: true },
//             (error, picture) => {
//               if (error) {
//                 reply.code(500).send({ error: 'Failed to update database' });
//               } else {
//                 // Return the public URL
//                 reply.send({ picture: publicUrl });
//               }
//             }
//           );
//         }
//       });
//     });

//     writeStream.on('error', (err) => {
//       reply.code(500).send({ error: 'Failed to move file' });
//     });
//   } catch (err) {
//     reply.code(500).send({ error: 'An error occurred' });
//   }
// });



fastify.post('/api/uploads/:supplierId', async (request, reply) => {
  try {
    const supplierId = request.params.supplierId;
    const data = await request.file();

    // Generate a unique file name
    const fileName = `${data.filename}`;

    // Define the destination bucket and file path
    const bucketName = 'arminta_profile_pictures';
    const filePath = `${fileName}`;

    // Create a write stream to the destination file in the bucket
    const writeStream = storage.bucket(bucketName).file(filePath).createWriteStream();

    // Pipe the file data to the write stream
    data.file.pipe(writeStream);

    writeStream.on('finish', async () => {
      try {
        // Make the uploaded file publicly accessible
        await storage.bucket(bucketName).file(filePath).makePublic();

        const publicUrl = `https://storage.googleapis.com/${bucketName}/${filePath}`;

        profilePictureSupplier.findOneAndUpdate(
          { supplierId },
          { picture: publicUrl },
          { new: true, upsert: true },
          (error, picture) => {
            if (error) {
              reply.code(500).send({ error: 'Failed to update database' });
            } else {
              // Return the public URL
              reply.send({ picture: publicUrl });
            }
          }
        );
      } catch (error) {
        reply.code(500).send({ error: 'Failed to make file public' });
      }
    });

    writeStream.on('error', (err) => {
      reply.code(500).send({ error: 'Failed to move file' });
    });
  } catch (err) {
    reply.code(500).send({ error: 'An error occurred' });
  }
});



fastify.post('/api/uploads-user/:customerId', async (request, reply) => {
  try {
    const customerId = request.params.customerId;
    const data = await request.file();

    // Generate a unique file name
    
    const fileName = `${data.filename}`;


    // Define the destination bucket and file path
    const bucketName = 'arminta_profile_pictures';
    const filePath = `arminta_user_profiles/${fileName}`;

    // Create a write stream to the destination file in the bucket
    const writeStream = storage.bucket(bucketName).file(filePath).createWriteStream();

    // Pipe the file data to the write stream
    data.file.pipe(writeStream);

    writeStream.on('finish', async () => {
      try {
        // Make the uploaded file publicly accessible
        await storage.bucket(bucketName).file(filePath).makePublic();

        const publicUrl = `https://storage.googleapis.com/${bucketName}/${filePath}`;

        ProfilePicture.findOneAndUpdate(
          { customerId },
          { picture: publicUrl },
          { new: true, upsert: true },
          (error, picture) => {
            if (error) {
              reply.code(500).send({ error: 'Failed to update database' });
            } else {
              // Return the public URL
              reply.send({ picture: publicUrl });
            }
          }
        );
      } catch (error) {
        reply.code(500).send({ error: 'Failed to make file public' });
      }
    });


    writeStream.on('error', (err) => {
      reply.code(500).send({ error: 'Failed to move file' });
    });
  } catch (err) {
    reply.code(500).send({ error: 'An error occurred' });
  }
});

fastify.post("/api/insatllLogin", {
  schema: {
    description: "This is for Login Install",
    tags: ["Install"],
    summary: "This is for Login Install",
    body: {
      type: "object",
      required: ["phone", "password"],
      properties: {
        phone: { type: "string" },
        password: { type: "string" },
      },
    },
  },
  async handler(req, reply) {
    try {
      const { phone, password } = req.body;
  
      // Check if an install with the phone number exists
      const install = await Install.findOne({ phone });
  
      if (!install) {
        return reply.status(401).send({
          simplydata: {
            error: true,
            message: 'Invalid Phone or password'
          }
        });
      }
  
      // Compare the password entered by the user with the hashed password stored in the database
      const isPasswordValid = await bcrypt.compare(password, install.services.password.bcrypt);
  
      if (!isPasswordValid) {
        return reply.status(401).send({
          simplydata: {
            error: true,
            message: 'Invalid phone or password'
          }
        });
      }
  
      // Generate a JWT token for the authenticated install
      const token = fastify.jwt.sign({ phone: install.phone }, 'your_jwt_secret', { expiresIn: '30d' });
  
      // Fetch the profile picture if it exists
      const profilePicture = await ProfilePictureInstall.findOne({ customerId: install._id });
  
      const responsePayload = {
        simplydata: {
          error: false,
          apiversion: fastify.config.APIVERSION,
          access_token: token,
          email: install.emails,
          installationId: install.installationId,
          phone: install.phone,
          address1: install.address1,
          address2: install.address2,
          phoneVerified: install.phoneVerified,
          oneTimePasswordSetFlag: install.oneTimePasswordSetFlag,
          type: install.profile.role,
          fcmIds: install.fcmIds,
          team: install.team,
          city: install.city,
          manager: install.manager,
          firstName: install.firstName,
          lastName: install.lastName,
          address: install.address,
          alternativeNumber: install.alternativeNumber,
        }
      };
  
      if (profilePicture) {
        responsePayload.simplydata.picture = profilePicture.picture;
      }
  
      // Return the token and user details to the client
      return reply.send(responsePayload);
    } catch (err) {
      reply.status(500).send({
        simplydata: {
          error: true,
          message: err.message
        }
      });
    }
  },});

// Run the server!
const start = async () => {

  try {
    

    await fastify.listen(3000, "0.0.0.0");
    fastify.log.info(`listening on ${fastify.server.address().port}`);
    fastify.log.info(`server listening on ${fastify.config}`);
  } catch (err) {
    fastify.log.error(err);
    process.exit(1);
  }
};
start();