import { decryptToken, encryptToken } from "./crypto";
import { runtimeEnv } from "./db";

export type Platform = "instagram" | "threads";

type TokenResponse = { access_token: string; user_id?: string; expires_in?: number };

async function readMetaResponse(response: Response) {
  const payload = await response.json() as Record<string, unknown>;
  if (!response.ok || payload.error) {
    const detail = payload.error && typeof payload.error === "object"
      ? String((payload.error as Record<string, unknown>).message || "Meta API 오류")
      : String(payload.error_description || payload.error || "Meta API 오류");
    throw new Error(detail);
  }
  return payload;
}

export function oauthConfig(platform: Platform, requestUrl: string) {
  const config = runtimeEnv();
  const origin = config.PUBLIC_APP_URL?.replace(/\/$/, "") || new URL(requestUrl).origin;
  const redirectUri = `${origin}/api/oauth/meta/callback`;
  if (platform === "instagram") {
    return {
      appId: config.INSTAGRAM_APP_ID,
      appSecret: config.INSTAGRAM_APP_SECRET,
      redirectUri,
      authorizeUrl: "https://www.instagram.com/oauth/authorize",
      scope: "instagram_business_basic,instagram_business_content_publish",
    };
  }
  return {
    appId: config.THREADS_APP_ID,
    appSecret: config.THREADS_APP_SECRET,
    redirectUri,
    authorizeUrl: "https://threads.net/oauth/authorize",
    scope: "threads_basic,threads_content_publish",
  };
}

export async function exchangeOAuthCode(platform: Platform, code: string, requestUrl: string) {
  const config = oauthConfig(platform, requestUrl);
  if (!config.appId || !config.appSecret) throw new Error(`${platform} 앱 설정이 필요합니다.`);
  const shortBody = new URLSearchParams({
    client_id: config.appId,
    client_secret: config.appSecret,
    grant_type: "authorization_code",
    redirect_uri: config.redirectUri,
    code,
  });
  const shortEndpoint = platform === "instagram"
    ? "https://api.instagram.com/oauth/access_token"
    : "https://graph.threads.net/oauth/access_token";
  const short = await readMetaResponse(await fetch(shortEndpoint, { method: "POST", body: shortBody })) as TokenResponse;

  const longEndpoint = platform === "instagram"
    ? "https://graph.instagram.com/access_token"
    : "https://graph.threads.net/access_token";
  const params = new URLSearchParams({
    grant_type: platform === "instagram" ? "ig_exchange_token" : "th_exchange_token",
    client_secret: config.appSecret,
    access_token: short.access_token,
  });
  const long = await readMetaResponse(await fetch(`${longEndpoint}?${params}`)) as TokenResponse;
  const token = long.access_token || short.access_token;
  const expiresIn = Number(long.expires_in || short.expires_in || 0);
  const profileUrl = platform === "instagram"
    ? `https://graph.instagram.com/v25.0/me?fields=id,user_id,username,account_type,profile_picture_url&access_token=${encodeURIComponent(token)}`
    : `https://graph.threads.net/v1.0/me?fields=id,username,threads_profile_picture_url,threads_biography&access_token=${encodeURIComponent(token)}`;
  const profile = await readMetaResponse(await fetch(profileUrl));
  const encrypted = await encryptToken(token);
  return {
    ...encrypted,
    platformUserId: String(profile.user_id || profile.id || short.user_id),
    username: String(profile.username || "connected_account"),
    profileImageUrl: String(profile.profile_picture_url || profile.threads_profile_picture_url || "") || null,
    tokenExpiresAt: expiresIn ? Date.now() + expiresIn * 1000 : null,
  };
}

type PublishInput = {
  platform: Platform;
  platformUserId: string;
  tokenEncrypted: string;
  tokenIv: string;
  content: string;
  mediaUrl?: string | null;
  mediaType?: "image" | "video" | null;
  altText?: string | null;
};

async function postMeta(url: string, values: Record<string, string>) {
  const response = await fetch(url, { method: "POST", body: new URLSearchParams(values) });
  return readMetaResponse(response);
}

export async function publishToMeta(input: PublishInput) {
  const accessToken = await decryptToken(input.tokenEncrypted, input.tokenIv);
  if (input.platform === "instagram") {
    if (!input.mediaUrl) throw new Error("Instagram 게시물에는 공개 이미지 또는 영상 URL이 필요합니다.");
    const containerValues: Record<string, string> = { caption: input.content, access_token: accessToken };
    if (input.altText) containerValues.alt_text = input.altText;
    if (input.mediaType === "video") {
      containerValues.media_type = "REELS";
      containerValues.video_url = input.mediaUrl;
    } else {
      containerValues.image_url = input.mediaUrl;
    }
    const container = await postMeta(`https://graph.instagram.com/v25.0/${input.platformUserId}/media`, containerValues);
    const published = await postMeta(`https://graph.instagram.com/v25.0/${input.platformUserId}/media_publish`, {
      creation_id: String(container.id), access_token: accessToken,
    });
    return String(published.id);
  }

  const values: Record<string, string> = { text: input.content, access_token: accessToken };
  if (!input.mediaUrl) values.media_type = "TEXT";
  else if (input.mediaType === "video") {
    values.media_type = "VIDEO";
    values.video_url = input.mediaUrl;
  } else {
    values.media_type = "IMAGE";
    values.image_url = input.mediaUrl;
  }
  if (input.altText) values.alt_text = input.altText;
  const container = await postMeta(`https://graph.threads.net/v1.0/${input.platformUserId}/threads`, values);
  const published = await postMeta(`https://graph.threads.net/v1.0/${input.platformUserId}/threads_publish`, {
    creation_id: String(container.id), access_token: accessToken,
  });
  return String(published.id);
}
