import { client } from "../discord"
import { getPRByGiteaId, insertComment } from "./db"
import { getConfig } from "../config"
import { ChannelType, TextChannel } from "discord.js"
import { convertUsername } from "../gitea"

export async function getThread(pullRequest: { id: number; number: number }) {
  const row = getPRByGiteaId(pullRequest.id)
  if (!row) return

  const thread = await client.channels.fetch(row.discord_thread_id)
  if (!thread) return

  if (!thread.isThread()) {
    throw new Error(`Discord channel ${row.discord_thread_id} is not a thread for PR #${pullRequest.number}`)
  }

  return thread
}

export async function getWebhook() {
  const channel = await getChannel()
  const webhooks = await channel.fetchWebhooks()
  let webhook = webhooks.find((wh) => wh.owner?.id === client.user?.id)

  if (!webhook) {
    webhook = await channel.createWebhook({
      name: "Gitea Bridge",
      reason: "Proxy Gitea comments to Discord",
    })
  }

  return webhook
}

export async function getDiscordAvatarUrl(discordUsername: string) {
  const discordUser = await getDiscordUser(discordUsername)
  return discordUser?.avatarURL() || undefined
}

export async function getDiscordUser(discordUsername: string) {
  const cachedUser = client.users.cache.find((user) => user.username === discordUsername)
  if (cachedUser) return cachedUser

  const channel = await getChannel()
  const members = await channel.guild.members.fetch({ query: discordUsername, limit: 1 })
  const member = members.first()
  return member?.user ?? undefined
}

export async function convertDiscordMentionsToGitea(content: string): Promise<string> {
  const mentionRegex = /<@(\d+)>/g
  const mentions = [...content.matchAll(mentionRegex)]

  let result = content
  for (const match of mentions) {
    const userId = match[1]!
    const user = await client.users.fetch(userId).catch(() => null)
    if (!user) continue

    const giteaUsername = convertUsername({ discordUsername: user.displayName })
    result = result.replace(match[0], `@${giteaUsername}`)
  }

  return result
}

// --- Gitea API call + DB insert (bridge between Discord messages and Gitea comments) ---

const giteaUrl = "https://git.nose.space"

type CreateCommentResponse = {
  id: number
  body: string
  user: { login: string }
  created_at: string
  updated_at: string
}

export async function createPRComment(
  prData: { gitea_pr_id: number; repo: string; pr_number: number; discord_thread_id: string },
  body: string,
  discordMessageId: string
): Promise<CreateCommentResponse> {
  const [owner, repo] = prData.repo.split("/")
  if (!owner) {
    throw new Error(`Could not determine owner from repo string: ${prData.repo}`)
  }
  if (!repo) {
    throw new Error(`Could not determine repo from repo string: ${prData.repo}`)
  }

  const url = `${giteaUrl}/api/v1/repos/${owner}/${repo}/issues/${prData.pr_number}/comments`

  const response = await fetch(url, {
    method: "POST",
    headers: {
      Authorization: `token ${process.env.GITEA_API_TOKEN}`,
      "Content-Type": "application/json",
    },
    body: JSON.stringify({ body }),
  })

  if (!response.ok) {
    throw new Error(`Gitea API error: ${response.status} ${response.statusText}`)
  }

  const comment = (await response.json()) as CreateCommentResponse

  // Store the mapping immediately so webhook handler knows this came from Discord
  insertComment(comment.id, discordMessageId, prData.discord_thread_id)

  return comment
}

// --- Private ---

let channel: TextChannel | undefined
async function getChannel(): Promise<TextChannel> {
  if (channel) return channel

  const channelId = getConfig("channelId")
  const foundChannel = await client.channels.fetch(channelId)
  if (foundChannel?.type !== ChannelType.GuildText) {
    throw new Error(
      `Discord channel ${channelId} (from config.ts) is type ${foundChannel?.type}, expected GuildText (0)`
    )
  }

  channel = foundChannel as TextChannel
  return channel
}
