import { Prisma } from '@prisma/client';
import {
  ConflictException,
  Injectable,
  NotFoundException,
} from '@nestjs/common';
import { PrismaService } from '../prisma/prisma.service';
import { CreateTagDto } from './dto/create-tag.dto';
import { UpdateTagDto } from './dto/update-tag.dto';
import { ReorderTagsDto } from './dto/reorder-tags.dto';

@Injectable()
export class TagsService {
  constructor(private readonly prisma: PrismaService) {}

  findAll() {
    return this.prisma.tag.findMany({
      orderBy: [{ tagGroupId: 'asc' }, { sortOrder: 'asc' }, { id: 'asc' }],
      include: { tagGroup: true },
    });
  }

  async findOne(id: bigint) {
    const t = await this.prisma.tag.findUnique({
      where: { id },
      include: { tagGroup: true },
    });
    if (!t) throw new NotFoundException(`Tag ${id} not found`);
    return t;
  }

  async create(dto: CreateTagDto) {
    try {
      return await this.prisma.tag.create({
        data: {
          tagName: dto.tagName,
          tagColor: dto.tagColor ?? null,
          tagFontColor: dto.tagFontColor ?? null,
          timing: dto.timing ?? null,
          tagGroupId: dto.tagGroupId ? BigInt(dto.tagGroupId) : null,
          sortOrder: dto.sortOrder ?? 0,
        },
      });
    } catch (err) {
      if (
        err instanceof Prisma.PrismaClientKnownRequestError &&
        err.code === 'P2002'
      ) {
        throw new ConflictException('Tag name already exists');
      }
      throw err;
    }
  }

  async update(id: bigint, dto: UpdateTagDto) {
    await this.findOne(id);
    const data: Prisma.TagUpdateInput = {};
    if (dto.tagName !== undefined) data.tagName = dto.tagName;
    if (dto.tagColor !== undefined) data.tagColor = dto.tagColor;
    if (dto.tagFontColor !== undefined) data.tagFontColor = dto.tagFontColor;
    if (dto.timing !== undefined) data.timing = dto.timing;
    if (dto.tagGroupId !== undefined) {
      data.tagGroup = dto.tagGroupId === null
        ? { disconnect: true }
        : { connect: { id: BigInt(dto.tagGroupId) } };
    }
    if (dto.sortOrder !== undefined) data.sortOrder = dto.sortOrder;
    try {
      return await this.prisma.tag.update({ where: { id }, data });
    } catch (err) {
      if (
        err instanceof Prisma.PrismaClientKnownRequestError &&
        err.code === 'P2002'
      ) {
        throw new ConflictException('Tag name already exists');
      }
      throw err;
    }
  }

  async remove(id: bigint) {
    await this.findOne(id);
    return this.prisma.tag.delete({ where: { id } });
  }

  async reorder(dto: ReorderTagsDto) {
    return this.prisma.$transaction(
      dto.items.map((item) =>
        this.prisma.tag.update({
          where: { id: BigInt(item.id) },
          data: {
            sortOrder: item.sortOrder,
            tagGroupId:
              item.tagGroupId === undefined
                ? undefined
                : item.tagGroupId === null
                  ? null
                  : BigInt(item.tagGroupId),
          },
        }),
      ),
    );
  }
}
