import { Injectable, NotFoundException } from "@nestjs/common";
import { Prisma, UserLayout } from "@prisma/client";
import { PrismaService } from "../prisma/prisma.service";
import type { CreateLayoutDto, UpdateLayoutDto } from "./dto";

/**
 * Service for managing user layouts
 */
@Injectable()
export class LayoutsService {
  constructor(private readonly prisma: PrismaService) {}

  /**
   * Get all layouts for a user
   */
  async findAll(workspaceId: string, userId: string): Promise<UserLayout[]> {
    return this.prisma.userLayout.findMany({
      where: {
        workspaceId,
        userId,
      },
      orderBy: {
        isDefault: "desc",
        createdAt: "desc",
      },
    });
  }

  /**
   * Get the default layout for a user
   */
  async findDefault(workspaceId: string, userId: string): Promise<UserLayout> {
    const layout = await this.prisma.userLayout.findFirst({
      where: {
        workspaceId,
        userId,
        isDefault: true,
      },
    });

    // If no default layout exists, return the most recently created one
    if (!layout) {
      const recentLayout = await this.prisma.userLayout.findFirst({
        where: {
          workspaceId,
          userId,
        },
        orderBy: {
          createdAt: "desc",
        },
      });

      if (!recentLayout) {
        throw new NotFoundException(`No layouts found for this user`);
      }

      return recentLayout;
    }

    return layout;
  }

  /**
   * Get a single layout by ID
   */
  async findOne(id: string, workspaceId: string, userId: string): Promise<UserLayout> {
    const layout = await this.prisma.userLayout.findUnique({
      where: {
        id,
        workspaceId,
        userId,
      },
    });

    if (!layout) {
      throw new NotFoundException(`Layout with ID ${id} not found`);
    }

    return layout;
  }

  /**
   * Create a new layout
   */
  async create(
    workspaceId: string,
    userId: string,
    createLayoutDto: CreateLayoutDto
  ): Promise<UserLayout> {
    // Use transaction to ensure atomicity when setting default
    return this.prisma.$transaction(async (tx) => {
      // If setting as default, unset other defaults first
      if (createLayoutDto.isDefault) {
        await tx.userLayout.updateMany({
          where: {
            workspaceId,
            userId,
            isDefault: true,
          },
          data: {
            isDefault: false,
          },
        });
      }

      return tx.userLayout.create({
        data: {
          name: createLayoutDto.name,
          workspaceId,
          userId,
          isDefault: createLayoutDto.isDefault ?? false,
          layout: createLayoutDto.layout as unknown as Prisma.InputJsonValue,
        },
      });
    });
  }

  /**
   * Update a layout
   */
  async update(
    id: string,
    workspaceId: string,
    userId: string,
    updateLayoutDto: UpdateLayoutDto
  ): Promise<UserLayout> {
    // Use transaction to ensure atomicity when setting default
    return this.prisma.$transaction(async (tx) => {
      // Verify layout exists
      const existingLayout = await tx.userLayout.findUnique({
        where: { id, workspaceId, userId },
      });

      if (!existingLayout) {
        throw new NotFoundException(`Layout with ID ${id} not found`);
      }

      // If setting as default, unset other defaults first
      if (updateLayoutDto.isDefault === true) {
        await tx.userLayout.updateMany({
          where: {
            workspaceId,
            userId,
            id: { not: id },
            isDefault: true,
          },
          data: {
            isDefault: false,
          },
        });
      }

      // Build update data, handling layout field separately
      const updateData: Prisma.UserLayoutUpdateInput = {};
      if (updateLayoutDto.name !== undefined) updateData.name = updateLayoutDto.name;
      if (updateLayoutDto.isDefault !== undefined) updateData.isDefault = updateLayoutDto.isDefault;
      if (updateLayoutDto.layout !== undefined) {
        updateData.layout = updateLayoutDto.layout as unknown as Prisma.InputJsonValue;
      }

      return tx.userLayout.update({
        where: {
          id,
          workspaceId,
          userId,
        },
        data: updateData,
      });
    });
  }

  /**
   * Delete a layout
   */
  async remove(id: string, workspaceId: string, userId: string): Promise<void> {
    // Verify layout exists
    const layout = await this.prisma.userLayout.findUnique({
      where: { id, workspaceId, userId },
    });

    if (!layout) {
      throw new NotFoundException(`Layout with ID ${id} not found`);
    }

    await this.prisma.userLayout.delete({
      where: {
        id,
        workspaceId,
        userId,
      },
    });
  }
}
