import { prisma } from "@/lib/db";
import { getAppBaseUrl } from "./config";

async function safeJson<T>(res: Response): Promise<T> {
  try {
    return (await res.json()) as T;
  } catch {
    throw new Error(`Threads API 回應格式錯誤（HTTP ${res.status}）`);
  }
}

const OAUTH_SCOPES = [
  "threads_basic",
  "threads_content_publish",
  "threads_read_replies",
  "threads_manage_replies",
  "threads_manage_insights",
  "threads_keyword_search",
];

export function buildThreadsOAuthUrl(
  request: Request,
  appId: string,
  state?: string,
  appUrlOverride?: string | null
): string {
  const clientId = appId.trim();
  if (!clientId || !/^\d+$/.test(clientId)) {
    throw new Error(
      "Threads App ID 格式不正確。請填 Meta 後台 Basic 裡的「Threads App ID」（純數字），不是上方的一般 App ID。"
    );
  }

  const redirectUri = `${getAppBaseUrl(request, appUrlOverride)}/api/threads/oauth/callback`;
  const url = new URL("https://threads.net/oauth/authorize");
  url.searchParams.set("client_id", clientId);
  url.searchParams.set("redirect_uri", redirectUri);
  url.searchParams.set("scope", OAUTH_SCOPES.join(","));
  url.searchParams.set("response_type", "code");
  url.searchParams.set("state", state?.trim() || "threadtools");
  return url.toString();
}

export async function exchangeCodeForToken(input: {
  appId: string;
  appSecret: string;
  code: string;
  redirectUri: string;
}) {
  const body = new URLSearchParams({
    client_id: input.appId,
    client_secret: input.appSecret,
    grant_type: "authorization_code",
    redirect_uri: input.redirectUri,
    code: input.code.replace(/#_$/, ""),
  });

  const res = await fetch("https://graph.threads.net/oauth/access_token", {
    method: "POST",
    body,
  });

  const json = await safeJson<{
    access_token?: string;
    user_id?: number | string;
    error_message?: string;
  }>(res);

  if (!res.ok || !json.access_token || !json.user_id) {
    throw new Error(json.error_message ?? "無法換取 Threads access token");
  }

  return {
    accessToken: json.access_token,
    userId: String(json.user_id),
  };
}

export async function exchangeForLongLivedToken(appSecret: string, shortLivedToken: string) {
  const url = new URL("https://graph.threads.net/access_token");
  url.searchParams.set("grant_type", "th_exchange_token");
  url.searchParams.set("client_secret", appSecret);
  url.searchParams.set("access_token", shortLivedToken);

  const res = await fetch(url.toString());
  const json = await safeJson<{
    access_token?: string;
    expires_in?: number;
    error_message?: string;
  }>(res);

  if (!res.ok || !json.access_token) {
    throw new Error(json.error_message ?? "無法換取長效 token");
  }

  return {
    accessToken: json.access_token,
    expiresIn: json.expires_in,
  };
}

/** 綁定後抓取該帳號的 Threads 公開資訊（目前只取 username），用來自動命名帳號 */
export async function fetchThreadsProfile(
  accessToken: string
): Promise<{ username?: string }> {
  try {
    const url = new URL("https://graph.threads.net/v1.0/me");
    url.searchParams.set("fields", "username");
    url.searchParams.set("access_token", accessToken);
    const res = await fetch(url.toString());
    if (!res.ok) return {};
    const json = (await res.json()) as { username?: string };
    return { username: json.username };
  } catch {
    return {};
  }
}

export async function saveThreadsAuthForAccount(
  accountId: string,
  data: {
    accessToken: string;
    userId: string;
    expiresIn?: number;
  }
) {
  const expiresAt = data.expiresIn
    ? new Date(Date.now() + data.expiresIn * 1000)
    : null;

  await prisma.account.update({
    where: { id: accountId },
    data: {
      threadsAccessToken: data.accessToken,
      threadsUserId: data.userId,
      threadsTokenExpiresAt: expiresAt,
    },
  });
}

export async function clearThreadsAuthForAccount(accountId: string) {
  await prisma.account.update({
    where: { id: accountId },
    data: {
      threadsAccessToken: null,
      threadsUserId: null,
      threadsTokenExpiresAt: null,
    },
  });
}

export async function refreshLongLivedToken(accessToken: string) {
  const url = new URL("https://graph.threads.net/refresh_access_token");
  url.searchParams.set("grant_type", "th_refresh_token");
  url.searchParams.set("access_token", accessToken);

  const res = await fetch(url.toString());
  const json = await safeJson<{
    access_token?: string;
    expires_in?: number;
    error_message?: string;
  }>(res);

  if (!res.ok || !json.access_token) {
    throw new Error(json.error_message ?? "無法更新 Threads token");
  }

  return {
    accessToken: json.access_token,
    expiresIn: json.expires_in,
  };
}

export async function ensureValidAccessTokenForAccount(account: {
  id: string;
  threadsAccessToken: string | null;
  threadsUserId: string | null;
  threadsTokenExpiresAt: Date | null;
}): Promise<string | null> {
  const token = account.threadsAccessToken?.trim();
  if (!token || !account.threadsUserId?.trim()) return null;

  const expiresAt = account.threadsTokenExpiresAt;
  const needsRefresh =
    !expiresAt || expiresAt.getTime() - Date.now() < 7 * 24 * 60 * 60 * 1000;

  if (!needsRefresh) return token;

  try {
    const refreshed = await refreshLongLivedToken(token);
    await saveThreadsAuthForAccount(account.id, {
      accessToken: refreshed.accessToken,
      userId: account.threadsUserId.trim(),
      expiresIn: refreshed.expiresIn,
    });
    return refreshed.accessToken;
  } catch {
    return token;
  }
}
