import { Request, Response } from "express";
import {
  userSchema,
  updateUserSchema,
  UserModel,
  generateAuthToken,
} from "../models/user";
import { ShopModel } from "../models/shop";
import bcrypt from "bcrypt";
import _ from "lodash";

/// Register a new user
const registerUser = async (req: Request, res: Response) => {
  const { error } = userSchema.validate(req.body);
  if (error) return res.status(400).json({ error: error.details[0].message });

  let user = await UserModel.findOne({ where: { email: req.body.email } });
  if (user) return res.status(400).json({ error: "Email already exists" });

  const userData: any = _.pick(req.body, [
    "name",
    "email",
    "phoneNumber",
    "password",
    "role",
    "modules",
  ]);
  userData.isDeleted = false;

  user = new UserModel(userData);
  const salt = await bcrypt.genSalt(10);
  user.password = await bcrypt.hash(user.password, salt);

  await user.save();
  res.status(201).json({
    message: "User registered successfully",
    user: _.pick(user, [
      "id",
      "name",
      "email",
      "phoneNumber",
      "role",
      "modules",
    ]),
  });
};

/// Login User
const loginUser = async (req: Request, res: Response) => {
  const { email, password } = req.body;
  
  let user = await UserModel.findOne({ 
    where: { email, isDeleted: false }
  });
  
  if (!user)
    return res.status(400).json({ error: "Invalid email or password" });
    
  const validPassword = await bcrypt.compare(password, user.password);
  if (!validPassword)
    return res.status(400).json({ error: "Invalid email or password" });

  // Get assigned shops for this user (if shopkeeper)
  let assignedShopIds: number[] = [];
  let assignedShopNames: string[] = [];

  if (user.role === "Shopkeeper") {
    const assignedShops = await ShopModel.findAll({
      where: {
        isDeleted: false
      },
      raw: true
    });

    // Filter shops where this user's ID is in the shopkeeperIds array
    const userShops = assignedShops.filter(shop => {
      const shopkeeperIds = shop.shopkeeperIds || [];
      return shopkeeperIds.includes(user.id);
    });

    assignedShopIds = userShops.map(shop => shop.id);
    assignedShopNames = userShops.map(shop => shop.name);
  }
    
  const token = generateAuthToken(user);
  
  res.status(200).json({
    token,
    user: {
      ..._.pick(user, [
        "id", 
        "name", 
        "email", 
        "phoneNumber", 
        "role", 
        "modules",
        "isDeleted",
        "createdAt",
        "updatedAt"
      ]),
      assignedShopIds,
      assignedShopNames
    }
  });
};


/// Create a new user (Admin only)
const createUser = async (req: Request, res: Response) => {
  try {
    const { error } = userSchema.validate(req.body);
    if (error) return res.status(400).json({ error: error.details[0].message });

    let user = await UserModel.findOne({ where: { email: req.body.email } });
    if (user) return res.status(400).json({ error: "Email already exists" });

    const userData: any = _.pick(req.body, [
      "name",
      "email",
      "phoneNumber",
      "password",
      "role",
      "modules",
    ]);
    userData.isDeleted = false;

    user = new UserModel(userData);
    const salt = await bcrypt.genSalt(10);
    user.password = await bcrypt.hash(user.password, salt);

    await user.save();
    res.status(201).json({
      message: "User created successfully",
      user: _.pick(user, [
        "id",
        "name",
        "email",
        "phoneNumber",
        "role",
        "modules",
      ]),
    });
  } catch (err) {
    res.status(500).json({ error: "Internal server error" });
  }
};

/// Get all users
const getAllUsers = async (req: Request, res: Response) => {
  try {
    const users = await UserModel.findAll({
      where: { isDeleted: false },
      attributes: { exclude: ["password"] },
      raw: true, // important
    });

    const updatedUsers = users.map((user: any) => ({
      ...user,
      modules: user.modules
        ? JSON.parse(user.modules).map((m: string) =>
            m.endsWith("s") ? m.slice(0, -1) : m
          )
        : [],
    }));

    res.status(200).json(updatedUsers);
  } catch (err) {
    res.status(500).json({ error: "Internal server error" });
  }
};

/// Get user by ID
const getUserById = async (req: Request, res: Response) => {
  try {
    const user = await UserModel.findByPk(req.params.id);
    if (!user || user.isDeleted) {
      return res.status(404).json({ error: "User not found" });
    }

    res
      .status(200)
      .json(
        _.pick(user, [
          "id",
          "name",
          "email",
          "phoneNumber",
          "role",
          "modules",
          "createdAt",
          "updatedAt",
        ])
      );
  } catch (err) {
    res.status(500).json({ error: "Internal server error" });
  }
};

/// Update user
const updateUser = async (req: Request, res: Response) => {
  try {
    const { error } = updateUserSchema.validate(req.body);
    if (error) return res.status(400).json({ error: error.details[0].message });

    const user = await UserModel.findByPk(req.params.id);
    if (!user || user.isDeleted) {
      return res.status(404).json({ error: "User not found" });
    }

    // Check if email is being updated and if it already exists
    if (req.body.email && req.body.email !== user.email) {
      const existingUser = await UserModel.findOne({
        where: { email: req.body.email },
      });
      if (existingUser) {
        return res.status(400).json({ error: "Email already exists" });
      }
    }

    const updatedData: any = _.pick(req.body, [
      "name",
      "email",
      "phoneNumber",
      "role",
      "modules",
    ]);

    // If password is provided, hash it
    if (req.body.password) {
      const salt = await bcrypt.genSalt(10);
      updatedData.password = await bcrypt.hash(req.body.password, salt);
    }

    await user.update(updatedData);
    await user.save();

    res.status(200).json({
      message: "User updated successfully",
      user: _.pick(user, [
        "id",
        "name",
        "email",
        "phoneNumber",
        "role",
        "modules",
        "createdAt",
        "updatedAt",
      ]),
    });
  } catch (err) {
    res.status(500).json({ error: "Internal server error" });
  }
};

/// Delete user (soft delete)
const deleteUser = async (req: Request, res: Response) => {
  try {
    const user = await UserModel.findByPk(req.params.id);
    if (!user || user.isDeleted) {
      return res.status(404).json({ error: "User not found" });
    }

    user.isDeleted = true;
    await user.save();

    res.status(200).json({ message: "User deleted successfully" });
  } catch (err) {
    res.status(500).json({ error: "Internal server error" });
  }
};

export {
  registerUser,
  loginUser,
  createUser,
  getAllUsers,
  getUserById,
  updateUser,
  deleteUser,
};
