import { AuthProvider, AuthProviderState } from "./AuthProvider";
import prisma from "@/prisma";

export type GitHubAccessTokenResponse = {
  access_token: string;
  scope: string;
  token_type: string;
};

type GitHubUserResponse = {
  login: string;
  id: number;
  avatar_url: string;
  email: string;
};

async function getMembers(): Promise<GitHubUserResponse[]> {
  const req = await fetch(
    `https://api.github.com/orgs/${process.env.GITHUB_ORG}/members`,
    {
      headers: {
        Authorization: `Bearer ${process.env.GITHUB_TOKEN}`
      }
    }
  );
  return await req.json();
}

export class GitHubAuthProvider extends AuthProvider {
  private async getMe(): Promise<GitHubUserResponse> {
    const req = await fetch("https://api.github.com/user", {
      headers: {
        Authorization: `Bearer ${this.accessToken}`
      }
    });
    const res: GitHubUserResponse = await req.json();
    return res;
  }

  async isPermitted(): Promise<boolean> {
    const me = await this.getMe();
    const members = await getMembers();
    return members.some((user) => user.login === me.login);
  }

  async getDisplayName(): Promise<string> {
    const me = await this.getMe();
    return me.login;
  }

  async getUsername(): Promise<string> {
    return this.getDisplayName();
  }

  async getId(): Promise<string> {
    const me = await this.getMe();
    return me.id.toString();
  }

  async getAvatar(): Promise<string | null> {
    const me = await this.getMe();
    return me.avatar_url;
  }

  async getEmail(): Promise<string | null> {
    const me = await this.getMe();
    return me.email;
  }

  async getState(): Promise<AuthProviderState> {
    const username = await this.getUsername();
    const id = await this.getId();

    return {
      name: "GitHub",
      connected: true,
      id,
      username
    };
  }

  static get redirectUri(): string {
    return `${process.env.BASE_DOMAIN}oauth/github/redirect`;
  }

  static async getToken(
    code: string
  ): Promise<GitHubAccessTokenResponse | null> {
    const query = new URLSearchParams();
    query.set("client_id", process.env.GITHUB_CLIENT_ID);
    query.set("client_secret", process.env.GITHUB_CLIENT_SECRET);
    query.set("code", code);
    query.set("redirect_uri", this.redirectUri);

    const tokenUrl = `https://github.com/login/oauth/access_token?${query.toString()}`;
    const tokenResponse = await fetch(tokenUrl, {
      method: "POST",
      headers: {
        Accept: "application/json"
      }
    });

    if (!tokenResponse.ok) return null;
    return await tokenResponse.json();
  }
  static async update(
    id: string,
    accessToken: string,
    userId?: number
  ): Promise<number> {
    const a = await prisma.gitHubAuth.upsert({
      where: {
        id
      },
      create: {
        id,
        accessToken,
        user:
          userId != null
            ? { connect: { id: userId } }
            : { create: { username: null } },
        invalid: false
      },
      update: {
        accessToken,
        invalid: false
      }
    });

    await prisma.user.update({
      where: {
        id: a.userId
      },
      data: {
        githubAuth: {
          connect: {
            id
          }
        }
      }
    });

    return a.userId;
  }
}

export async function inviteToGitHub(auth: GitHubAuthProvider) {
  const id = await auth.getId();
  const members = await getMembers();
  if (members.find((x) => x.id === parseInt(id))) return;

  await fetch(
    `https://api.github.com/orgs/${process.env.GITHUB_ORG}/invitations`,
    {
      method: "POST",
      headers: {
        Authorization: `Bearer ${process.env.GITHUB_TOKEN}`,
        Accept: "application/vnd.github.v3+json"
      },
      body: JSON.stringify({
        invitee_id: parseInt(id),
        role: "direct_member"
      })
    }
  );
}
