import { z } from "zod";
import { Hono } from "hono";
import { zValidator } from "@hono/zod-validator";
import { auth } from "@/auth";
import { prisma } from "@/lib/prisma";
import { TaskStatus } from "@prisma/client";

import { createTaskSchema } from "../schemas";
import { storeMentionsForTask, storeMentionsForComment } from "@/lib/mentions";
import { createTaskAssignmentNotification } from "@/lib/notifications";
import { emitNotification } from "@/lib/socket";

const app = new Hono()
  .delete("/:taskId", async (c) => {
    const session = await auth();
    const { taskId } = c.req.param();

    if (!session?.user?.email) {
      return c.json({ error: "Unauthorized" }, 401);
    }

    const currentUser = await prisma.user.findUnique({
      where: { email: session.user.email },
    });

    if (!currentUser) {
      return c.json({ error: "User not found" }, 404);
    }

    // Get the task
    const task = await prisma.task.findUnique({
      where: { id: taskId },
    });

    if (!task) {
      return c.json({ error: "Task not found" }, 404);
    }

    // Check if user is a member of the workspace
    const member = await prisma.member.findFirst({
      where: {
        workspaceId: task.workspaceId,
        userId: currentUser.id,
      },
    });

    if (!member) {
      return c.json({ error: "Unauthorized" }, 401);
    }

    // Delete the task
    await prisma.task.delete({
      where: { id: taskId },
    });

    return c.json({ data: { id: task.id } });
  })
  .get(
    "/",
    zValidator(
      "query",
      z.object({
        workspaceId: z.string(),
        projectId: z.string().nullish(),
        assigneeId: z.string().nullish(),
        status: z.nativeEnum(TaskStatus).nullish(),
        search: z.string().nullish(),
        dueDate: z.string().nullish(),
        boardId: z.string().nullish(),
        labels: z.string().nullish(),
      })
    ),
    async (c) => {
      const session = await auth();
      const { workspaceId, projectId, assigneeId, status, search, dueDate, boardId, labels } = c.req.valid("query");

      if (!session?.user?.email) {
        return c.json({ error: "Unauthorized" }, 401);
      }

      const currentUser = await prisma.user.findUnique({
        where: { email: session.user.email },
      });

      if (!currentUser) {
        return c.json({ error: "User not found" }, 404);
      }

      // Check if user is a member of the workspace
      const member = await prisma.member.findFirst({
        where: {
          workspaceId,
          userId: currentUser.id,
        },
      });

      if (!member) {
        return c.json({ error: "Unauthorized" }, 401);
      }

      // Build the where clause
      const where: any = {
        workspaceId,
      };

      if (projectId) {
        where.projectId = projectId;
      }

      if (assigneeId) {
        where.assigneeId = assigneeId;
      }

      if (status) {
        where.status = status;
      }

      if (dueDate) {
        where.dueDate = {
          lte: new Date(dueDate),
        };
      }

      if (boardId) {
        where.boardId = boardId;
      }

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

      // Handle labels filtering
      if (labels) {
        const labelArray = labels.split(",").filter(Boolean);
        if (labelArray.length > 0) {
          where.labels = {
            hasSome: labelArray,
          };
        }
      }

      // Get tasks with their relations
      const tasks = await prisma.task.findMany({
        where,
        orderBy: { createdAt: 'desc' },
        include: {
          project: {
            select: {
              id: true,
              name: true,
              imageUrl: true,
              workspaceId: true,
            },
          },
          assignee: {
            include: {
              user: {
                select: {
                  id: true,
                  name: true,
                  email: true,
                  image: true,
                },
              },
            },
          },
          epic: {
            select: {
              id: true,
              name: true,
              status: true,
              description: true,
            },
          },
        },
      });

      // Format the response
      const formattedTasks = tasks.map(task => ({
        id: task.id,
        name: task.name,
        status: task.status,
        workspaceId: task.workspaceId,
        projectId: task.projectId,
        assigneeId: task.assigneeId,
        boardId: task.boardId,
        epicId: task.epicId,
        position: task.position,
        dueDate: task.dueDate ? task.dueDate.toISOString() : null,
        description: task.description || null,
        labels: task.labels || [],
        attachments: task.attachments || [],
        createdAt: task.createdAt,
        updatedAt: task.updatedAt,
        project: task.project ? {
          id: task.project.id,
          name: task.project.name,
          imageUrl: task.project.imageUrl,
          workspaceId: task.project.workspaceId,
        } : null,
        assignee: task.assignee ? {
          id: task.assignee.id,
          userId: task.assignee.userId,
          workspaceId: task.assignee.workspaceId,
          role: task.assignee.role,
          name: task.assignee.user.name || task.assignee.user.email,
          email: task.assignee.user.email,
          imageUrl: task.assignee.user.image,
        } : null,
        epic: task.epic ? {
          id: task.epic.id,
          name: task.epic.name,
          status: task.epic.status,
          description: task.epic.description,
        } : null,
      }));

      return c.json({
        data: {
          total: formattedTasks.length,
          documents: formattedTasks,
        },
      });
    }
  )
  .post(
    "/",
    zValidator("json", createTaskSchema),
    async (c) => {
      const session = await auth();
      const { name, status, workspaceId, projectId, assigneeId, boardId, epicId, dueDate, description, labels, attachments } =
        c.req.valid("json");

      if (!session?.user?.email) {
        return c.json({ error: "Unauthorized" }, 401);
      }

      const currentUser = await prisma.user.findUnique({
        where: { email: session.user.email },
      });

      if (!currentUser) {
        return c.json({ error: "User not found" }, 404);
      }

      // Check if user is a member of the workspace
      const member = await prisma.member.findFirst({
        where: {
          workspaceId,
          userId: currentUser.id,
        },
      });

      if (!member) {
        return c.json({ error: "Unauthorized" }, 401);
      }

      // Find the highest position for the status
      const highestPositionTask = await prisma.task.findFirst({
        where: {
          status,
          workspaceId,
        },
        orderBy: {
          position: 'asc',
        },
      });

      const newPosition = highestPositionTask
        ? highestPositionTask.position + 1000
        : 1000;

      // Create the task
      const task = await prisma.task.create({
        data: {
          name,
          status,
          workspaceId,
          projectId,
          assigneeId,
          boardId: boardId && boardId !== "none" ? boardId : null,
          epicId: epicId || null,
          dueDate,
          description: description || null,
          position: newPosition,
          labels: labels || [],
          attachments: attachments || [],
        },
      });

      // Store mentions from the task description
      if (description) {
        try {
          await storeMentionsForTask(
            task.id,
            workspaceId,
            currentUser.id,
            description
          );
        } catch (error) {
          console.error('Failed to store mentions for task:', error);
          // Don't fail the task creation if mentions fail
        }
      }
      console.log(assigneeId);
      // Create task assignment notification
      if (assigneeId) {
        try {
          // Get the user ID from the member (assigneeId is a Member ID, not User ID)
          const assigneeMember = await prisma.member.findUnique({
            where: { id: assigneeId },
            select: { userId: true },
          });

          if (assigneeMember) {
            const notification = await createTaskAssignmentNotification(
              assigneeMember.userId, // Use the actual user ID, not member ID
              currentUser.id,
              workspaceId,
              task.id,
              task.name,
              currentUser.name || currentUser.email
            );
            console.log(assigneeMember.userId,
              currentUser.id,
              workspaceId,
              task.id,
              task.name,
              currentUser.name || currentUser.email);
            if (notification) {
              await emitNotification(notification.userId, notification);
            }
          }
        } catch (error) {
          console.error('Failed to create task assignment notification:', error);
        }
      }

      return c.json({ data: task });
    }
  )
  .patch(
    "/:taskId",
    zValidator("json", createTaskSchema.partial()),
    async (c) => {
      const session = await auth();
      const { taskId } = c.req.param();
      const { name, status, description, projectId, assigneeId, boardId, epicId, dueDate, labels, attachments } =
        c.req.valid("json");

      if (!session?.user?.email) {
        return c.json({ error: "Unauthorized" }, 401);
      }

      const currentUser = await prisma.user.findUnique({
        where: { email: session.user.email },
      });

      if (!currentUser) {
        return c.json({ error: "User not found" }, 404);
      }

      // Get the existing task
      const existingTask = await prisma.task.findUnique({
        where: { id: taskId },
      });

      if (!existingTask) {
        return c.json({ error: "Task not found" }, 404);
      }

      // Check if user is a member of the workspace
      const member = await prisma.member.findFirst({
        where: {
          workspaceId: existingTask.workspaceId,
          userId: currentUser.id,
        },
      });

      if (!member) {
        return c.json({ error: "Unauthorized" }, 401);
      }

      // Update the task
      const task = await prisma.task.update({
        where: { id: taskId },
        data: {
          name,
          status,
          projectId,
          dueDate,
          assigneeId,
          boardId: boardId !== undefined ? (boardId && boardId !== "none" ? boardId : null) : undefined,
          description,
          labels: labels !== undefined ? labels : undefined,
          attachments: attachments !== undefined ? attachments : undefined,
          epicId: epicId !== undefined ? epicId : undefined,
        },
      });

      // Store mentions from the updated task description
      if (description !== undefined) {
        try {
          await storeMentionsForTask(
            taskId,
            existingTask.workspaceId,
            currentUser.id,
            description || ""
          );
        } catch (error) {
          console.error('Failed to store mentions for task update:', error);
          // Don't fail the task update if mentions fail
        }
      }

      // Create task assignment notification if assignee changed
      if (assigneeId !== undefined && assigneeId !== existingTask.assigneeId) {
        try {
          if (assigneeId) {
            // Get the user ID from the member (assigneeId is a Member ID, not User ID)
            const assigneeMember = await prisma.member.findUnique({
              where: { id: assigneeId },
              select: { userId: true },
            });

            if (assigneeMember) {
              const notification = await createTaskAssignmentNotification(
                assigneeMember.userId, // Use the actual user ID, not member ID
                currentUser.id,
                existingTask.workspaceId,
                taskId,
                task.name,
                currentUser.name || currentUser.email
              );

              if (notification) {
                await emitNotification(notification.userId, notification);
              }
            }
          }
        } catch (error) {
          console.error('Failed to create task assignment notification:', error);
        }
      }

      return c.json({ data: task });
    }
  )
  .get("/:taskId", async (c) => {
    const session = await auth();
    const { taskId } = c.req.param();

    if (!session?.user?.email) {
      return c.json({ error: "Unauthorized" }, 401);
    }

    const currentUser = await prisma.user.findUnique({
      where: { email: session.user.email },
    });

    if (!currentUser) {
      return c.json({ error: "User not found" }, 404);
    }

    // Get the task with relations
    const task = await prisma.task.findUnique({
      where: { id: taskId },
      include: {
        project: {
          select: {
            id: true,
            name: true,
            imageUrl: true,
            workspaceId: true,
          },
        },
        assignee: {
          include: {
            user: {
              select: {
                id: true,
                name: true,
                email: true,
                image: true,
              },
            },
          },
        },
        epic: {
          select: {
            id: true,
            name: true,
            status: true,
            description: true,
          },
        },
      },
    });

    if (!task) {
      return c.json({ error: "Task not found" }, 404);
    }

    // Check if user is a member of the workspace
    const currentMember = await prisma.member.findFirst({
      where: {
        workspaceId: task.workspaceId,
        userId: currentUser.id,
      },
    });

    if (!currentMember) {
      return c.json({ error: "Unauthorized" }, 401);
    }

    // Format the response
    const formattedTask = {
      id: task.id,
      name: task.name,
      status: task.status,
      workspaceId: task.workspaceId,
      projectId: task.projectId,
      assigneeId: task.assigneeId,
      boardId: task.boardId,
      epicId: task.epicId,
      position: task.position,
      dueDate: task.dueDate ? task.dueDate.toISOString() : null,
      description: task.description || null,
      labels: task.labels || [],
      attachments: task.attachments || [],
      createdAt: task.createdAt,
      updatedAt: task.updatedAt,
      project: task.project ? {
        id: task.project.id,
        name: task.project.name,
        imageUrl: task.project.imageUrl,
        workspaceId: task.project.workspaceId,
      } : null,
      assignee: task.assignee ? {
        id: task.assignee.id,
        userId: task.assignee.userId,
        workspaceId: task.assignee.workspaceId,
        role: task.assignee.role,
        name: task.assignee.user.name || task.assignee.user.email,
        email: task.assignee.user.email,
        imageUrl: task.assignee.user.image,
      } : null,
      epic: task.epic ? {
        id: task.epic.id,
        name: task.epic.name,
        status: task.epic.status,
        description: task.epic.description,
      } : null,
    };

    return c.json({ data: formattedTask });
  })
  .get("/:taskId/comments", async (c) => {
    const session = await auth();
    const { taskId } = c.req.param();

    if (!session?.user?.email) {
      return c.json({ error: "Unauthorized" }, 401);
    }

    const currentUser = await prisma.user.findUnique({
      where: { email: session.user.email },
    });

    if (!currentUser) {
      return c.json({ error: "User not found" }, 404);
    }

    // Get the task to check workspace membership
    const task = await prisma.task.findUnique({
      where: { id: taskId },
      select: { workspaceId: true },
    });

    if (!task) {
      return c.json({ error: "Task not found" }, 404);
    }

    // Check if user is a member of the workspace
    const member = await prisma.member.findFirst({
      where: {
        workspaceId: task.workspaceId,
        userId: currentUser.id,
      },
    });

    if (!member) {
      return c.json({ error: "Unauthorized" }, 401);
    }

    // Get comments for the task
    const comments = await prisma.comment.findMany({
      where: { taskId },
      include: {
        author: {
          select: {
            id: true,
            name: true,
            email: true,
            image: true,
          },
        },
      },
      orderBy: { createdAt: 'desc' },
    });

    return c.json({ data: comments });
  })
  .post("/:taskId/comments",
    zValidator("json", z.object({
      content: z.string().min(1),
      attachments: z.array(z.string()).optional().default([])
    })),
    async (c) => {
      const session = await auth();
      const { taskId } = c.req.param();
      const { content, attachments } = c.req.valid("json");

      if (!session?.user?.email) {
        return c.json({ error: "Unauthorized" }, 401);
      }

      const currentUser = await prisma.user.findUnique({
        where: { email: session.user.email },
      });

      if (!currentUser) {
        return c.json({ error: "User not found" }, 404);
      }

      // Get the task to check workspace membership
      const task = await prisma.task.findUnique({
        where: { id: taskId },
        select: { workspaceId: true },
      });

      if (!task) {
        return c.json({ error: "Task not found" }, 404);
      }

      // Check if user is a member of the workspace
      const member = await prisma.member.findFirst({
        where: {
          workspaceId: task.workspaceId,
          userId: currentUser.id,
        },
      });

      if (!member) {
        return c.json({ error: "Unauthorized" }, 401);
      }

      // Create the comment
      const comment = await prisma.comment.create({
        data: {
          content,
          taskId,
          authorId: currentUser.id,
          attachments: attachments || [],
        },
        include: {
          author: {
            select: {
              id: true,
              name: true,
              email: true,
              image: true,
            },
          },
        },
      });

      // Store mentions from the comment content
      if (content) {
        try {
          await storeMentionsForComment(
            comment.id,
            taskId,
            task.workspaceId,
            currentUser.id,
            content
          );
        } catch (error) {
          console.error('Failed to store mentions for comment:', error);
          // Don't fail the comment creation if mentions fail
        }
      }

      return c.json({ data: comment });
    }
  )
  .post(
    "/bulk-update",
    zValidator(
      "json",
      z.object({
        tasks: z.array(
          z.object({
            id: z.string().optional(),
            $id: z.string().optional(),
            status: z.nativeEnum(TaskStatus),
            position: z.number().int().positive().min(1000).max(1_000_000),
          }).refine(data => data.id || data.$id, {
            message: "Either 'id' or '$id' must be provided",
            path: ["id"],
          })
        ),
      })
    ),
    async (c) => {
      const session = await auth();
      const { tasks } = c.req.valid("json");

      if (!session?.user?.email) {
        return c.json({ error: "Unauthorized" }, 401);
      }

      const currentUser = await prisma.user.findUnique({
        where: { email: session.user.email },
      });

      if (!currentUser) {
        return c.json({ error: "User not found" }, 404);
      }

      // Get all task IDs
      const taskIds = tasks.map(task => (task.id || task.$id) as string);

      // Get all tasks to update
      const tasksToUpdate = await prisma.task.findMany({
        where: {
          id: { in: taskIds }
        }
      });

      // Check if all tasks belong to the same workspace
      const workspaceIds = Array.from(new Set(tasksToUpdate.map(task => task.workspaceId)));

      if (workspaceIds.length !== 1) {
        return c.json({
          error: "All tasks must belong to the same workspace",
        }, 400);
      }

      const workspaceId = workspaceIds[0];

      // Check if user is a member of the workspace
      const member = await prisma.member.findFirst({
        where: {
          workspaceId,
          userId: currentUser.id,
        },
      });

      if (!member) {
        return c.json({ error: "Unauthorized" }, 401);
      }

      // Update all tasks
      const updatedTasks = await Promise.all(
        tasks.map(async (task) => {
          const { status, position } = task;
          const id = task.id || task.$id as string;
          return prisma.task.update({
            where: { id },
            data: {
              status,
              position,
            },
          });
        })
      );

      return c.json({ data: updatedTasks });
    }
  )

  // Update comment endpoint
  .patch(
    "/:taskId/comments/:commentId",
    zValidator("json", z.object({
      content: z.string().min(1),
      attachments: z.array(z.string()).optional().default([])
    })),
    async (c) => {
      const session = await auth();
      const { taskId, commentId } = c.req.param();
      const { content, attachments } = c.req.valid("json");

      if (!session?.user?.email) {
        return c.json({ error: "Unauthorized" }, 401);
      }

      const currentUser = await prisma.user.findUnique({
        where: { email: session.user.email },
      });

      if (!currentUser) {
        return c.json({ error: "User not found" }, 404);
      }

      // Get the comment and check ownership
      const comment = await prisma.comment.findUnique({
        where: { id: commentId },
        include: { task: { select: { workspaceId: true } } },
      });

      if (!comment) {
        return c.json({ error: "Comment not found" }, 404);
      }

      if (comment.authorId !== currentUser.id) {
        return c.json({ error: "Not authorized to edit this comment" }, 403);
      }

      if (comment.taskId !== taskId) {
        return c.json({ error: "Comment does not belong to this task" }, 400);
      }

      // Check if user is a member of the workspace
      const member = await prisma.member.findFirst({
        where: {
          workspaceId: comment.task.workspaceId,
          userId: currentUser.id,
        },
      });

      if (!member) {
        return c.json({ error: "Unauthorized" }, 401);
      }

      // Update the comment
      const updatedComment = await prisma.comment.update({
        where: { id: commentId },
        data: {
          content,
          attachments: attachments || [],
        },
        include: {
          author: {
            select: {
              id: true,
              name: true,
              email: true,
              image: true,
            },
          },
        },
      });

      // Store mentions from the updated comment content
      if (content) {
        try {
          await storeMentionsForComment(
            commentId,
            taskId,
            comment.task.workspaceId,
            currentUser.id,
            content
          );
        } catch (error) {
          console.error('Failed to store mentions for comment update:', error);
          // Don't fail the comment update if mentions fail
        }
      }

      return c.json({ data: updatedComment });
    }
  )

  // Delete comment endpoint
  .delete(
    "/:taskId/comments/:commentId",
    async (c) => {
      const session = await auth();
      const { taskId, commentId } = c.req.param();

      if (!session?.user?.email) {
        return c.json({ error: "Unauthorized" }, 401);
      }

      const currentUser = await prisma.user.findUnique({
        where: { email: session.user.email },
      });

      if (!currentUser) {
        return c.json({ error: "User not found" }, 404);
      }

      // Get the comment and check ownership
      const comment = await prisma.comment.findUnique({
        where: { id: commentId },
        include: { task: { select: { workspaceId: true } } },
      });

      if (!comment) {
        return c.json({ error: "Comment not found" }, 404);
      }

      if (comment.authorId !== currentUser.id) {
        return c.json({ error: "Not authorized to delete this comment" }, 403);
      }

      if (comment.taskId !== taskId) {
        return c.json({ error: "Comment does not belong to this task" }, 400);
      }

      // Check if user is a member of the workspace
      const member = await prisma.member.findFirst({
        where: {
          workspaceId: comment.task.workspaceId,
          userId: currentUser.id,
        },
      });

      if (!member) {
        return c.json({ error: "Unauthorized" }, 401);
      }

      // Delete the comment
      await prisma.comment.delete({
        where: { id: commentId },
      });

      return c.json({ data: { id: commentId } });
    }
  );

export default app;
