import {
  buildTimelineToken,
  decodeJetfuel,
  decodeTimelineToken,
  extractTimelineTokens,
} from "../parsers/jetfuel.js";
import parseTimeline from "../parsers/timeline.js";

export const ENGAGEMENTS = ["Likes", "Replies", "Bookmarks", "Quotes", "VideoQualityViews"];

export const PERIODS = ["Daily", "Weekly", "Monthly"];

export const COUNTRIES = [
  "All", "USA", "CAN", "GBR", "AUS", "DEU", "FRA", "JPN", "BRA", "IND", "MEX",
  "ESP", "ITA", "KOR", "NLD", "ARG", "POL", "TUR", "IDN", "SAU", "ZAF", "NGA",
  "PHL", "THA", "VNM", "EGY", "PAK", "MYS", "SGP", "ARE", "COL", "CHL", "SWE",
  "NOR", "DNK", "FIN", "BEL", "AUT", "CHE", "PRT", "GRC", "IRL", "NZL", "ISR",
  "RUS", "UKR", "CZE", "ROU", "HUN",
];

function assertEnum(value, allowed, label) {
  if (!allowed.includes(value)) {
    throw new Error(`invalid ${label} "${value}", expected one of: ${allowed.join(", ")}`);
  }
}

export async function page(route, opts = {}) {
  const res = await this.jf(route, opts);
  return { ...decodeJetfuel(res.buffer), status: res.status, contentType: res.contentType };
}

export async function remote(route, opts = {}) {
  return await page.call(this, route, opts);
}

export async function raw(route, opts = {}) {
  return await this.jf(route, opts);
}

export function decode(buffer) {
  return decodeJetfuel(buffer);
}

export function topPostsTimelineId({ engagement = "Likes", period = "Daily", country = "All" } = {}) {
  assertEnum(engagement, ENGAGEMENTS, "engagement");
  assertEnum(period, PERIODS, "period");
  return buildTimelineToken({ engagement, country, period });
}

export async function topPostsFeed({ engagement = "Likes", period = "Daily", country = "All" } = {}) {
  assertEnum(engagement, ENGAGEMENTS, "engagement");
  assertEnum(period, PERIODS, "period");

  const params = { engagement, period };
  if (country && country !== "All") params.country = country;

  const res = await this.jf("creators/inspiration/remote/urt", { params });
  const tokens = extractTimelineTokens(res.buffer);
  const token = tokens[0] || null;

  return {
    status: res.status,
    token,
    params: token ? decodeTimelineToken(token) : null,
    decoded: decodeJetfuel(res.buffer),
  };
}

export async function topPosts(opts = {}) {
  const { engagement = "Likes", period = "Daily", country = "All", count = 20, cursor, viaRemote, raw: returnRaw } = opts;
  assertEnum(engagement, ENGAGEMENTS, "engagement");
  assertEnum(period, PERIODS, "period");

  let timelineId;
  if (viaRemote) {
    const feed = await topPostsFeed.call(this, { engagement, period, country });
    if (!feed.token) throw new Error("inspiration feed returned no timeline token");
    timelineId = feed.token;
  } else {
    timelineId = buildTimelineToken({ engagement, country, period });
  }

  const variables = {
    timelineId,
    count,
    withQuickPromoteEligibilityTweetFields: true,
  };
  if (cursor) variables.cursor = cursor;

  const res = await this.graphql("GenericTimelineById", { variables });
  if (returnRaw) return res;

  const parsed = parseTimeline(res);
  return { ...parsed, engagement, period, country, timelineId };
}

export async function inspirationPage(opts = {}) {
  return await page.call(this, "creators/inspiration/top_posts", opts);
}

export async function stories(opts = {}) {
  return await page.call(this, "stories/home", opts);
}

export async function storiesRemote({ category } = {}, opts = {}) {
  const params = { ...opts.params };
  if (category) params.category = category;
  return await remote.call(this, "stories/storiesRemote", { ...opts, params });
}

export async function newsArticle(id, opts = {}) {
  if (!id) throw new Error("newsArticle requires an article id");
  return await page.call(this, `news/article/id/${id}`, opts);
}

export const SPORT_EVENTS = {
  nba: { noun: "game", home: true },
  nfl: { noun: "game", home: true },
  f1: { noun: "race", home: true },
  nhl: { noun: "game", home: false },
  soccer: { noun: "match", home: false },
};

export async function sportHome(sport, opts = {}) {
  if (!sport) throw new Error("sportHome requires a sport");
  return await page.call(this, `${sport}/home`, opts);
}

export async function league(sport, opts = {}) {
  if (!sport) throw new Error("league requires a sport");
  return await page.call(this, `${sport}/league/home`, opts);
}

export async function game({ sport, id, noun, home } = {}, opts = {}) {
  if (!sport || !id) throw new Error("game requires { sport, id }");
  const shape = SPORT_EVENTS[sport] || {};
  const eventNoun = noun ?? shape.noun ?? "game";
  const hasHome = home ?? shape.home ?? false;
  const route = `${sport}/${eventNoun}${hasHome ? "/home" : ""}/id/${id}`;
  return await page.call(this, route, opts);
}

export async function event(id, opts = {}) {
  if (!id) throw new Error("event requires an id");
  return await page.call(this, "events/event", { ...opts, params: { id, ...opts.params } });
}

export async function brackets(opts = {}) {
  return await page.call(this, "brackets/home", opts);
}

export async function bracketView(userId, opts = {}) {
  return await page.call(this, userId ? `brackets/view/id/${userId}` : "brackets/view", opts);
}

export async function creatorsStudio(opts = {}) {
  return await page.call(this, "creators/studio", opts);
}

export async function onboardingTopics(opts = {}) {
  return await remote.call(this, "onboarding/remotes/topics", opts);
}

export async function health(opts = {}) {
  const res = await this.jf("health", { ...opts, origin: "jf" });
  return res.text();
}

export function ogImageUrl(tweetId) {
  return `https://jf.x.com/images/post/${tweetId}`;
}

export function mediaPreviewUrl(tweetId) {
  return `https://jf.x.com/images/media-preview/${tweetId}`;
}
