import { describe, test, expect, beforeAll, afterAll, setDefaultTimeout } from "bun:test"

setDefaultTimeout(30_000)
import { ensure } from "@workshop/shared/utils"
import {
  type User,
  setupWebhooks,
  expectPullRequestWebhook,
  expectIssueCommentWebhook,
  expectPullRequestCommentWebhook,
  openTestPR,
  commentOnPR,
  createReview,
  mergePR,
} from "./helpers"

function testBranch(label: string) {
  return `test-${crypto.randomUUID().slice(0, 8)}-${label}`
}

function getEnv(name: string): string {
  const value = process.env[name]
  ensure(value, `Missing env var: ${name}. See .env.example`)
  return value
}

const corey: User = { token: getEnv("TEST_GITEA_API_TOKEN_COREY"), username: "probablycorey" }
const spike: User = { token: getEnv("TEST_GITEA_API_TOKEN_SPIKE"), username: "Spike" }
const coreyRepo = getEnv("TEST_REPO_COREY")
const spikeRepo = getEnv("TEST_REPO_SPIKE")

let teardown: () => void

beforeAll(async () => {
  teardown = await setupWebhooks(corey, spike, coreyRepo, spikeRepo)
})

afterAll(() => teardown())

// --- Tests for Corey's repo (Spike opens PR, Corey comments/reviews/merges) ---

describe(`${coreyRepo}`, () => {
  test("opening a PR sends pull_request webhook with correct author and repo", async () => {
    const branch = testBranch("open")
    await expectPullRequestWebhook({ action: "opened", branch }, async (webhook) => {
      await openTestPR(coreyRepo, spike, branch)
      const payload = await webhook
      expect(payload.pull_request.user.login).toBe(spike.username)
      expect(payload.repository.full_name).toBe(coreyRepo)
    })
  })

  test("commenting sends issue_comment webhook with correct user", async () => {
    const branch = testBranch("comment")
    const pr = await openTestPR(coreyRepo, spike, branch)
    await expectIssueCommentWebhook({ action: "created", username: corey.username, prNumber: pr.number }, async (webhook) => {
      await commentOnPR(coreyRepo, pr.number, "Looks good!", corey)
      const payload = await webhook
      expect(payload.issue.number).toBe(pr.number)
    })
  })

  test("review with line comment sends pull_request_comment webhook", async () => {
    const branch = testBranch("review")
    const pr = await openTestPR(coreyRepo, spike, branch)
    await expectPullRequestCommentWebhook({ action: "reviewed", branch }, async (webhook) => {
      await createReview(coreyRepo, pr.number, corey, pr.filename)
      const payload = await webhook
      expect(payload.sender.login).toBe(corey.username)
      expect(payload.review).toBeDefined()
    })
  })

  test("merging sends pull_request closed webhook", async () => {
    const branch = testBranch("merge")
    const pr = await openTestPR(coreyRepo, spike, branch)
    await expectPullRequestWebhook({ action: "closed", branch }, async (webhook) => {
      await mergePR(coreyRepo, pr.number, corey)
      const payload = await webhook
      expect(payload.pull_request.merged).toBe(true)
    })
  })
})

// --- Tests for Spike's repo (Corey opens PR, Spike comments/reviews/merges) ---

describe(`${spikeRepo}`, () => {
  test("opening a PR sends pull_request webhook with correct author and repo", async () => {
    const branch = testBranch("open")
    await expectPullRequestWebhook({ action: "opened", branch }, async (webhook) => {
      await openTestPR(spikeRepo, corey, branch)
      const payload = await webhook
      expect(payload.pull_request.user.login).toBe(corey.username)
      expect(payload.repository.full_name).toBe(spikeRepo)
    })
  })

  test("commenting sends issue_comment webhook with correct user", async () => {
    const branch = testBranch("comment")
    const pr = await openTestPR(spikeRepo, corey, branch)
    await expectIssueCommentWebhook({ action: "created", username: spike.username, prNumber: pr.number }, async (webhook) => {
      await commentOnPR(spikeRepo, pr.number, "On it!", spike)
      const payload = await webhook
      expect(payload.issue.number).toBe(pr.number)
    })
  })

  test("review with line comment sends pull_request_comment webhook", async () => {
    const branch = testBranch("review")
    const pr = await openTestPR(spikeRepo, corey, branch)
    await expectPullRequestCommentWebhook({ action: "reviewed", branch }, async (webhook) => {
      await createReview(spikeRepo, pr.number, spike, pr.filename)
      const payload = await webhook
      expect(payload.sender.login).toBe(spike.username)
      expect(payload.review).toBeDefined()
    })
  })

  test("merging sends pull_request closed webhook", async () => {
    const branch = testBranch("merge")
    const pr = await openTestPR(spikeRepo, corey, branch)
    await expectPullRequestWebhook({ action: "closed", branch }, async (webhook) => {
      await mergePR(spikeRepo, pr.number, spike)
      const payload = await webhook
      expect(payload.pull_request.merged).toBe(true)
    })
  })
})
