import { Role } from "@prisma/client";
import { Response } from "express";
import { AuthenticatedRequest } from "../middleware/auth";
import { created, handleError, ok } from "../utils/http";
import * as todoService from "../services/todoService";
import { canAccessTodo, validateCommentAuthor } from "../services/permissions";

function getRequester(req: AuthenticatedRequest) {
  const requesterId = req.user?.userId as string | undefined;
  const requesterRole = req.user?.role as Role | undefined;
  if (!requesterId || !requesterRole) return null;
  return { id: requesterId, role: requesterRole } as const;
}

export async function createTodo(req: AuthenticatedRequest, res: Response) {
  try {
    const { title, task, status, priority, dueDate, assigneeIds, createdById } = req.body as any;
    const data = await todoService.createTodo({
      title,
      task,
      status,
      priority,
      dueDate: new Date(dueDate),
      createdById,
      assigneeIds: (assigneeIds || []) as string[],
    });
    return created(res, data);
  } catch (error) {
    return handleError(res, "Failed to create todo", error);
  }
}

export async function listTodos(req: AuthenticatedRequest, res: Response) {
  try {
    const requester = getRequester(req);
    if (!requester) return handleError(res, "Unauthorized", "Missing user", 401);

    const { items, total, page, limit } = await todoService.listTodos(
      {
        page: req.query.page as any,
        limit: req.query.limit as any,
        search: (req.query.search as string) || "",
        statuses: (req.query.statuses as string) || "",
        priorities: (req.query.priorities as string) || "",
        sortBy: (req.query.sortBy as string) || "",
        sortDir: (req.query.sortDir as string) || "",
        assigneeUserId: (req.query.assigneeUserId as string) || "",
        teamOfManagerId: (req.query.teamOfManagerId as string) || "",
      },
      requester
    );

    return ok(res, { items, total, page, limit });
  } catch (error) {
    return handleError(res, "Failed to fetch todos", error);
  }
}

export async function updateTodo(req: AuthenticatedRequest, res: Response) {
  try {
    const todoId = (req.params as any).todoId || (req.params as any).id;
    const { title, task, status, priority, dueDate, assigneeIds } = req.body as any;

    const data = await todoService.updateTodo(todoId, {
      title,
      task,
      status,
      priority,
      dueDate: dueDate ? new Date(dueDate) : null,
      assigneeIds: assigneeIds || undefined,
    });
    return ok(res, data);
  } catch (error) {
    return handleError(res, "Failed to update todo", error);
  }
}

export async function deleteTodo(req: AuthenticatedRequest, res: Response) {
  try {
    const todoId = (req.params as any).todoId || (req.params as any).id;

    // Check existence first
    const existing = await todoService.getTodoAccessShape(todoId);
    if (!existing) return handleError(res, "Todo not found", "Not found", 404);

    await todoService.deleteTodo(todoId);
    return ok(res, { message: "Todo deleted successfully" });
  } catch (error) {
    return handleError(res, "Failed to delete todo", error);
  }
}

export async function listComments(req: AuthenticatedRequest, res: Response) {
  try {
    const requester = getRequester(req);
    if (!requester) return handleError(res, "Unauthorized", "Missing user", 401);

    const todoId = (req.params as any).todoId || (req.params as any).id;
    const todo = await todoService.getTodoAccessShape(todoId);
    if (!todo) return handleError(res, "Todo not found", "Not found", 404);

    if (!canAccessTodo(requester, todo)) {
      return handleError(res, "Forbidden", "Access denied", 403);
    }

    const comments = await todoService.listComments(todoId);
    return ok(res, comments);
  } catch (error) {
    return handleError(res, "Failed to fetch comments", error);
  }
}

export async function addComment(req: AuthenticatedRequest, res: Response) {
  try {
    const requester = getRequester(req);
    if (!requester) return handleError(res, "Unauthorized", "Missing user", 401);

    const todoId = (req.params as any).todoId || (req.params as any).id;
    const { content } = req.body as { content: string };
    if (!content || !content.trim()) {
      return handleError(res, "Content is required", "Validation error", 400);
    }

    const todo = await todoService.getTodoAccessShape(todoId);
    if (!todo) return handleError(res, "Todo not found", "Not found", 404);

    if (!canAccessTodo(requester, todo)) {
      return handleError(res, "Forbidden", "Access denied", 403);
    }

    const newComment = await todoService.addComment(todoId, requester.id, content.trim());
    return created(res, newComment);
  } catch (error) {
    return handleError(res, "Failed to add comment", error);
  }
}

export async function updateComment(req: AuthenticatedRequest, res: Response) {
  try {
    const requester = getRequester(req);
    if (!requester) return handleError(res, "Unauthorized", "Missing user", 401);

    const todoId = (req.params as any).todoId || (req.params as any).id;
    const { commentId } = req.params as any;
    const { content } = req.body as { content: string };
    if (!content || !content.trim()) {
      return handleError(res, "Content is required", "Validation error", 400);
    }

    const todo = await todoService.getTodoAccessShape(todoId);
    if (!todo) return handleError(res, "Todo not found", "Not found", 404);

    if (!canAccessTodo(requester, todo)) {
      return handleError(res, "Forbidden", "Access denied", 403);
    }

    const existing = await todoService.getCommentSlim(commentId);
    if (!existing || existing.todoId !== todoId) {
      return handleError(res, "Comment not found", "Not found", 404);
    }

    validateCommentAuthor(requester.id, { userId: existing.userId });

    const updated = await todoService.updateComment(commentId, content.trim());
    return ok(res, updated);
  } catch (error: any) {
    const status = (error && typeof error === "object" && "status" in error ? (error as any).status : undefined) || 500;
    return handleError(res, "Failed to update comment", error, status);
  }
}

export async function deleteComment(req: AuthenticatedRequest, res: Response) {
  try {
    const requester = getRequester(req);
    if (!requester) return handleError(res, "Unauthorized", "Missing user", 401);

    const todoId = (req.params as any).todoId || (req.params as any).id;
    const { commentId } = req.params as any;

    const todo = await todoService.getTodoAccessShape(todoId);
    if (!todo) return handleError(res, "Todo not found", "Not found", 404);

    if (!canAccessTodo(requester, todo)) {
      return handleError(res, "Forbidden", "Access denied", 403);
    }

    const existing = await todoService.getCommentSlim(commentId);
    if (!existing || existing.todoId !== todoId) {
      return handleError(res, "Comment not found", "Not found", 404);
    }

    validateCommentAuthor(requester.id, { userId: existing.userId });

    await todoService.deleteComment(commentId);
    return ok(res, { message: "Comment deleted" });
  } catch (error: any) {
    const status = (error && typeof error === "object" && "status" in error ? (error as any).status : undefined) || 500;
    return handleError(res, "Failed to delete comment", error, status);
  }
}

export async function listTodosForUser(req: AuthenticatedRequest, res: Response) {
  try {
    const requester = getRequester(req);
    if (!requester) return handleError(res, "Unauthorized", "Missing user", 401);

    const { userId } = req.params as any;
    const data = await todoService.getTodosForUser(userId, requester);
    return ok(res, data);
  } catch (error) {
    return handleError(res, "Failed to fetch user todos", error);
  }
}
