const { Router } = require("express");
const prisma = require("../prisma");
const { requireAuth, requireRoles } = require("../middleware/auth");
const { requireAdminPerm } = require("../middleware/perms");
const {
  isAdminOnly,
  assertCanAccessUser,
  SUPER_ADMIN_ROLE,
} = require("../lib/rbac");

const router = Router();

function normalizeOptionalString(input) {
  if (input === undefined) return undefined;
  if (input === null || input === "") return null;
  const s = String(input).trim();
  return s || null;
}

function prismaConflict(err, res) {
  if (err.code === "P2002") {
    return res.status(409).json({ error: "Branch name or code already exists" });
  }
  return null;
}

const OPTIONAL_FIELDS = ["code", "address", "email", "phone", "country", "city"];

function parsePhonesInput(input) {
  if (input === undefined) return undefined;
  if (input === null) return null;
  if (!Array.isArray(input)) return null;
  const arr = input.map((s) => String(s).trim()).filter(Boolean);
  return arr.length ? arr : null;
}

function buildOptionalData(body) {
  const data = {};
  for (const k of OPTIONAL_FIELDS) {
    if (body[k] !== undefined) data[k] = normalizeOptionalString(body[k]);
  }
  if (body.phones !== undefined) {
    const phones = parsePhonesInput(body.phones);
    data.phones = phones;
    if (body.phone === undefined) {
      data.phone = phones?.[0] ?? null;
    }
  }
  return data;
}

router.get("/", requireAuth, requireAdminPerm, async (req, res, next) => {
  try {
    const page = Math.max(1, parseInt(req.query.page) || 1);
    const limit = Math.min(100, Math.max(1, parseInt(req.query.limit) || 25));
    const skip = (page - 1) * limit;
    const search = (req.query.search || "").trim();

    const userRoles = req.user?.roles || [];
    const isAdmin =
      userRoles.includes("Admin") || userRoles.includes("Super Admin");

    if (isAdmin) {
      const where = {};
      if (search) {
        where.OR = [
          { name: { contains: search, mode: "insensitive" } },
          { code: { contains: search, mode: "insensitive" } },
        ];
      }

      const [total, rows] = await Promise.all([
        prisma.branch.count({ where }),
        prisma.branch.findMany({
          where,
          orderBy: [{ sortOrder: "asc" }, { name: "asc" }],
          skip,
          take: limit,
          include: { _count: { select: { users: true } } },
        }),
      ]);

      return res.json({
        data: rows,
        pagination: { page, limit, total, totalPages: Math.ceil(total / limit) },
      });
    }

    const branchWhere = search
      ? {
          userId: req.user.sub,
          branch: {
            OR: [
              { name: { contains: search, mode: "insensitive" } },
              { code: { contains: search, mode: "insensitive" } },
            ],
          },
        }
      : { userId: req.user.sub };

    const [total, memberships] = await Promise.all([
      prisma.userBranch.count({ where: branchWhere }),
      prisma.userBranch.findMany({
        where: branchWhere,
        include: { branch: true },
        orderBy: { branch: { sortOrder: "asc" } },
        skip,
        take: limit,
      }),
    ]);

    return res.json({
      data: memberships.map((m) => m.branch),
      pagination: { page, limit, total, totalPages: Math.ceil(total / limit) },
    });
  } catch (err) {
    return next(err);
  }
});

const branchUserInclude = {
  users: {
    include: {
      user: {
        select: {
          id: true,
          email: true,
          fullName: true,
          username: true,
          roles: { select: { role: { select: { name: true } } } },
        },
      },
    },
  },
};

function filterBranchUsersForActor(req, row) {
  if (!row?.users?.length || !isAdminOnly(req)) return row;
  row.users = row.users.filter(
    (ub) => !(ub.user?.roles || []).some((r) => r.role?.name === SUPER_ADMIN_ROLE)
  );
  return row;
}

router.get("/:id", requireAuth, requireAdminPerm, async (req, res, next) => {
  try {
    const row = await prisma.branch.findUnique({
      where: { id: req.params.id },
      include: branchUserInclude,
    });
    if (!row) return res.status(404).json({ error: "Not found" });
    return res.json(filterBranchUsersForActor(req, row));
  } catch (err) {
    return next(err);
  }
});

router.post("/", requireAuth, requireRoles(["Admin", "Super Admin"]), async (req, res, next) => {
  try {
    const body = req.body || {};
    const { name, sortOrder, isActive } = body;
    if (!name || !String(name).trim())
      return res.status(400).json({ error: "Missing name" });

    const data = {
      name: String(name).trim(),
      sortOrder: Number.isFinite(Number(sortOrder)) ? Number(sortOrder) : 0,
      isActive: typeof isActive === "boolean" ? isActive : true,
      ...buildOptionalData(body),
    };
    const userId = req.user?.sub;
    const branch = await prisma.$transaction(async (tx) => {
      const created = await tx.branch.create({ data });

      // Auto-assign the creator to the new branch so they can switch to it.
      if (userId) {
        await tx.userBranch.upsert({
          where: { userId_branchId: { userId, branchId: created.id } },
          update: {},
          create: { userId, branchId: created.id },
        });

        const user = await tx.user.findUnique({
          where: { id: userId },
          select: { activeBranchId: true },
        });
        if (!user?.activeBranchId) {
          await tx.user.update({
            where: { id: userId },
            data: { activeBranchId: created.id },
          });
        }
      }

      return created;
    });

    return res.status(201).json(branch);
  } catch (err) {
    const r = prismaConflict(err, res);
    if (r) return r;
    return next(err);
  }
});

router.patch("/:id", requireAuth, requireRoles(["Admin", "Super Admin"]), async (req, res, next) => {
  try {
    const { id } = req.params;
    const existing = await prisma.branch.findUnique({ where: { id } });
    if (!existing) return res.status(404).json({ error: "Not found" });

    const body = req.body || {};
    const { name, sortOrder, isActive } = body;
    if (name !== undefined && !String(name).trim()) {
      return res.status(400).json({ error: "Name cannot be empty" });
    }

    const data = {};
    if (name !== undefined) data.name = String(name).trim();
    for (const k of OPTIONAL_FIELDS) {
      if (body[k] !== undefined) data[k] = normalizeOptionalString(body[k]);
    }
    if (body.phones !== undefined) {
      const phones = parsePhonesInput(body.phones);
      data.phones = phones;
      if (body.phone === undefined) {
        data.phone = phones?.[0] ?? null;
      }
    }
    if (sortOrder !== undefined) {
      data.sortOrder = Number.isFinite(Number(sortOrder)) ? Number(sortOrder) : 0;
    }
    if (isActive !== undefined) data.isActive = Boolean(isActive);

    const branch = await prisma.branch.update({ where: { id }, data });
    return res.json(branch);
  } catch (err) {
    const r = prismaConflict(err, res);
    if (r) return r;
    return next(err);
  }
});

router.delete("/:id", requireAuth, requireRoles(["Admin", "Super Admin"]), async (req, res, next) => {
  try {
    const { id } = req.params;
    const row = await prisma.branch.findUnique({ where: { id } });
    if (!row) return res.status(404).json({ error: "Not found" });
    await prisma.branch.delete({ where: { id } });
    return res.json({ ok: true });
  } catch (err) {
    return next(err);
  }
});

router.post("/:id/assign", requireAuth, requireRoles(["Admin", "Super Admin"]), async (req, res, next) => {
  try {
    const { id } = req.params;
    const branch = await prisma.branch.findUnique({ where: { id } });
    if (!branch) return res.status(404).json({ error: "Branch not found" });

    const { userIds } = req.body || {};
    if (!Array.isArray(userIds) || userIds.length === 0) {
      return res.status(400).json({ error: "Provide an array of userIds" });
    }

    for (const userId of userIds) {
      await assertCanAccessUser(prisma, req, userId);
    }

    await prisma.userBranch.createMany({
      data: userIds.map((userId) => ({ userId, branchId: id })),
      skipDuplicates: true,
    });

    const updated = await prisma.branch.findUnique({
      where: { id },
      include: branchUserInclude,
    });
    return res.json(filterBranchUsersForActor(req, updated));
  } catch (err) {
    if (err.status) return res.status(err.status).json({ error: err.message });
    return next(err);
  }
});

router.delete("/:id/users/:userId", requireAuth, requireRoles(["Admin", "Super Admin"]), async (req, res, next) => {
  try {
    const { id, userId } = req.params;
    await assertCanAccessUser(prisma, req, userId);
    await prisma.userBranch.delete({
      where: { userId_branchId: { userId, branchId: id } },
    }).catch(() => null);
    return res.json({ ok: true });
  } catch (err) {
    if (err.status) return res.status(err.status).json({ error: err.message });
    return next(err);
  }
});

module.exports = router;
