import { and, eq } from 'drizzle-orm';

import { db } from '@/lib/db';
import { tags } from '@/lib/db/schema';
import { createAuditLog, type AuditMeta } from '@/lib/audit';
import { ConflictError, NotFoundError } from '@/lib/errors';
import { emitToRoom } from '@/lib/socket/server';
import type { CreateTagInput, UpdateTagInput } from '@/lib/validators/tags';

export async function listTags(portId: string) {
  return db.select().from(tags).where(eq(tags.portId, portId)).orderBy(tags.name);
}

export async function createTag(portId: string, data: CreateTagInput, meta: AuditMeta) {
  // Enforce unique (portId, name)
  const existing = await db.query.tags.findFirst({
    where: and(eq(tags.portId, portId), eq(tags.name, data.name)),
  });
  if (existing) {
    throw new ConflictError(`A tag named "${data.name}" already exists in this port`);
  }

  const [tag] = await db
    .insert(tags)
    .values({ portId, name: data.name, color: data.color })
    .returning();

  void createAuditLog({
    userId: meta.userId,
    portId,
    action: 'create',
    entityType: 'tag',
    entityId: tag!.id,
    newValue: { name: tag!.name, color: tag!.color },
    ipAddress: meta.ipAddress,
    userAgent: meta.userAgent,
  });

  emitToRoom(`port:${portId}`, 'system:alert', {
    alertType: 'tag:created',
    message: `Tag "${tag!.name}" created`,
    severity: 'info',
  });

  return tag!;
}

export async function updateTag(id: string, portId: string, data: UpdateTagInput, meta: AuditMeta) {
  const tag = await db.query.tags.findFirst({
    where: and(eq(tags.id, id), eq(tags.portId, portId)),
  });
  if (!tag) throw new NotFoundError('Tag');

  // Check name uniqueness if name is being changed
  if (data.name && data.name !== tag.name) {
    const conflict = await db.query.tags.findFirst({
      where: and(eq(tags.portId, portId), eq(tags.name, data.name)),
    });
    if (conflict) {
      throw new ConflictError(`A tag named "${data.name}" already exists in this port`);
    }
  }

  const [updated] = await db
    .update(tags)
    .set({
      ...(data.name ? { name: data.name } : {}),
      ...(data.color ? { color: data.color } : {}),
    })
    .where(and(eq(tags.id, id), eq(tags.portId, portId)))
    .returning();

  void createAuditLog({
    userId: meta.userId,
    portId,
    action: 'update',
    entityType: 'tag',
    entityId: id,
    oldValue: { name: tag.name, color: tag.color },
    newValue: { name: updated!.name, color: updated!.color },
    ipAddress: meta.ipAddress,
    userAgent: meta.userAgent,
  });

  emitToRoom(`port:${portId}`, 'system:alert', {
    alertType: 'tag:updated',
    message: `Tag "${updated!.name}" updated`,
    severity: 'info',
  });

  return updated!;
}

export async function deleteTag(id: string, portId: string, meta: AuditMeta) {
  const tag = await db.query.tags.findFirst({
    where: and(eq(tags.id, id), eq(tags.portId, portId)),
  });
  if (!tag) throw new NotFoundError('Tag');

  await db.delete(tags).where(and(eq(tags.id, id), eq(tags.portId, portId)));

  void createAuditLog({
    userId: meta.userId,
    portId,
    action: 'delete',
    entityType: 'tag',
    entityId: id,
    oldValue: { name: tag.name, color: tag.color },
    ipAddress: meta.ipAddress,
    userAgent: meta.userAgent,
  });

  emitToRoom(`port:${portId}`, 'system:alert', {
    alertType: 'tag:deleted',
    message: `Tag "${tag.name}" deleted`,
    severity: 'info',
  });
}
