"use strict";

const {
  User,
  UserBalance,
  sequelize,
  Notification,
} = require("../../models");

const { Op } = require("sequelize");
const { ValidationError, NotFoundError, BadRequestError, ConflictError } = require("../../utils/errors");
const { generateRandomString } = require("../../utils/helpers");

function splitName(fullName) {
  const cleaned = String(fullName || "").trim().replace(/\s+/g, " ");
  if (!cleaned) return { firstName: "", lastName: "" };
  const parts = cleaned.split(" ");
  if (parts.length === 1) return { firstName: parts[0], lastName: "" };
  return { firstName: parts[0], lastName: parts.slice(1).join(" ") };
}

function normalizeRole(input) {
  // UI: Admin / Moderator / User
  // DB: user / trader / admin / superadmin
  const v = String(input || "").trim().toLowerCase();
  if (v === "admin") return "admin";
  if (v === "moderator") return "trader"; // best-fit mapping
  if (v === "superadmin") return "superadmin";
  if (v === "trader") return "trader";
  return "user";
}

function normalizeStatus(input) {
  // UI: Active / Inactive / Pending
  // DB: active / inactive / held / disabled / suspended / pending
  const v = String(input || "").trim().toLowerCase();
  if (v === "active") return "active";
  if (v === "inactive") return "inactive";
  if (v === "pending") return "pending";
  if (["held", "disabled", "suspended"].includes(v)) return v;
  return "pending";
}

class AdminUserService {
  async getUsers(query = {}) {
    const { q, status, kycStatus, page = 1, limit = 20 } = query;
    const safeLimit = Math.min(Math.max(Number(limit) || 20, 1), 100);
    const safePage = Math.max(Number(page) || 1, 1);
    const offset = (safePage - 1) * safeLimit;

    const where = {};

    if (status) where.status = normalizeStatus(status);
    if (kycStatus) where.kycStatus = kycStatus;

    if (q) {
      const needle = String(q).trim();
      if (needle) {
        where[Op.or] = [
          { id: { [Op.like]: `%${needle}%` } },
          { userId: { [Op.like]: `%${needle}%` } },
          { email: { [Op.like]: `%${needle}%` } },
          { firstName: { [Op.like]: `%${needle}%` } },
          { lastName: { [Op.like]: `%${needle}%` } },
        ];
      }
    }

    const { rows, count } = await User.findAndCountAll({
      where,
      order: [["created_at", "DESC"]],
      limit: safeLimit,
      offset,
      include: [{ model: UserBalance, as: "balance" }],
    });

    return {
      count,
      page: safePage,
      limit: safeLimit,
      users: rows.map((u) => ({
        id: u.id,
        user_id: u.userId,
        name: `${u.firstName || ""} ${u.lastName || ""}`.trim() || u.email,
        firstName: u.firstName,
        lastName: u.lastName,
        email: u.email,
        role: u.role,
        status: u.status,
        balance: u.balance?.balance ?? 0,
        bonus: u.balance?.bonus ?? 0,
        createdAt: u.createdAt,
      })),
    };
  }

  async createUser(payload = {}, adminId = null) {
    const name = String(payload.name || "").trim();
    const email = String(payload.email || "").trim().toLowerCase();
    if (!name) throw new ValidationError("name is required");
    if (!email) throw new ValidationError("email is required");

    const { firstName, lastName } = splitName(name);

    const role = normalizeRole(payload.role);
    const status = normalizeStatus(payload.status);

    // Your User model requires password + pin (NOT NULL).
    // To avoid changing the React layout, we auto-generate them if missing.
    const tempPassword = String(payload.password || "").trim() || `Pw@${generateRandomString(10)}`;
    const tempPin = String(payload.pin || "").trim() || String(Math.floor(1000 + Math.random() * 9000));

    const existing = await User.findOne({ where: { email } });
    if (existing) throw new ConflictError("Email already exists");

    return await sequelize.transaction(async (t) => {
      const user = await User.create(
        {
          firstName,
          lastName,
          email,
          role,
          status,
          password: tempPassword, // hook will hash password in model
          pin: Number(tempPin),   // stored as numeric (matches your model)
        },
        { transaction: t }
      );

      // Create balance row if your app expects it
      await UserBalance.create(
        {
          reference: generateRandomString(16),
          userId: user.id,
          balance: 0,
          profit: 0,
          bonus: 0,
        },
        { transaction: t }
      );

      return {
        user: {
          id: user.id,
          user_id: user.userId,
          name: `${user.firstName || ""} ${user.lastName || ""}`.trim() || user.email,
          firstName: user.firstName,
          lastName: user.lastName,
          email: user.email,
          role: user.role,
          status: user.status,
          createdAt: user.created_at,
        },
        tempPassword, // show admin once (frontend can alert / toast)
        tempPin,
      };
    });
  }

  async getUserById(userId) {
    const user = await User.findByPk(userId, {
      include: [{ model: UserBalance, as: "balance" }],
    });
    if (!user) throw new NotFoundError("User not found");
    return { user };
  }

  async updateUser(userId, data = {}) {
    const user = await User.findByPk(userId);
    if (!user) throw new NotFoundError("User not found");

    const patch = { ...data };

    // allow UI "name" to update first/last
    if (patch.name) {
      const { firstName, lastName } = splitName(patch.name);
      patch.firstName = firstName;
      patch.lastName = lastName;
      delete patch.name;
    }

    if (patch.role) patch.role = normalizeRole(patch.role);
    if (patch.status) patch.status = normalizeStatus(patch.status);

    // guard: don't let random fields break things
    const allowed = [
      "firstName",
      "lastName",
      "email",
      "role",
      "status",
      "country",
      "phone",
      "avatar",
      "bio",
    ];
    const clean = {};
    for (const k of allowed) if (patch[k] !== undefined) clean[k] = patch[k];

    await user.update(clean);
    return user;
  }

  async deleteUser(userId) {
    const user = await User.findByPk(userId);
    if (!user) throw new NotFoundError("User not found");

    return await sequelize.transaction(async (t) => {
      await UserBalance.destroy({ where: { userId: user.id }, transaction: t });
      await user.destroy({ transaction: t });
      return { deleted: true, userId: Number(userId) };
    });
  }

  async suspendUser(userId, data = {}) {
    const user = await User.findByPk(userId);
    if (!user) throw new NotFoundError("User not found");
    await user.update({ status: "suspended", suspendReason: data?.reason ?? null });
    return user;
  }

  async holdUser(userId, data = {}) {
    const user = await User.findByPk(userId);
    if (!user) throw new NotFoundError("User not found");
    await user.update({ status: "held", suspendReason: data?.reason ?? null });
    return user;
  }

  async disableUser(userId, data = {}) {
    const user = await User.findByPk(userId);
    if (!user) throw new NotFoundError("User not found");
    await user.update({ status: "disabled", suspendReason: data?.reason ?? null });
    return { user, data: data ?? {} };
  }

  async unsuspendUser(userId) {
    const user = await User.findByPk(userId);
    if (!user) throw new NotFoundError("User not found");
    await user.update({ status: "active", suspendReason: null });
    return user;
  }

  async updateBalance(userId, data) {
    const { operation, type, amount } = data;

    const user = await User.findByPk(userId);
    if (!user) throw new NotFoundError("User not found");

    const user_balance = await UserBalance.findOne({ where: { userId } });
    if (!user_balance) throw new NotFoundError("balance not found");

    const balance = Number(user_balance.balance);
    const bonus = Number(user_balance.bonus);
    const px = Number(amount ?? 0);
    if (!Number.isFinite(px) || px < 0) throw new BadRequestError("amount must be a valid number >= 0");

    if (type === "Balance") {
      let newBalance = balance;
      if (operation === "add") newBalance = balance + px;
      else if (operation === "subtract") newBalance = balance - px;
      else if (operation === "clear") newBalance = 0;
      else throw new BadRequestError("Invalid operation");
      if (newBalance < 0) throw new BadRequestError("Balance cannot be negative");
      await user_balance.update({ balance: newBalance });
    } else if (type === "Bonus") {
      let newBonus = bonus;
      if (operation === "add") newBonus = bonus + px;
      else if (operation === "subtract") newBonus = bonus - px;
      else if (operation === "clear") newBonus = 0;
      else throw new BadRequestError("Invalid operation");
      if (newBonus < 0) throw new BadRequestError("Bonus cannot be negative");
      await user_balance.update({ bonus: newBonus });
    } else {
      throw new BadRequestError("type must be Balance or Bonus");
    }

    return { user, balance: user_balance };
  }

  async updateBilling(userId, data) {
    const { status, title, message } = data;
    const user = await User.findByPk(userId);
    if (!user) throw new NotFoundError("User not found");
    user.update({
      billingStatus: status,
      billingTitle: status ? title : "",
      billingMessage: status ? message : ""
    })

    return { user};
  }

  async notifyUser(userId, body = {}, adminId = null) {
    console.log("notifyUser called with:", { userId, body, adminId });
    const user = await User.findByPk(userId);
    if (!user) throw new NotFoundError("User not found");
    const type = String(body.type || "system");
    const title = String(body.title || "").trim();
    const message = String(body.message || "").trim();

    if (!title) throw new ValidationError("title is required");
    if (!message) throw new ValidationError("message is required");

    const priority = String(body.priority || "medium");
    const channels = body.channels && typeof body.channels === "object"
      ? body.channels
      : { inApp: true, email: false, push: false, sms: false };

    const payload = {
      reference: generateRandomString(16),
      userId: user.id,
      type,
      title,
      message,
      icon: body.icon || null,
      color: body.color || null,
      link: body.link || null,
      priority,
      channels,
      relatedId: body.relatedId || null,
      relatedType: body.relatedType || null,
      metadata: {
        ...(body.metadata || {}),
        sentBy: "admin",
        adminId: adminId || null,
      },
    };

    const notification = await Notification.create(payload);
    return { user, notification };
  }
  
}

module.exports = new AdminUserService();