const { getSetting, setSetting } = require("./settings");
const { incrementMessages } = require("./stats");
const { ensureUserForIdentity } = require("./users");
const { createLogger } = require("./logger");
const { publishOverlayChatMessage } = require("./overlay-chat");

const youtubeLog = createLogger("platform:youtube", { category: "integration" });

let client = null;
let pollTimer = null;

async function startYouTubeBot({ commandRouter } = {}) {
  const clientId = getSetting("youtube_client_id");
  const clientSecret = getSetting("youtube_client_secret");
  const refreshToken = getSetting("youtube_bot_refresh_token");
  if (!clientId || !clientSecret || !refreshToken) {
    return null;
  }

  const state = {
    clientId,
    clientSecret,
    refreshToken,
    accessToken: null,
    accessTokenExpiresAt: 0,
    channelId: getSetting("youtube_bot_channel_id", "") || null,
    channelName: null,
    channelAvatar: null,
    liveChatId: null,
    activeVideoId: null,
    liveStateUpdatedAt: 0,
    nextPageToken: null,
    skipHistory: true,
    stopped: false,
    commandRouter
  };
  state.sendMessage = async (content) => {
    const liveChatId = await ensureLiveChatId(state);
    if (!liveChatId) {
      throw new Error("No active YouTube live chat is available.");
    }
    return sendChatMessage(state, liveChatId, content);
  };

  client = state;

  try {
    await hydrateBotChannel(state);
  } catch (error) {
    youtubeLog.error("YouTube bot failed to load channel details", error, { event: "channel_load_failed" });
  }

  schedulePoll(state, 1000);
  return state;
}

async function stopYouTubeBot() {
  if (pollTimer) {
    clearTimeout(pollTimer);
    pollTimer = null;
  }
  if (client) {
    client.stopped = true;
  }
  client = null;
}

function getClient() {
  return client;
}

function schedulePoll(state, delayMs) {
  if (state.stopped) {
    return;
  }
  if (pollTimer) {
    clearTimeout(pollTimer);
  }
  pollTimer = setTimeout(() => {
    pollTimer = null;
    pollLiveChat(state).catch((error) => {
      youtubeLog.error("YouTube chat poll failed", error, { event: "poll_failed" });
      schedulePoll(state, 10000);
    });
  }, delayMs);
}

async function pollLiveChat(state) {
  if (state.stopped) {
    return;
  }
  const liveChatId = await ensureLiveChatId(state);
  if (!liveChatId) {
    schedulePoll(state, 30000);
    return;
  }

  const response = await listLiveChatMessages(state, liveChatId, state.nextPageToken);
  state.nextPageToken = response.nextPageToken || state.nextPageToken;
  if (state.skipHistory) {
    state.skipHistory = false;
    schedulePoll(state, response.pollingIntervalMillis || 5000);
    return;
  }

  const items = Array.isArray(response.items) ? response.items : [];
  for (const item of items) {
    await handleChatItem(state, liveChatId, item);
  }

  schedulePoll(state, response.pollingIntervalMillis || 5000);
}

async function handleChatItem(state, liveChatId, item) {
  const snippet = item?.snippet;
  const author = item?.authorDetails;
  if (!snippet || !author) {
    return;
  }
  const messageText = snippet.displayMessage;
  if (!messageText) {
    return;
  }
  const displayName = author.displayName || "YouTube User";
  const avatar = author.profileImageUrl || null;
  const isSelf = Boolean(state.channelId && author.channelId === state.channelId);
  const profile = isSelf ? null : ensureUserForIdentity({
    provider: "youtube",
    providerUserId: author.channelId,
    displayName,
    avatar
  });
  publishOverlayChatMessage({
    id: item.id,
    platform: "youtube",
    text: messageText,
    timestamp: Date.parse(snippet.publishedAt) || Date.now(),
    channel: {
      id: liveChatId,
      name: state.channelName || state.activeVideoId || "Live chat",
      key: state.activeVideoId || liveChatId
    },
    author: {
      id: author.channelId,
      name: displayName,
      username: displayName,
      avatar,
      badges: [author.isChatOwner && "Owner", author.isChatModerator && "Moderator", author.isChatSponsor && "Member"].filter(Boolean),
      lumi: profile ? { id: profile.id, username: profile.internal_username } : null
    }
  });
  if (isSelf) return;
  incrementMessages(profile.id);

  if (!state.commandRouter) {
    return;
  }

  await state.commandRouter.handleMessage({
    platform: "youtube",
    raw: messageText,
    user: profile,
    platformUser: {
      id: author.channelId,
      displayName,
      username: displayName
    },
    meta: {
      liveChatId,
      activeVideoId: state.activeVideoId,
      broadcasterChannelId: state.channelId,
      messageId: item.id,
      snippet,
      author
    },
    reply: async (content) => {
      try {
        await sendChatMessage(state, liveChatId, content);
      } catch (error) {
        youtubeLog.error("YouTube command reply failed", error, { event: "reply_failed" });
      }
    }
  });
}

async function ensureLiveChatId(state) {
  const cacheExpired = Date.now() - (state.liveStateUpdatedAt || 0) >= 60 * 1000;
  let liveChatId = state.liveChatId;
  if (!liveChatId || cacheExpired) {
    const broadcast = await findActiveBroadcast(state);
    liveChatId = broadcast?.liveChatId || null;
    state.activeVideoId = broadcast?.videoId || null;
    state.liveStateUpdatedAt = Date.now();
  }
  if (liveChatId && liveChatId !== state.liveChatId) {
    state.liveChatId = liveChatId;
    state.nextPageToken = null;
    state.skipHistory = true;
  } else if (!liveChatId) {
    state.liveChatId = null;
    state.nextPageToken = null;
  }
  return state.liveChatId;
}

async function hydrateBotChannel(state) {
  const channel = await fetchMyChannel(state);
  if (!channel) {
    return;
  }
  state.channelId = channel.id;
  state.channelName = channel.snippet?.title || null;
  state.channelAvatar = channel.snippet?.thumbnails?.default?.url || null;
  if (state.channelId) {
    setSetting("youtube_bot_channel_id", state.channelId);
  }
}

async function ensureAccessToken(state) {
  const now = Date.now();
  if (state.accessToken && now < state.accessTokenExpiresAt) {
    return state.accessToken;
  }
  const token = await refreshAccessToken(state);
  state.accessToken = token.access_token;
  const expiresIn = Number(token.expires_in || 0);
  state.accessTokenExpiresAt = now + Math.max(30, expiresIn - 60) * 1000;
  return state.accessToken;
}

async function refreshAccessToken(state) {
  const body = new URLSearchParams({
    client_id: state.clientId,
    client_secret: state.clientSecret,
    refresh_token: state.refreshToken,
    grant_type: "refresh_token"
  });
  const response = await fetch("https://oauth2.googleapis.com/token", {
    method: "POST",
    headers: { "Content-Type": "application/x-www-form-urlencoded" },
    body
  });
  if (!response.ok) {
    throw new Error(`YouTube token refresh failed: ${response.status}`);
  }
  return response.json();
}

async function fetchMyChannel(state) {
  const accessToken = await ensureAccessToken(state);
  const response = await fetch(
    "https://www.googleapis.com/youtube/v3/channels?part=snippet&mine=true",
    { headers: { Authorization: `Bearer ${accessToken}` } }
  );
  if (!response.ok) {
    throw new Error(`YouTube channel fetch failed: ${response.status}`);
  }
  const data = await response.json();
  return data.items?.[0] || null;
}

async function findActiveBroadcast(state) {
  const accessToken = await ensureAccessToken(state);
  const response = await fetch(
    "https://www.googleapis.com/youtube/v3/liveBroadcasts?part=snippet,status&broadcastStatus=active&broadcastType=all",
    { headers: { Authorization: `Bearer ${accessToken}` } }
  );
  if (!response.ok) {
    return null;
  }
  const data = await response.json();
  const broadcast = data.items?.[0];
  if (!broadcast) return null;
  return {
    liveChatId: broadcast.snippet?.liveChatId || null,
    videoId: broadcast.id || null
  };
}

async function listLiveChatMessages(state, liveChatId, pageToken) {
  const accessToken = await ensureAccessToken(state);
  const params = new URLSearchParams({
    liveChatId,
    part: "snippet,authorDetails",
    maxResults: "200"
  });
  if (pageToken) {
    params.set("pageToken", pageToken);
  }
  const response = await fetch(
    `https://www.googleapis.com/youtube/v3/liveChatMessages?${params.toString()}`,
    { headers: { Authorization: `Bearer ${accessToken}` } }
  );
  if (!response.ok) {
    throw new Error(`YouTube chat fetch failed: ${response.status}`);
  }
  return response.json();
}

async function sendChatMessage(state, liveChatId, messageText) {
  const accessToken = await ensureAccessToken(state);
  const response = await fetch(
    "https://www.googleapis.com/youtube/v3/liveChatMessages?part=snippet",
    {
      method: "POST",
      headers: {
        Authorization: `Bearer ${accessToken}`,
        "Content-Type": "application/json"
      },
      body: JSON.stringify({
        snippet: {
          liveChatId,
          type: "textMessageEvent",
          textMessageDetails: {
            messageText
          }
        }
      })
    }
  );
  if (!response.ok) {
    throw new Error(`YouTube chat send failed: ${response.status}`);
  }
  return response.json();
}

module.exports = {
  startYouTubeBot,
  stopYouTubeBot,
  getClient
};
