import { z } from "zod";
import { Hono } from "hono";
import { zValidator } from "@hono/zod-validator";
import { endOfMonth, startOfMonth, subMonths } from "date-fns";
import { auth } from "@/auth";
import { prisma } from "@/lib/prisma";
import { TaskStatus } from "@prisma/client";

import { createProjectSchema, updateProjectSchema } from "../schemas";

// Helper function to safely check if value is a File
const isFile = (value: unknown): value is File => {
  if (typeof File === 'undefined') {
    // During build or in environments without File API
    return value && typeof value === 'object' && value !== null && 'arrayBuffer' in value;
  }
  return value instanceof File;
};

const app = new Hono()
  .post(
    "/",
    zValidator("form", createProjectSchema),
    async (c) => {
      const session = await auth();
      const { name, image, workspaceId } = c.req.valid("form");

      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);
      }

      // Handle image upload
      let uploadedImageUrl = "";
      
      if (isFile(image)) {
        // Convert file to base64 for storage
        const buffer = await image.arrayBuffer();
        const base64 = Buffer.from(buffer).toString('base64');
        uploadedImageUrl = `data:${image.type};base64,${base64}`;
      } else if (typeof image === "string" && image) {
        uploadedImageUrl = image;
      }

      // Create the project
      const project = await prisma.project.create({
        data: {
          name,
          imageUrl: uploadedImageUrl,
          workspaceId,
        },
      });

      return c.json({ data: project });
    }
  )
  .get(
    "/",
    zValidator("query", z.object({ workspaceId: z.string() })),
    async (c) => {
      const session = await auth();
      const { workspaceId } = 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);
      }

      // Get all projects for the workspace
      const projects = await prisma.project.findMany({
        where: { workspaceId },
        orderBy: { createdAt: 'desc' },
      });

      return c.json({ 
        data: {
          documents: projects,
          total: projects.length
        } 
      });
    }
  )
  .get("/:projectId", async (c) => {
    const session = await auth();
    const { projectId } = 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 project
    const project = await prisma.project.findUnique({
      where: { id: projectId },
    });

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

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

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

    return c.json({ data: project });
  })
  .patch(
    "/:projectId",
    zValidator("form", updateProjectSchema),
    async (c) => {
      const session = await auth();
      const { projectId } = c.req.param();
      const { name, image } = c.req.valid("form");

      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 project
      const existingProject = await prisma.project.findUnique({
        where: { id: projectId },
      });

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

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

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

      // Handle image upload
      let uploadedImageUrl = existingProject.imageUrl;
      
      if (isFile(image)) {
        // Convert file to base64 for storage
        const buffer = await image.arrayBuffer();
        const base64 = Buffer.from(buffer).toString('base64');
        uploadedImageUrl = `data:${image.type};base64,${base64}`;
      } else if (typeof image === "string" && image) {
        uploadedImageUrl = image;
      }

      // Update the project
      const project = await prisma.project.update({
        where: { id: projectId },
        data: {
          name: name || existingProject.name,
          imageUrl: uploadedImageUrl,
        },
      });

      return c.json({ data: project });
    }
  )
  .delete("/:projectId", async (c) => {
    const session = await auth();
    const { projectId } = 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 existing project
    const existingProject = await prisma.project.findUnique({
      where: { id: projectId },
    });

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

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

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

    // Delete the project and its tasks
    await prisma.$transaction([
      prisma.task.deleteMany({
        where: { projectId },
      }),
      prisma.project.delete({
        where: { id: projectId },
      }),
    ]);

    return c.json({ data: { id: existingProject.id } });
  })
  .get("/:projectId/labels", async (c) => {
    const session = await auth();
    const { projectId } = 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 project
    const project = await prisma.project.findUnique({
      where: { id: projectId },
    });

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

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

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

    // Get all unique labels from project tasks
    const tasks = await prisma.task.findMany({
      where: { projectId },
      select: { labels: true },
    });

    // Extract unique labels
    const allLabels = tasks.flatMap(task => task.labels || []);
    const uniqueLabels = Array.from(new Set(allLabels)).sort();

    return c.json({ data: uniqueLabels });
  })
  .get("/:projectId/analytics", async (c) => {
    const session = await auth();
    const { projectId } = 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 project
    const project = await prisma.project.findUnique({
      where: { id: projectId },
    });

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

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

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

    const now = new Date();
    const thisMonthStart = startOfMonth(now);
    const thisMonthEnd = endOfMonth(now);
    const lastMonthStart = startOfMonth(subMonths(now, 1));
    const lastMonthEnd = endOfMonth(subMonths(now, 1));

    // This month tasks
    const thisMonthTasks = await prisma.task.count({
      where: {
        projectId,
        createdAt: {
          gte: thisMonthStart,
          lte: thisMonthEnd,
        },
      },
    });

    // Last month tasks
    const lastMonthTasks = await prisma.task.count({
      where: {
        projectId,
        createdAt: {
          gte: lastMonthStart,
          lte: lastMonthEnd,
        },
      },
    });

    const taskCount = thisMonthTasks;
    const taskDifference = taskCount - lastMonthTasks;

    // This month assigned tasks
    const thisMonthAssignedTasks = await prisma.task.count({
      where: {
        projectId,
        assigneeId: member.id,
        createdAt: {
          gte: thisMonthStart,
          lte: thisMonthEnd,
        },
      },
    });

    // Last month assigned tasks
    const lastMonthAssignedTasks = await prisma.task.count({
      where: {
        projectId,
        assigneeId: member.id,
        createdAt: {
          gte: lastMonthStart,
          lte: lastMonthEnd,
        },
      },
    });

    const assignedTaskCount = thisMonthAssignedTasks;
    const assignedTaskDifference = assignedTaskCount - lastMonthAssignedTasks;

    // This month completed tasks
    const thisMonthCompletedTasks = await prisma.task.count({
      where: {
        projectId,
        status: TaskStatus.DONE,
        createdAt: {
          gte: thisMonthStart,
          lte: thisMonthEnd,
        },
      },
    });

    // Last month completed tasks
    const lastMonthCompletedTasks = await prisma.task.count({
      where: {
        projectId,
        status: TaskStatus.DONE,
        createdAt: {
          gte: lastMonthStart,
          lte: lastMonthEnd,
        },
      },
    });

    const completedTaskCount = thisMonthCompletedTasks;
    const completedTaskDifference = completedTaskCount - lastMonthCompletedTasks;

    // This month overdue tasks
    const thisMonthOverdueTasks = await prisma.task.count({
      where: {
        projectId,
        dueDate: {
          lt: new Date(),
        },
        status: {
          not: TaskStatus.DONE,
        },
      },
    });

    // Last month overdue tasks
    const lastMonthOverdueTasks = await prisma.task.count({
      where: {
        projectId,
        dueDate: {
          lt: lastMonthEnd,
        },
        status: {
          not: TaskStatus.DONE,
        },
      },
    });

    const overdueTaskCount = thisMonthOverdueTasks;
    const overdueTaskDifference = overdueTaskCount - lastMonthOverdueTasks;

    return c.json({
      data: {
        taskCount,
        taskDifference,
        assignedTaskCount,
        assignedTaskDifference,
        completedTaskCount,
        completedTaskDifference,
        overdueTaskCount,
        overdueTaskDifference,
      },
    });
  });

export default app;
