import { getLinkedInConfig } from "@/lib/env";
import type { StoredSocialAccount } from "@/lib/storage/social-accounts";
import {
  loadProjectSocialAccounts,
  saveProjectSocialAccounts,
} from "@/lib/storage/social-accounts";
import { refreshYouTubeToken } from "@/lib/social/youtube-oauth";
import { isLinkedInExternalIdValid } from "@/lib/social/linkedin-member";
import { probeLinkedInAccessToken } from "@/lib/social/linkedin-probe";
import { refreshLinkedInAccessToken } from "@/lib/social/linkedin-token";
import { refreshMetaUserToken, resolveMetaConnection } from "@/lib/social/meta-oauth";
import { writePublishLog } from "@/lib/logging/publish-logs";

function isExpired(account: StoredSocialAccount): boolean {
  if (!account.expiresAt) return false;
  return new Date(account.expiresAt).getTime() <= Date.now() + 60_000;
}

async function refreshLinkedInAccount(
  account: StoredSocialAccount,
  projectId: string,
): Promise<StoredSocialAccount> {
  let token = account.accessToken;
  let refreshToken = account.refreshToken;
  let expiresAt = account.expiresAt;

  // Proactively refresh when expired / near-expiry before probing.
  if (refreshToken && (isExpired(account) || !token)) {
    try {
      const refreshed = await refreshLinkedInAccessToken(refreshToken);
      token = refreshed.accessToken;
      refreshToken = refreshed.refreshToken ?? refreshToken;
      expiresAt = refreshed.expiresAt ?? expiresAt;
      await writePublishLog({
        level: "info",
        action: "linkedin_token_refreshed",
        projectId,
        platform: "linkedin",
        message: "LinkedIn access token refreshed successfully.",
      });
    } catch (error) {
      await writePublishLog({
        level: "error",
        action: "linkedin_token_refresh_failed",
        projectId,
        platform: "linkedin",
        message: error instanceof Error ? error.message : "LinkedIn token refresh failed",
      });
      return {
        ...account,
        connectionStatus: "expired",
        updatedAt: new Date().toISOString(),
      };
    }
  }

  if (!token) {
    return {
      ...account,
      connectionStatus: "expired",
      updatedAt: new Date().toISOString(),
    };
  }

  let probe = await probeLinkedInAccessToken(token, account.externalId);

  if (!probe.ok && refreshToken) {
    try {
      const refreshed = await refreshLinkedInAccessToken(refreshToken);
      token = refreshed.accessToken;
      refreshToken = refreshed.refreshToken ?? refreshToken;
      expiresAt = refreshed.expiresAt ?? expiresAt;
      probe = await probeLinkedInAccessToken(token, null);
    } catch (error) {
      await writePublishLog({
        level: "error",
        action: "linkedin_token_refresh_failed",
        projectId,
        platform: "linkedin",
        message: error instanceof Error ? error.message : "LinkedIn token refresh failed",
      });
    }
  }

  if (probe.ok && probe.memberId) {
    return {
      ...account,
      accessToken: token,
      refreshToken,
      expiresAt: expiresAt ?? null,
      externalId: probe.memberId,
      connected: true,
      connectionStatus: "connected",
      updatedAt: new Date().toISOString(),
    };
  }

  await writePublishLog({
    level: "error",
    action: "linkedin_member_id_failed",
    projectId,
    platform: "linkedin",
    message: probe.recommendation,
    details: { attempts: probe.attempts },
  });

  return {
    ...account,
    accessToken: token,
    refreshToken,
    expiresAt: expiresAt ?? null,
    connectionStatus: isExpired({ ...account, expiresAt: expiresAt ?? null })
      ? "expired"
      : "error",
    updatedAt: new Date().toISOString(),
  };
}

async function refreshMetaAccount(
  account: StoredSocialAccount,
  projectId: string,
): Promise<StoredSocialAccount> {
  if (!account.accessToken && !account.publishToken) {
    return { ...account, connectionStatus: "expired", updatedAt: new Date().toISOString() };
  }

  try {
    // Re-extend user token when near expiry, then rebuild page publish token.
    let userToken = account.accessToken;
    let expiresAt = account.expiresAt;

    if (userToken && (isExpired(account) || !account.publishToken)) {
      const refreshed = await refreshMetaUserToken(userToken);
      userToken = refreshed.accessToken;
      if (refreshed.expiresIn) {
        expiresAt = new Date(Date.now() + refreshed.expiresIn * 1000).toISOString();
      }
    }

    if (!userToken) {
      return { ...account, connectionStatus: "expired", updatedAt: new Date().toISOString() };
    }

    const platform = account.platform === "instagram" ? "instagram" : "facebook";
    const resolved = await resolveMetaConnection(userToken, platform);

    await writePublishLog({
      level: "info",
      action: "meta_token_refreshed",
      projectId,
      platform: account.platform,
      message: `${account.platform} token refreshed / page token renewed.`,
    });

    return {
      ...account,
      accessToken: userToken,
      publishToken: resolved.publishToken,
      profileName: resolved.profileName || account.profileName,
      externalId: resolved.externalId || account.externalId,
      expiresAt: expiresAt ?? null,
      connected: true,
      connectionStatus: "connected",
      updatedAt: new Date().toISOString(),
    };
  } catch (error) {
    await writePublishLog({
      level: "error",
      action: "meta_token_refresh_failed",
      projectId,
      platform: account.platform,
      message: error instanceof Error ? error.message : "Meta token refresh failed",
    });
    return {
      ...account,
      connectionStatus: isExpired(account) ? "expired" : "error",
      updatedAt: new Date().toISOString(),
    };
  }
}

export async function ensureFreshAccountTokens(
  userId: string,
  projectId: string,
): Promise<StoredSocialAccount[]> {
  const accounts = await loadProjectSocialAccounts(userId, projectId);
  let changed = false;

  for (let i = 0; i < accounts.length; i++) {
    const account = accounts[i];
    if (!account.connected && account.connectionStatus !== "expired") continue;

    if (account.platform === "linkedin") {
      const updated = await refreshLinkedInAccount(account, projectId);
      if (JSON.stringify(updated) !== JSON.stringify(account)) {
        accounts[i] = updated;
        changed = true;
      }
    } else if (account.platform === "facebook" || account.platform === "instagram") {
      if (isExpired(account) || !account.publishToken || account.connectionStatus === "expired") {
        const updated = await refreshMetaAccount(account, projectId);
        if (JSON.stringify(updated) !== JSON.stringify(account)) {
          accounts[i] = updated;
          changed = true;
        }
      }
    } else if (account.platform === "youtube" && isExpired(account) && account.refreshToken) {
      try {
        const refreshed = await refreshYouTubeToken(account.refreshToken);
        const expiresAt = refreshed.expiresIn
          ? new Date(Date.now() + refreshed.expiresIn * 1000).toISOString()
          : account.expiresAt;
        accounts[i] = {
          ...account,
          accessToken: refreshed.accessToken,
          expiresAt: expiresAt ?? null,
          connected: true,
          connectionStatus: "connected",
          updatedAt: new Date().toISOString(),
        };
        changed = true;
      } catch {
        accounts[i] = { ...account, connectionStatus: "expired" };
        changed = true;
      }
    } else if (isExpired(account)) {
      accounts[i] = { ...account, connectionStatus: "expired" };
      changed = true;
    }
  }

  if (changed) {
    await saveProjectSocialAccounts(userId, projectId, accounts);
  }

  return accounts;
}

export function getLinkedInPublishBlockReason(
  accounts: StoredSocialAccount[],
): string | null {
  const linkedin = accounts.find((a) => a.platform === "linkedin" && a.connected);
  if (!linkedin) return null;
  if (linkedin.connectionStatus === "error") {
    const { scopeValidation } = getLinkedInConfig();
    return (
      "LinkedIn connection is invalid. Disconnect and reconnect in Connected Accounts. " +
      `Server scopes (${scopeValidation.mode}): ${scopeValidation.raw || "not set"}. ` +
      `Expected: ${scopeValidation.envExample}`
    );
  }
  if (linkedin.connectionStatus === "expired") {
    return "LinkedIn token expired. Reconnect in Connected Accounts.";
  }
  if (!isLinkedInExternalIdValid(linkedin.externalId)) {
    return "LinkedIn member ID missing. Reconnect in Connected Accounts.";
  }
  return null;
}
