import { randomBytes } from "crypto";
import { getLinkedInConfig } from "@/lib/env";
import { fetchLinkedInMemberId } from "@/lib/social/linkedin-member";

export interface LinkedInProfile {
  accountId: string;
  accountName: string;
  email?: string;
}

export function createOAuthState(): string {
  return randomBytes(24).toString("hex");
}

export function buildLinkedInAuthUrl(state: string, redirectUri?: string, forceConsent = false): string {
  const { clientId, scopes } = getLinkedInConfig();
  const uri = redirectUri ?? getLinkedInConfig().redirectUri;
  if (!clientId || !uri) {
    throw new Error("LinkedIn OAuth is not configured");
  }

  const params = new URLSearchParams({
    response_type: "code",
    client_id: clientId,
    redirect_uri: uri,
    state,
    scope: scopes,
  });

  // Force LinkedIn to re-approve scopes (required after changing LINKEDIN_SCOPES)
  if (forceConsent) {
    params.set("prompt", "consent");
  }

  return `https://www.linkedin.com/oauth/v2/authorization?${params.toString()}`;
}

export async function exchangeLinkedInCode(
  code: string,
  redirectUri?: string,
): Promise<{
  accessToken: string;
  refreshToken?: string;
  expiresAt?: string;
  profile: LinkedInProfile;
}> {
  const { clientId, clientSecret } = getLinkedInConfig();
  const uri = redirectUri ?? getLinkedInConfig().redirectUri;
  if (!clientId || !clientSecret || !uri) {
    throw new Error("LinkedIn OAuth is not configured");
  }

  const body = new URLSearchParams({
    grant_type: "authorization_code",
    code,
    redirect_uri: uri,
    client_id: clientId,
    client_secret: clientSecret,
  });

  const tokenRes = await fetch("https://www.linkedin.com/oauth/v2/accessToken", {
    method: "POST",
    headers: { "Content-Type": "application/x-www-form-urlencoded" },
    body: body.toString(),
  });

  const tokenData = (await tokenRes.json()) as {
    access_token?: string;
    expires_in?: number;
    refresh_token?: string;
    error_description?: string;
  };

  if (!tokenRes.ok || !tokenData.access_token) {
    throw new Error(tokenData.error_description ?? "LinkedIn token exchange failed");
  }

  const profile = await fetchLinkedInProfile(tokenData.access_token);

  return {
    accessToken: tokenData.access_token,
    refreshToken: tokenData.refresh_token,
    expiresAt: tokenData.expires_in
      ? new Date(Date.now() + tokenData.expires_in * 1000).toISOString()
      : undefined,
    profile,
  };
}

async function fetchLinkedInProfile(accessToken: string): Promise<LinkedInProfile> {
  const auth = { Authorization: `Bearer ${accessToken}` };
  const restli = { ...auth, "X-Restli-Protocol-Version": "2.0.0" };
  let accountId: string | undefined;
  let accountName = "LinkedIn User";
  let email: string | undefined;

  // Legacy tokens (r_liteprofile) — try /v2/me first
  const meRes = await fetch("https://api.linkedin.com/v2/me?projection=(id,localizedFirstName,localizedLastName)", {
    headers: restli,
  });
  if (meRes.ok) {
    const me = (await meRes.json()) as {
      id?: string;
      localizedFirstName?: string;
      localizedLastName?: string;
    };
    accountId = me.id;
    const name = [me.localizedFirstName, me.localizedLastName].filter(Boolean).join(" ");
    if (name) accountName = name;
  }

  // OpenID tokens
  if (!accountId) {
    const userinfoRes = await fetch("https://api.linkedin.com/v2/userinfo", { headers: auth });
    if (userinfoRes.ok) {
      const profile = (await userinfoRes.json()) as {
        sub?: string;
        name?: string;
        email?: string;
      };
      accountId = profile.sub;
      accountName = profile.name ?? profile.email ?? accountName;
      email = profile.email;
    }
  }

  if (!accountId) {
    accountId = await fetchLinkedInMemberId(accessToken);
  }

  if (!accountId) {
    throw new Error(
      "LinkedIn did not return a member profile. " +
        "Set LINKEDIN_SCOPES=r_liteprofile r_emailaddress w_member_social in .env.local, restart the app, " +
        "then use Fix LinkedIn Connection to reconnect and approve all permissions.",
    );
  }

  return { accountId, accountName, email };
}
