import express from "express";

import { requireAuth, requireRole } from "../../middleware/auth.js";
import { validate } from "../../shared/validate.js";
import { httpError } from "../../shared/errors.js";
import { Ticket } from "./ticket.model.js";
import { Channel } from "../chat/chat.model.js";
import {
  createTicketSchema,
  ticketIdParamSchema,
  updateTicketSchema,
} from "./tickets.schemas.js";

export const ticketsRouter = express.Router();

ticketsRouter.use(requireAuth);

// Admin: list all tickets. Customer: list own tickets.
ticketsRouter.get("/", async (req, res, next) => {
  try {
    if (req.user.role === "admin") {
      const tickets = await Ticket.find({})
        .populate("createdBy", "fullName email role")
        .populate("assignedTo", "fullName email role")
        .populate("channelId", "name")
        .sort({ createdAt: -1 })
        .lean();
      return res.json({ tickets });
    }

    if (req.user.role !== "customer") {
      throw httpError(403, "Forbidden");
    }

    const tickets = await Ticket.find({ createdBy: req.user._id })
      .populate("channelId", "name")
      .sort({ createdAt: -1 })
      .lean();
    res.json({ tickets });
  } catch (err) {
    next(err);
  }
});

// Customer: create a ticket (also creates a private discussion channel)
ticketsRouter.post(
  "/",
  requireRole("customer"),
  validate(createTicketSchema),
  async (req, res, next) => {
    try {
      const { title, description } = req.validated.body;

      const companyName = req.user.fullName;

      // Keep one private support discussion per customer to avoid mixing customers
      // into a single group, and to avoid creating many duplicate chats.
      let channel = await Channel.findOne({
        type: "private",
        isPrivate: true,
        name: `Support - ${companyName}`,
        members: req.user._id,
      }).sort({ createdAt: -1 });

      if (!channel) {
        channel = await Channel.create({
          name: `Support - ${companyName}`,
          description: "Customer support discussion",
          type: "private",
          isPrivate: true,
          members: [req.user._id.toString()],
          createdBy: req.user._id,
        });
      }

      const ticket = await Ticket.create({
        title,
        description,
        createdBy: req.user._id,
        contactName: req.user.fullName,
        companyName: req.user.fullName,
        contactEmail: req.user.email,
        contactPhone: req.user.phone,
        channelId: channel._id,
      });

      const populated = await Ticket.findById(ticket._id)
        .populate("channelId", "name")
        .lean();

      res.status(201).json({ ticket: populated });
    } catch (err) {
      next(err);
    }
  }
);

// Get single ticket (admin or owning customer)
ticketsRouter.get(
  "/:id",
  validate(ticketIdParamSchema),
  async (req, res, next) => {
    try {
      const { id } = req.validated.params;
      const ticket = await Ticket.findById(id)
        .populate("createdBy", "fullName email role")
        .populate("assignedTo", "fullName email role")
        .populate("channelId", "name")
        .lean();
      if (!ticket) throw httpError(404, "Ticket not found");

      const canRead =
        req.user.role === "admin" ||
        (req.user.role === "customer" &&
          ticket.createdBy?._id?.toString?.() === req.user._id.toString());
      if (!canRead) throw httpError(403, "Forbidden");

      res.json({ ticket });
    } catch (err) {
      next(err);
    }
  }
);

// Admin: update ticket status / assignment
// Use chat channel patch endpoint to add/remove members; only admins can.
ticketsRouter.patch(
  "/:id",
  requireRole("admin"),
  validate(updateTicketSchema),
  async (req, res, next) => {
    try {
      const { id } = req.validated.params;
      const { status, assignedTo } = req.validated.body;

      const ticket = await Ticket.findById(id);
      if (!ticket) throw httpError(404, "Ticket not found");

      if (status !== undefined) ticket.status = status;
      if (assignedTo !== undefined) {
        ticket.assignedTo = assignedTo === null ? undefined : assignedTo;
      }

      await ticket.save();

      const populated = await Ticket.findById(id)
        .populate("createdBy", "fullName email role")
        .populate("assignedTo", "fullName email role")
        .populate("channelId", "name")
        .lean();

      res.json({ ticket: populated });
    } catch (err) {
      next(err);
    }
  }
);
