import type { GeneratedPost } from "@/types/workflow";
import { AutoPostJobLogger } from "@/lib/logging/auto-post-logger";
import { publishPostToPlatforms } from "@/lib/social/publish-post";
import { updateGeneratedPostPublished } from "@/lib/storage/generated-posts";
import type { ConnectedAccount } from "@/types/workflow";
import type { Project } from "@/types/workflow";
import { markSlotFired } from "./slot-fire-state";

const MAX_RETRIES = 1; // Retries were re-posting LinkedIn when another platform failed.
const RETRY_DELAY_MS = 3000;

function sleep(ms: number) {
  return new Promise((resolve) => setTimeout(resolve, ms));
}

export async function publishPostAtSlot(input: {
  post: GeneratedPost;
  project: Project;
  accounts: ConnectedAccount[];
  slotLabel: string;
  postsPerDay: 1 | 2;
  scheduleSeed: number;
  now: Date;
  source: "queued" | "generated";
  logger?: AutoPostJobLogger;
}): Promise<{ ok: boolean; message: string; platforms?: string }> {
  const { post, project, accounts, slotLabel, postsPerDay, scheduleSeed, now, source, logger } =
    input;
  const log = logger ?? new AutoPostJobLogger();

  let lastError = "Publish failed";
  let publishResult: Awaited<ReturnType<typeof publishPostToPlatforms>> | null = null;

  for (let attempt = 1; attempt <= MAX_RETRIES; attempt++) {
    if (attempt > 1) {
      await log.warn("retry_initiated", `Retry ${attempt}/${MAX_RETRIES} for post "${post.title}".`, {
        projectId: project.id,
        opportunityId: post.id,
        status: "retrying",
        details: { attempt, previousError: lastError },
      });
      await sleep(RETRY_DELAY_MS);
    }

    await log.info("api_request_sent", `Sending publish request (attempt ${attempt}).`, {
      projectId: project.id,
      opportunityId: post.id,
      status: "api_request",
      details: { attempt, source },
    });

    try {
      publishResult = await publishPostToPlatforms({
        post,
        accounts,
        projectTimezone: project.timezone,
        projectCountries: project.countries,
        postsPerDay,
        scheduleSeed,
        scheduleSaved: true,
        forcePublish: true,
        quickPublish: true,
        jobId: log.jobId,
        projectId: project.id,
      });
    } catch (error) {
      lastError = error instanceof Error ? error.message : "Publish threw an exception";
      await log.error("api_request_exception", lastError, {
        projectId: project.id,
        opportunityId: post.id,
        status: "failed",
        error,
        details: { attempt },
      });
      continue;
    }

    await log.info("api_response_received", publishResult.ok ? "API returned success." : "API returned failure.", {
      projectId: project.id,
      opportunityId: post.id,
      status: publishResult.ok ? "api_success" : "api_failed",
      details: {
        attempt,
        outcomes: publishResult.outcomes,
        error: publishResult.error,
      },
    });

    if (publishResult.ok) break;
    lastError = publishResult.error ?? "Publish failed";
  }

  if (!publishResult?.ok) {
    await log.error("posting_failed", lastError, {
      projectId: project.id,
      opportunityId: post.id,
      status: "failed",
      details: { slot: slotLabel, source, retries: MAX_RETRIES },
    });
    return { ok: false, message: lastError };
  }

  if (MAX_RETRIES > 1) {
    await log.info("retry_completed", "Publish succeeded.", {
      projectId: project.id,
      opportunityId: post.id,
      status: "success",
    });
  }

  const permalinks: Record<string, string> = {};
  for (const outcome of publishResult.outcomes) {
    if (outcome.success && outcome.permalink) {
      permalinks[outcome.platform] = outcome.permalink;
    }
  }
  await updateGeneratedPostPublished(post.id, permalinks);
  // Slot is claimed up-front in run-scheduled-publish; Manual still records a fire marker.
  if (slotLabel === "Manual") {
    await markSlotFired(project.id, slotLabel, project.timezone, now);
  }

  const platforms = publishResult.outcomes
    .filter((o) => o.success)
    .map((o) => o.platform)
    .join(", ");

  const verb = source === "queued" ? "Published queued post" : "Generated and published";
  const message = `Scheduled ${slotLabel}: ${verb} to ${platforms || "platforms"} (${post.title}).`;

  return { ok: true, message, platforms };
}
