import {
  ActionRowBuilder,
  ButtonBuilder,
  ButtonStyle,
  ChannelSelectMenuBuilder,
  ContainerBuilder,
  MediaGalleryBuilder,
  MediaGalleryItemBuilder,
  MentionableSelectMenuBuilder,
  MessageFlags,
  RoleSelectMenuBuilder,
  SectionBuilder,
  SeparatorBuilder,
  SeparatorSpacingSize,
  StringSelectMenuBuilder,
  TextDisplayBuilder,
  ThumbnailBuilder,
  UserSelectMenuBuilder,
  type MessageActionRowComponentBuilder,
  type MessageCreateOptions
} from 'discord.js';
import {
  encodeComponentCustomId,
  mapComponentsV2TextFields,
  MessageComponentsV2Schema,
  type ComponentButton,
  type ComponentButtonStyle,
  type ComponentSelect,
  type ComponentV2Node,
  type ComponentV2Source,
  type MessageComponentsV2,
  type SeparatorSpacing
} from '@nexumi/shared';

function isHttpUrl(value: string | undefined): value is string {
  if (!value) {
    return false;
  }
  try {
    const url = new URL(value);
    return url.protocol === 'http:' || url.protocol === 'https:';
  } catch {
    return false;
  }
}

const BUTTON_STYLE_MAP: Record<ComponentButtonStyle, ButtonStyle> = {
  Primary: ButtonStyle.Primary,
  Secondary: ButtonStyle.Secondary,
  Success: ButtonStyle.Success,
  Danger: ButtonStyle.Danger,
  Link: ButtonStyle.Link
};

const SPACING_MAP: Record<SeparatorSpacing, SeparatorSpacingSize> = {
  Small: SeparatorSpacingSize.Small,
  Large: SeparatorSpacingSize.Large
};

export interface BuildComponentsV2Options {
  source: ComponentV2Source;
  ref: string;
  renderText?: (value: string) => string;
}

function buildButton(
  button: ComponentButton,
  options: BuildComponentsV2Options
): ButtonBuilder | null {
  const builder = new ButtonBuilder()
    .setLabel(button.label.slice(0, 80))
    .setDisabled(Boolean(button.disabled));

  if (button.emoji) {
    builder.setEmoji(button.emoji);
  }

  if (button.style === 'Link') {
    if (!isHttpUrl(button.url)) {
      return null;
    }
    builder.setStyle(ButtonStyle.Link).setURL(button.url);
    return builder;
  }

  if (!button.actionId) {
    return null;
  }

  builder
    .setStyle(BUTTON_STYLE_MAP[button.style] ?? ButtonStyle.Secondary)
    .setCustomId(encodeComponentCustomId(options.source, options.ref, button.actionId));
  return builder;
}

function buildSelect(
  select: ComponentSelect,
  options: BuildComponentsV2Options
): MessageActionRowComponentBuilder | null {
  const placeholder = select.placeholder?.slice(0, 150);
  const minValues = select.minValues;
  const maxValues = select.maxValues;

  if (select.type === 'string_select') {
    const menu = new StringSelectMenuBuilder()
      .setCustomId(
        encodeComponentCustomId(
          options.source,
          options.ref,
          select.actionId ?? select.id ?? 'select'
        )
      )
      .setDisabled(Boolean(select.disabled))
      .addOptions(
        select.options.slice(0, 25).map((option) => {
          const entry: {
            label: string;
            value: string;
            description?: string;
            emoji?: string;
          } = {
            label: option.label.slice(0, 100),
            value: option.value.slice(0, 100)
          };
          if (option.description) {
            entry.description = option.description.slice(0, 100);
          }
          if (option.emoji) {
            entry.emoji = option.emoji;
          }
          return entry;
        })
      );
    if (placeholder) {
      menu.setPlaceholder(placeholder);
    }
    if (minValues !== undefined) {
      menu.setMinValues(minValues);
    }
    if (maxValues !== undefined) {
      menu.setMaxValues(maxValues);
    }
    return menu;
  }

  const customId = encodeComponentCustomId(options.source, options.ref, select.actionId);
  let menu:
    | UserSelectMenuBuilder
    | RoleSelectMenuBuilder
    | ChannelSelectMenuBuilder
    | MentionableSelectMenuBuilder;

  switch (select.type) {
    case 'user_select':
      menu = new UserSelectMenuBuilder().setCustomId(customId);
      break;
    case 'role_select':
      menu = new RoleSelectMenuBuilder().setCustomId(customId);
      break;
    case 'channel_select':
      menu = new ChannelSelectMenuBuilder().setCustomId(customId);
      break;
    case 'mentionable_select':
      menu = new MentionableSelectMenuBuilder().setCustomId(customId);
      break;
    default:
      return null;
  }

  menu.setDisabled(Boolean(select.disabled));
  if (placeholder) {
    menu.setPlaceholder(placeholder);
  }
  if (minValues !== undefined) {
    menu.setMinValues(minValues);
  }
  if (maxValues !== undefined) {
    menu.setMaxValues(maxValues);
  }
  return menu;
}

function buildThumbnail(url: string, description?: string, spoiler?: boolean): ThumbnailBuilder | null {
  if (!isHttpUrl(url)) {
    return null;
  }
  const thumb = new ThumbnailBuilder().setURL(url);
  if (description) {
    thumb.setDescription(description.slice(0, 1024));
  }
  if (spoiler) {
    thumb.setSpoiler(true);
  }
  return thumb;
}

type TopLevelBuilder =
  | ContainerBuilder
  | TextDisplayBuilder
  | SeparatorBuilder
  | MediaGalleryBuilder
  | SectionBuilder
  | ActionRowBuilder<MessageActionRowComponentBuilder>;

function buildNode(node: ComponentV2Node, options: BuildComponentsV2Options): TopLevelBuilder | null {
  switch (node.type) {
    case 'text_display':
      return new TextDisplayBuilder().setContent(node.content.slice(0, 4000));
    case 'separator': {
      const sep = new SeparatorBuilder();
      if (node.divider !== undefined) {
        sep.setDivider(node.divider);
      }
      if (node.spacing) {
        sep.setSpacing(SPACING_MAP[node.spacing]);
      }
      return sep;
    }
    case 'media_gallery': {
      const gallery = new MediaGalleryBuilder();
      for (const item of node.items) {
        if (!isHttpUrl(item.url)) {
          continue;
        }
        const media = new MediaGalleryItemBuilder().setURL(item.url);
        if (item.description) {
          media.setDescription(item.description.slice(0, 1024));
        }
        if (item.spoiler) {
          media.setSpoiler(true);
        }
        gallery.addItems(media);
      }
      return gallery;
    }
    case 'section': {
      const section = new SectionBuilder();
      const texts = Array.isArray(node.text) ? node.text : [node.text];
      for (const text of texts.slice(0, 3)) {
        section.addTextDisplayComponents(new TextDisplayBuilder().setContent(text.slice(0, 4000)));
      }
      if (node.accessory.type === 'thumbnail') {
        const thumb = buildThumbnail(
          node.accessory.url,
          node.accessory.description,
          node.accessory.spoiler
        );
        if (thumb) {
          section.setThumbnailAccessory(thumb);
        }
      } else {
        const button = buildButton(node.accessory, options);
        if (button) {
          section.setButtonAccessory(button);
        }
      }
      return section;
    }
    case 'action_row': {
      const row = new ActionRowBuilder<MessageActionRowComponentBuilder>();
      for (const child of node.components) {
        if (child.type === 'button') {
          const button = buildButton(child, options);
          if (button) {
            row.addComponents(button);
          }
        } else {
          const select = buildSelect(child, options);
          if (select) {
            row.addComponents(select);
          }
        }
      }
      return row.components.length > 0 ? row : null;
    }
    case 'button': {
      const button = buildButton(node, options);
      if (!button) {
        return null;
      }
      return new ActionRowBuilder<MessageActionRowComponentBuilder>().addComponents(button);
    }
    case 'string_select':
    case 'user_select':
    case 'role_select':
    case 'channel_select':
    case 'mentionable_select': {
      const select = buildSelect(node, options);
      if (!select) {
        return null;
      }
      return new ActionRowBuilder<MessageActionRowComponentBuilder>().addComponents(select);
    }
    case 'thumbnail':
      // Thumbnails must be section accessories; skip top-level.
      return null;
    case 'container': {
      const container = new ContainerBuilder();
      if (node.accentColor !== undefined) {
        container.setAccentColor(node.accentColor);
      }
      if (node.spoiler) {
        container.setSpoiler(true);
      }
      for (const child of node.components) {
        switch (child.type) {
          case 'text_display':
            container.addTextDisplayComponents(
              new TextDisplayBuilder().setContent(child.content.slice(0, 4000))
            );
            break;
          case 'separator': {
            const sep = new SeparatorBuilder();
            if (child.divider !== undefined) {
              sep.setDivider(child.divider);
            }
            if (child.spacing) {
              sep.setSpacing(SPACING_MAP[child.spacing]);
            }
            container.addSeparatorComponents(sep);
            break;
          }
          case 'media_gallery': {
            const gallery = new MediaGalleryBuilder();
            for (const item of child.items) {
              if (!isHttpUrl(item.url)) {
                continue;
              }
              const media = new MediaGalleryItemBuilder().setURL(item.url);
              if (item.description) {
                media.setDescription(item.description.slice(0, 1024));
              }
              if (item.spoiler) {
                media.setSpoiler(true);
              }
              gallery.addItems(media);
            }
            container.addMediaGalleryComponents(gallery);
            break;
          }
          case 'section': {
            const section = buildNode(child, options);
            if (section instanceof SectionBuilder) {
              container.addSectionComponents(section);
            }
            break;
          }
          case 'action_row':
          case 'button':
          case 'string_select':
          case 'user_select':
          case 'role_select':
          case 'channel_select':
          case 'mentionable_select': {
            const row = buildNode(child, options);
            if (row instanceof ActionRowBuilder) {
              container.addActionRowComponents(row);
            }
            break;
          }
          case 'container':
            for (const nested of child.components) {
              const sibling = buildNode(nested, options);
              if (sibling instanceof TextDisplayBuilder) {
                container.addTextDisplayComponents(sibling);
              } else if (sibling instanceof SeparatorBuilder) {
                container.addSeparatorComponents(sibling);
              } else if (sibling instanceof MediaGalleryBuilder) {
                container.addMediaGalleryComponents(sibling);
              } else if (sibling instanceof SectionBuilder) {
                container.addSectionComponents(sibling);
              } else if (sibling instanceof ActionRowBuilder) {
                container.addActionRowComponents(sibling);
              }
            }
            break;
          default:
            break;
        }
      }
      return container;
    }
    default:
      return null;
  }
}

/**
 * Builds a discord.js Components V2 message payload from shared JSON.
 * Sets MessageFlags.IsComponentsV2; never includes content/embeds.
 */
export function buildComponentsV2Payload(
  raw: MessageComponentsV2 | null | undefined,
  options: BuildComponentsV2Options
): MessageCreateOptions | null {
  const parsed = MessageComponentsV2Schema.safeParse(raw);
  if (!parsed.success) {
    return null;
  }

  const data = options.renderText
    ? mapComponentsV2TextFields(parsed.data, options.renderText)
    : parsed.data;

  const components: TopLevelBuilder[] = [];
  for (const node of data.components) {
    const built = buildNode(node, options);
    if (built) {
      components.push(built);
    }
  }

  if (components.length === 0) {
    return null;
  }

  return {
    components,
    flags: MessageFlags.IsComponentsV2
  };
}

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