import express from "express";

import { requireAuth, requireRole } from "../../middleware/auth.js";
import { validate } from "../../shared/validate.js";
import { httpError } from "../../shared/errors.js";
import { Todo } from "./todo.model.js";
import {
  createTodoSchema,
  todoIdParamSchema,
  updateTodoSchema,
} from "./todos.schemas.js";

export const todosRouter = express.Router();
todosRouter.use(requireAuth);

// Admin: list all todos
todosRouter.get("/", requireRole("admin"), async (req, res, next) => {
  try {
    const todos = await Todo.find({})
      .populate("assignedTo", "fullName email role")
      .populate("createdBy", "fullName email role")
      .sort({ createdAt: -1 })
      .lean();
    res.json({ todos });
  } catch (err) {
    next(err);
  }
});

todosRouter.get("/me", async (req, res, next) => {
  try {
    const todos = await Todo.find({ assignedTo: req.user._id })
      .sort({ createdAt: -1 })
      .lean();
    res.json({ todos });
  } catch (err) {
    next(err);
  }
});

todosRouter.post("/", validate(createTodoSchema), async (req, res, next) => {
  try {
    const { title, description, dueAt, projectId, assignedTo } =
      req.validated.body;

    const finalAssignedTo =
      req.user.role === "customer" ? req.user._id : assignedTo || req.user._id;

    const doc = await Todo.create({
      title,
      description,
      dueAt: dueAt ? new Date(dueAt) : undefined,
      projectId,
      assignedTo: finalAssignedTo,
      createdBy: req.user._id,
      status: "open",
    });

    res.status(201).json({ todo: doc });
  } catch (err) {
    next(err);
  }
});

todosRouter.patch(
  "/:id",
  validate(updateTodoSchema),
  async (req, res, next) => {
    try {
      const { id } = req.validated.params;
      const { title, description, status, dueAt } = req.validated.body;

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

      const canEdit =
        req.user.role === "admin" ||
        todo.assignedTo.toString() === req.user._id.toString();
      if (!canEdit) throw httpError(403, "Forbidden");

      if (title !== undefined) todo.title = title;
      if (description !== undefined) todo.description = description;
      if (status !== undefined) todo.status = status;
      if (dueAt !== undefined)
        todo.dueAt = dueAt === null ? undefined : new Date(dueAt);

      await todo.save();
      res.json({ todo });
    } catch (err) {
      next(err);
    }
  }
);

// Get a single todo by id (used by some clients)
todosRouter.get("/:id", validate(todoIdParamSchema), async (req, res, next) => {
  try {
    const { id } = req.validated.params;
    const todo = await Todo.findById(id).lean();
    if (!todo) throw httpError(404, "Todo not found");

    const canRead =
      req.user.role === "admin" ||
      todo.assignedTo?.toString?.() === req.user._id.toString();
    if (!canRead) throw httpError(403, "Forbidden");

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

// Delete a todo by id
todosRouter.delete(
  "/:id",
  validate(todoIdParamSchema),
  async (req, res, next) => {
    try {
      const { id } = req.validated.params;

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

      const canDelete =
        req.user.role === "admin" ||
        todo.assignedTo?.toString?.() === req.user._id.toString();
      if (!canDelete) throw httpError(403, "Forbidden");

      await Todo.findByIdAndDelete(id);
      res.json({ ok: true });
    } catch (err) {
      next(err);
    }
  }
);
