import crypto from "node:crypto";
import type { Db } from "@paperclipai/db";
import { secretService } from "./secrets.js";

const GOOGLE_AUTH_URL = "https://accounts.google.com/o/oauth2/v2/auth";
const GOOGLE_TOKEN_URL = "https://oauth2.googleapis.com/token";
const GEMINI_CLIENT_ID =
  "812546505895-ag9nvbqvf8cpqk3mfem1glig0jtl5i31.apps.googleusercontent.com";
const GEMINI_SCOPES =
  "openid https://www.googleapis.com/auth/cloud-platform https://www.googleapis.com/auth/generative-language.retriever";
const GOOGLE_TOKEN_SECRET_NAME = "google_gemini_oauth_token";

export interface GoogleTokens {
  accessToken: string;
  refreshToken?: string;
}

export function googleOAuthService(db: Db) {
  const svc = secretService(db);

  function generatePkce(): { verifier: string; challenge: string } {
    const verifier = crypto.randomBytes(32).toString("base64url");
    const challenge = crypto.createHash("sha256").update(verifier).digest("base64url");
    return { verifier, challenge };
  }

  function generateAuthUrl(
    redirectUri: string,
    state: string,
  ): { url: string; verifier: string } {
    const { verifier, challenge } = generatePkce();
    const params = new URLSearchParams({
      client_id: GEMINI_CLIENT_ID,
      redirect_uri: redirectUri,
      response_type: "code",
      scope: GEMINI_SCOPES,
      code_challenge: challenge,
      code_challenge_method: "S256",
      state,
      access_type: "offline",
      prompt: "consent",
    });
    const url = `${GOOGLE_AUTH_URL}?${params.toString()}`;
    return { url, verifier };
  }

  async function exchangeCode(
    code: string,
    redirectUri: string,
    verifier: string,
  ): Promise<{ accessToken: string; refreshToken?: string; expiresIn?: number }> {
    const body = new URLSearchParams({
      grant_type: "authorization_code",
      client_id: GEMINI_CLIENT_ID,
      code,
      redirect_uri: redirectUri,
      code_verifier: verifier,
    });

    const response = await fetch(GOOGLE_TOKEN_URL, {
      method: "POST",
      headers: { "Content-Type": "application/x-www-form-urlencoded" },
      body: body.toString(),
    });

    if (!response.ok) {
      const errorText = await response.text();
      throw new Error(`Google token exchange failed: ${response.status} ${errorText}`);
    }

    const data = (await response.json()) as {
      access_token: string;
      refresh_token?: string;
      expires_in?: number;
    };

    return {
      accessToken: data.access_token,
      refreshToken: data.refresh_token,
      expiresIn: data.expires_in,
    };
  }

  async function storeTokens(companyId: string, tokens: GoogleTokens): Promise<void> {
    const value = JSON.stringify(tokens);
    const existing = await svc.getByName(companyId, GOOGLE_TOKEN_SECRET_NAME);
    if (existing) {
      await svc.rotate(existing.id, { value });
    } else {
      await svc.create(companyId, {
        name: GOOGLE_TOKEN_SECRET_NAME,
        provider: "local_encrypted",
        value,
        description: "Google OAuth tokens for Gemini API access",
      });
    }
  }

  async function resolveTokens(companyId: string): Promise<GoogleTokens> {
    const secret = await svc.getByName(companyId, GOOGLE_TOKEN_SECRET_NAME);
    if (!secret) {
      throw new Error("Google OAuth tokens not found for this company");
    }
    const value = await svc.resolveSecretValue(companyId, secret.id, "latest");
    return JSON.parse(value) as GoogleTokens;
  }

  return {
    generatePkce,
    generateAuthUrl,
    exchangeCode,
    storeTokens,
    resolveTokens,
  };
}
