import { userNickname } from "../auth0-mgmt.js"
import { getInstallationOctokitByOwner, isUserCollaborator } from "../github/github-utils.js"
import { githubApp } from "../github/github-app.js"
import { Request, Response } from "express"
import { AuthorizedUserReq } from "../types.js"
import { logger } from "../utils/logger.js"
import {
  missingRequiredFields,
  validationFailure,
  unauthorized,
  githubInstallationError,
  githubNotCollaborator,
  githubInstallationNotFound,
  internalServerError,
} from "../exceptions/index.js"

// Dependencies interface for easier testing
export interface IsGitHubAppInstalledDependencies {
  userNickname: typeof userNickname
  getInstallationOctokitByOwner: typeof getInstallationOctokitByOwner
  isUserCollaborator: typeof isUserCollaborator
  githubApp: typeof githubApp
}

// Default dependencies
let dependencies: IsGitHubAppInstalledDependencies = {
  userNickname,
  getInstallationOctokitByOwner,
  isUserCollaborator,
  githubApp,
}

// For testing - allow dependency injection
export function setIsGitHubAppInstalledDependencies(
  deps: Partial<IsGitHubAppInstalledDependencies>,
) {
  dependencies = { ...dependencies, ...deps }
}

export function resetIsGitHubAppInstalledDependencies() {
  dependencies = {
    userNickname,
    getInstallationOctokitByOwner,
    isUserCollaborator,
    githubApp,
  }
}

export async function isGitHubAppInstalled(req: Request, res: Response): Promise<void> {
  const { owner, repo } = req.query

  if (!owner || !repo) {
    throw missingRequiredFields("owner, repo")
  }

  const ownerStr = String(owner).trim()
  const repoStr = String(repo).trim()

  if (ownerStr === "" || repoStr === "") {
    throw validationFailure("owner and repo cannot be empty")
  }

  try {
    const nickname = await dependencies.userNickname((req as AuthorizedUserReq).userId)
    if (nickname == null) {
      throw unauthorized("")
    }

    const installationOctokit = await dependencies.getInstallationOctokitByOwner(
      dependencies.githubApp,
      ownerStr,
      repoStr,
      (req as AuthorizedUserReq).userId,
    )

    if (installationOctokit instanceof Error) {
      throw githubInstallationError(installationOctokit.message)
    }

    const isCollaborator = await dependencies.isUserCollaborator(
      installationOctokit,
      ownerStr,
      repoStr,
      nickname,
    )

    if (!isCollaborator) {
      throw githubNotCollaborator(`${ownerStr}/${repoStr}`)
    }

    logger.info("GitHub App installation and collaborator status verified", req, {
      repo: `${ownerStr}/${repoStr}`,
      nickname,
    })

    res.json(true)
  } catch (error: any) {
    if (error && typeof error === "object" && "getHttpStatus" in error) {
      throw error
    }
    if (error.status === 404) {
      logger.warn("GitHub App not installed on repository", req, {
        repo: `${ownerStr}/${repoStr}`,
      })
      throw githubInstallationNotFound(`${ownerStr}/${repoStr}`)
    }

    logger.errorWithSentry(
      "Error checking GitHub App installation or collaborator status:",
      req,
      { repo: `${ownerStr}/${repoStr}` },
      error,
    )
    throw internalServerError("Error checking GitHub App installation or collaborator status")
  }
}
