import {
  MessageComponentsV2Schema,
  WelcomeEmbedSchema,
  type MessageComponentsV2,
  type TagDashboard,
  type TagDashboardCreate,
  type TagDashboardUpdate,
  type WelcomeEmbed
} from '@nexumi/shared';
import { Prisma, type Tag } from '@prisma/client';
import { prisma } from '../prisma';
import { assertPremiumLimit, PremiumLimitError } from '../premium';

export class TagConflictError extends Error {
  constructor() {
    super('A tag with this name already exists');
  }
}

export class TagPremiumError extends Error {
  constructor(public readonly code: 'premium_limit' | 'premium_feature') {
    super(code);
  }
}

function parseEmbed(raw: unknown): WelcomeEmbed | null {
  const parsed = WelcomeEmbedSchema.safeParse(raw);
  return parsed.success ? parsed.data : null;
}

function parseComponents(raw: unknown): MessageComponentsV2 | null {
  const parsed = MessageComponentsV2Schema.safeParse(raw);
  return parsed.success ? parsed.data : null;
}

function toDashboard(tag: Tag): TagDashboard {
  return {
    id: tag.id,
    name: tag.name,
    content: tag.content,
    embed: parseEmbed(tag.embed),
    components: parseComponents(tag.components),
    responseType: tag.responseType as TagDashboard['responseType'],
    triggerWord: tag.triggerWord,
    cooldownSeconds: tag.cooldownSeconds,
    allowedRoleIds: tag.allowedRoleIds,
    allowedChannelIds: tag.allowedChannelIds
  };
}

export async function listTags(guildId: string): Promise<TagDashboard[]> {
  const tags = await prisma.tag.findMany({ where: { guildId }, orderBy: { name: 'asc' } });
  return tags.map(toDashboard);
}

export async function createTag(
  guildId: string,
  createdById: string,
  input: TagDashboardCreate
): Promise<TagDashboard> {
  await prisma.guild.upsert({ where: { id: guildId }, update: {}, create: { id: guildId } });
  const currentCount = await prisma.tag.count({ where: { guildId } });
  try {
    await assertPremiumLimit(prisma, guildId, 'tags', currentCount);
  } catch (error) {
    if (error instanceof PremiumLimitError) {
      throw new TagPremiumError(error.code === 'feature_disabled' ? 'premium_feature' : 'premium_limit');
    }
    throw error;
  }
  try {
    const tag = await prisma.tag.create({
      data: {
        guildId,
        name: input.name,
        content: input.content ?? null,
        embed:
          input.embed === undefined || input.embed === null
            ? Prisma.JsonNull
            : (input.embed as Prisma.InputJsonValue),
        components:
          input.components === undefined || input.components === null
            ? Prisma.JsonNull
            : (input.components as Prisma.InputJsonValue),
        responseType: input.responseType,
        triggerWord: input.triggerWord ?? null,
        cooldownSeconds: input.cooldownSeconds ?? 15,
        allowedRoleIds: input.allowedRoleIds,
        allowedChannelIds: input.allowedChannelIds,
        createdById
      }
    });
    return toDashboard(tag);
  } catch (error) {
    if (error && typeof error === 'object' && 'code' in error && error.code === 'P2002') {
      throw new TagConflictError();
    }
    throw error;
  }
}

export async function updateTag(
  guildId: string,
  tagId: string,
  patch: TagDashboardUpdate
): Promise<TagDashboard | null> {
  const existing = await prisma.tag.findFirst({ where: { id: tagId, guildId } });
  if (!existing) {
    return null;
  }

  try {
    const updated = await prisma.tag.update({
      where: { id: tagId },
      data: {
        ...(patch.name !== undefined ? { name: patch.name } : {}),
        ...(patch.content !== undefined ? { content: patch.content } : {}),
        ...(patch.embed !== undefined
          ? { embed: patch.embed === null ? Prisma.JsonNull : (patch.embed as Prisma.InputJsonValue) }
          : {}),
        ...(patch.components !== undefined
          ? {
              components:
                patch.components === null ? Prisma.JsonNull : (patch.components as Prisma.InputJsonValue)
            }
          : {}),
        ...(patch.responseType !== undefined ? { responseType: patch.responseType } : {}),
        ...(patch.triggerWord !== undefined ? { triggerWord: patch.triggerWord } : {}),
        ...(patch.cooldownSeconds !== undefined ? { cooldownSeconds: patch.cooldownSeconds } : {}),
        ...(patch.allowedRoleIds !== undefined ? { allowedRoleIds: patch.allowedRoleIds } : {}),
        ...(patch.allowedChannelIds !== undefined ? { allowedChannelIds: patch.allowedChannelIds } : {})
      }
    });
    return toDashboard(updated);
  } catch (error) {
    if (error && typeof error === 'object' && 'code' in error && error.code === 'P2002') {
      throw new TagConflictError();
    }
    throw error;
  }
}

export async function deleteTag(guildId: string, tagId: string): Promise<boolean> {
  const result = await prisma.tag.deleteMany({ where: { id: tagId, guildId } });
  return result.count > 0;
}
