import { Request, Response } from "express";
import { Role } from "@prisma/client";
import { AuthenticatedRequest } from "../../middleware/auth";
import { todoService, CreateTodoData, UpdateTodoData } from "./todos.service";
import { handleError } from "../../utils/http";

export class TodoController {
  async createTodo(req: Request, res: Response) {
    try {
      const data: CreateTodoData = {
        title: req.body.title,
        task: req.body.task,
        status: req.body.status,
        priority: req.body.priority,
        dueDate: req.body.dueDate,
        createdById: req.body.createdById,
        assigneeIds: req.body.assigneeIds || [],
      };

      const todo = await todoService.createTodo(data);
      res.status(201).json(todo);
    } catch (error: any) {
      return handleError(res, "Failed to create todo", error);
    }
  }

  async listTodos(req: Request, res: Response) {
    try {
      const params = {
        page: Math.max(parseInt((req.query.page as string) || "1", 10), 1),
        limit: Math.max(parseInt((req.query.limit as string) || "25", 10), 1),
        search: ((req.query.search as string) || "").trim(),
        statuses: ((req.query.statuses as string) || "")
          .split(",")
          .map((s) => s.trim())
          .filter((s) => !!s),
        priorities: ((req.query.priorities as string) || "")
          .split(",")
          .map((p) => p.trim())
          .filter((p) => !!p),
        sortBy: req.query.sortBy || "",
        sortDir: (((req.query.sortDir as string) || "asc").toLowerCase() ===
        "desc"
          ? "desc"
          : "asc") as "asc" | "desc",
        assigneeUserId: (req.query.assigneeUserId as string) || undefined,
        teamOfManagerId: (req.query.teamOfManagerId as string) || undefined,
      } as any;

      const requesterRole = (req as AuthenticatedRequest).user?.role as
        | Role
        | undefined;
      const requesterId = (req as AuthenticatedRequest).user?.userId as
        | string
        | undefined;

      const result = await todoService.listTodos(
        params,
        requesterRole,
        requesterId
      );
      res.json({
        items: result.items,
        total: result.total,
        page: params.page,
        limit: params.limit,
      });
    } catch (error: any) {
      return handleError(res, "Failed to fetch todos", error);
    }
  }

  async getUserTodos(req: Request, res: Response) {
    try {
      const { userId } = req.params as { userId: string };
      const requesterRole = (req as AuthenticatedRequest).user?.role as
        | Role
        | undefined;
      const requesterId = (req as AuthenticatedRequest).user?.userId as
        | string
        | undefined;

      const todos = await todoService.getUserTodos(
        userId,
        requesterRole,
        requesterId
      );
      res.json(todos);
    } catch (error: any) {
      return handleError(res, "Failed to fetch user todos", error);
    }
  }

  async updateTodo(req: Request, res: Response) {
    try {
      const { id } = req.params as { id: string };
      const data: UpdateTodoData = {
        title: req.body.title,
        task: req.body.task,
        status: req.body.status,
        priority: req.body.priority,
        dueDate: req.body.dueDate,
        assigneeIds: req.body.assigneeIds,
      };

      const todo = await todoService.updateTodo(id, data);
      res.json(todo);
    } catch (error: any) {
      return handleError(res, "Failed to update todo", error);
    }
  }

  async deleteTodo(req: Request, res: Response) {
    try {
      const { id } = req.params as { id: string };
      await todoService.deleteTodo(id);
      res.status(200).json({ message: "Todo deleted successfully" });
    } catch (error: any) {
      if (error.code === "NOT_FOUND") {
        return handleError(res, "Todo not found", error, 404);
      }
      return handleError(res, "Failed to delete todo", error);
    }
  }

  async listComments(req: Request, res: Response) {
    try {
      const { id } = req.params as { id: string }; // todoId
      const requesterRole = (req as AuthenticatedRequest).user?.role as
        | Role
        | undefined;
      const requesterId = (req as AuthenticatedRequest).user?.userId as
        | string
        | undefined;

      const comments = await todoService.listComments(
        id,
        requesterRole,
        requesterId
      );
      res.json(comments);
    } catch (error: any) {
      if (error.code === "FORBIDDEN") {
        return handleError(res, "Forbidden", error, 403);
      }
      if (error.code === "NOT_FOUND") {
        return handleError(res, "Todo not found", error, 404);
      }
      return handleError(res, "Failed to fetch comments", error);
    }
  }

  async addComment(req: Request, res: Response) {
    try {
      const { id } = req.params as { id: string }; // todoId
      const { content } = req.body as { content: string };
      const requesterRole = (req as AuthenticatedRequest).user?.role as
        | Role
        | undefined;
      const requesterId = (req as AuthenticatedRequest).user?.userId as
        | string
        | undefined;

      const newComment = await todoService.addComment(
        id,
        content,
        requesterRole,
        requesterId
      );
      res.status(201).json(newComment);
    } catch (error: any) {
      if (error.code === "BAD_REQUEST") {
        return handleError(res, "Content is required", error, 400);
      }
      if (error.code === "FORBIDDEN") {
        return handleError(res, "Forbidden", error, 403);
      }
      if (error.code === "NOT_FOUND") {
        return handleError(res, "Todo not found", error, 404);
      }
      return handleError(res, "Failed to add comment", error);
    }
  }

  async updateComment(req: Request, res: Response) {
    try {
      const { todoId, commentId } = req.params as {
        todoId: string;
        commentId: string;
      };
      const { content } = req.body as { content: string };
      const requesterRole = (req as AuthenticatedRequest).user?.role as
        | Role
        | undefined;
      const requesterId = (req as AuthenticatedRequest).user?.userId as
        | string
        | undefined;

      const updated = await todoService.updateComment(
        todoId,
        commentId,
        content,
        requesterRole,
        requesterId
      );
      res.json(updated);
    } catch (error: any) {
      if (error.code === "BAD_REQUEST") {
        return handleError(res, "Content is required", error, 400);
      }
      if (error.code === "FORBIDDEN") {
        return handleError(res, "Forbidden", error, 403);
      }
      if (error.code === "NOT_FOUND") {
        return handleError(res, "Comment not found", error, 404);
      }
      return handleError(res, "Failed to update comment", error);
    }
  }

  async deleteComment(req: Request, res: Response) {
    try {
      const { todoId, commentId } = req.params as {
        todoId: string;
        commentId: string;
      };
      const requesterRole = (req as AuthenticatedRequest).user?.role as
        | Role
        | undefined;
      const requesterId = (req as AuthenticatedRequest).user?.userId as
        | string
        | undefined;

      await todoService.deleteComment(
        todoId,
        commentId,
        requesterRole,
        requesterId
      );
      res.status(200).json({ message: "Comment deleted" });
    } catch (error: any) {
      if (error.code === "FORBIDDEN") {
        return handleError(res, "Forbidden", error, 403);
      }
      if (error.code === "NOT_FOUND") {
        return handleError(res, "Comment not found", error, 404);
      }
      return handleError(res, "Failed to delete comment", error);
    }
  }
}

export const todoController = new TodoController();
