import type { ConnectedAccount, GeneratedPost } from "@/types/workflow";
import { logAppError } from "@/lib/logging/app-error-logger";
import { logAutoPost } from "@/lib/logging/auto-post-logger";
import { writePublishLog } from "@/lib/logging/publish-logs";
import { checkScheduleSlot } from "@/lib/scheduling/slot-check";
import { isYouTubeAccountPublishReady } from "@/lib/social/publish-platforms";
import {
  canPublishToYouTube,
  YOUTUBE_NO_VIDEO_MESSAGE,
} from "@/lib/social/quick-publish";
import { getAppUrl } from "@/lib/env";
import { isPlatformPublishReady } from "@/lib/social/platforms";
import { isAccountPublishReady } from "@/lib/social/token-validation";
import { publishToLinkedIn } from "@/lib/social/linkedin-publish";
import { publishToFacebook } from "@/lib/social/facebook-publish";
import { publishToInstagram } from "@/lib/social/instagram-publish";
import { publishToYouTube } from "@/lib/social/youtube-publish";
import { resolvePublishableImageUrl, describeImageSkipReason, loadPublishableImageBuffer } from "@/lib/social/publish-image";
import {
  recordProviderPostFailure,
  recordProviderPostSuccess,
} from "@/lib/storage/provider-post-stats";

export interface PlatformPublishOutcome {
  platform: string;
  success: boolean;
  skipped?: boolean;
  postId?: string;
  permalink?: string;
  error?: string;
  imageAttached?: boolean;
  imageSkipReason?: string;
}

export interface PublishPostInput {
  post: GeneratedPost;
  accounts: ConnectedAccount[];
  projectTimezone: string;
  projectCountries: string[];
  postsPerDay: 1 | 2;
  scheduleSeed: number;
  scheduleSaved: boolean;
  forcePublish?: boolean;
  /** Instant publish to every publish-ready connected account (LinkedIn + Facebook + Instagram). */
  quickPublish?: boolean;
  jobId?: string;
  projectId?: string;
}

export interface PublishPostResult {
  ok: boolean;
  outcomes: PlatformPublishOutcome[];
  error?: string;
}

async function recordPlatformOutcome(input: {
  jobId?: string;
  projectId: string;
  opportunityId: string;
  providerId: string;
  success: boolean;
  message: string;
  error?: string;
  postId?: string;
  permalink?: string;
  response?: Record<string, unknown>;
}) {
  const projectId = input.projectId;

  if (input.success) {
    await logAutoPost({
      level: "SUCCESS",
      event: "provider_posted",
      jobId: input.jobId,
      projectId,
      opportunityId: input.opportunityId,
      providerId: input.providerId,
      status: "success",
      message: input.message,
      details: { postId: input.postId, permalink: input.permalink, response: input.response },
    });
    await recordProviderPostSuccess({
      projectId,
      platform: input.providerId,
      postId: input.postId,
      permalink: input.permalink,
    });
    await writePublishLog({
      level: "success",
      action: "published",
      projectId,
      postId: input.opportunityId,
      platform: input.providerId,
      message: input.message,
      details: input.response,
    });
  } else {
    await logAutoPost({
      level: "ERROR",
      event: "provider_post_failed",
      jobId: input.jobId,
      projectId,
      opportunityId: input.opportunityId,
      providerId: input.providerId,
      status: "failed",
      message: input.message,
      error: input.error,
      details: { response: input.response },
    });
    await recordProviderPostFailure({
      projectId,
      platform: input.providerId,
      error: input.error ?? input.message,
    });
    await writePublishLog({
      level: "error",
      action: "publish_failed",
      projectId,
      postId: input.opportunityId,
      platform: input.providerId,
      message: input.error ?? input.message,
    });
  }
}

export async function publishPostToPlatforms(
  input: PublishPostInput,
): Promise<PublishPostResult> {
  const outcomes: PlatformPublishOutcome[] = [];
  const projectId = input.projectId ?? input.post.projectId;
  const opportunityId = input.post.id;

  if (!input.forcePublish && input.scheduleSaved) {
    const slot = checkScheduleSlot({
      timezone: input.projectTimezone,
      countries: input.projectCountries,
      postsPerDay: input.postsPerDay,
      scheduleSeed: input.scheduleSeed,
    });

    await writePublishLog({
      level: slot.allowed ? "info" : "warn",
      action: "schedule_check",
      projectId: input.post.projectId,
      postId: input.post.id,
      timezone: slot.timezone,
      regions: slot.regions,
      message: slot.reason,
      details: { slots: slot.slots.map((s) => s.display), localTime: slot.localTime },
    });

    if (!slot.allowed) {
      await logAutoPost({
        level: "WARNING",
        event: "schedule_validation_failed",
        jobId: input.jobId,
        projectId,
        opportunityId,
        status: "blocked",
        message: slot.reason,
      });
      return {
        ok: false,
        outcomes,
        error: slot.reason,
      };
    }
  }

  const targets = input.quickPublish
    ? input.accounts
        .filter((a) => isAccountPublishReady(a))
        .map((a) => a.platform)
    : input.post.platforms.filter((p) => isPlatformPublishReady(p));

  const youtubeConnected = input.accounts.some(
    (a) => a.platform === "youtube" && a.connected,
  );

  if (!targets.length && !youtubeConnected && !input.post.platforms.includes("youtube")) {
    const msg = input.quickPublish
      ? "No connected publish-ready accounts. Connect LinkedIn, Facebook, or Instagram."
      : "No supported publish platforms on this post.";
    await logAutoPost({
      level: "ERROR",
      event: "no_providers_ready",
      jobId: input.jobId,
      projectId,
      opportunityId,
      status: "blocked",
      message: msg,
    });
    await writePublishLog({
      level: "error",
      action: "publish_blocked",
      projectId,
      postId: opportunityId,
      message: msg,
    });
    return { ok: false, outcomes, error: msg };
  }

  const platformsToPublish = [...targets];

  const youtubeAccount = input.accounts.find((a) => a.platform === "youtube");
  const youtubeCanPublish = isYouTubeAccountPublishReady(youtubeAccount, input.post);

  if (youtubeCanPublish && !platformsToPublish.includes("youtube")) {
    platformsToPublish.push("youtube");
  } else if (
    youtubeAccount?.connected &&
    !canPublishToYouTube(input.post) &&
    !platformsToPublish.includes("youtube")
  ) {
    outcomes.push({
      platform: "youtube",
      success: false,
      skipped: true,
      error: YOUTUBE_NO_VIDEO_MESSAGE,
    });
    await writePublishLog({
      level: "warn",
      action: "publish_skipped",
      projectId: input.post.projectId,
      postId: input.post.id,
      platform: "youtube",
      message: YOUTUBE_NO_VIDEO_MESSAGE,
    });
  } else if (!input.quickPublish) {
    const youtubeRequested = input.post.platforms.includes("youtube");
    if (youtubeRequested && !platformsToPublish.includes("youtube")) {
      platformsToPublish.push("youtube");
    }
  }

  if (!platformsToPublish.length) {
    const msg =
      outcomes.length > 0
        ? YOUTUBE_NO_VIDEO_MESSAGE
        : "No connected publish-ready accounts.";
    return { ok: false, outcomes, error: msg };
  }

  const appOrigin = getAppUrl();
  const publishImageUrl = await resolvePublishableImageUrl(input.post.imageUrl, appOrigin);
  const publishImageBuffer = await loadPublishableImageBuffer(
    input.post.imageUrl,
    appOrigin,
  );
  const existingPermalinks = input.post.platformPermalinks ?? {};

  if (input.post.imageUrl && !publishImageBuffer) {
    const skipReason = describeImageSkipReason({
      imageUrl: input.post.imageUrl,
      appOrigin,
      platform: "linkedin",
      bufferLoaded: false,
    });
    await writePublishLog({
      level: "warn",
      action: "publish_image_unavailable",
      projectId: input.post.projectId,
      postId: input.post.id,
      message: skipReason ?? "Banner image could not be loaded for publish.",
      details: {
        imageUrl: input.post.imageUrl,
        publishImageUrl,
        appOrigin,
      },
    });
  }

  for (const platform of platformsToPublish) {
    // Never re-post to a platform that already has a permalink for this draft.
    if (existingPermalinks[platform]) {
      outcomes.push({
        platform,
        success: true,
        skipped: true,
        permalink: existingPermalinks[platform],
        postId: existingPermalinks[platform],
      });
      await writePublishLog({
        level: "info",
        action: "publish_skipped_duplicate",
        projectId: input.post.projectId,
        postId: input.post.id,
        platform,
        message: `${platform} already published for this post — skipped duplicate.`,
        details: { permalink: existingPermalinks[platform] },
      });
      continue;
    }

    const account = input.accounts.find((a) => a.platform === platform);
    const token =
      (account as ConnectedAccount & { publishToken?: string | null })?.publishToken ??
      account?.accessToken;

    if (!account?.connected || !token || token.startsWith("sim_")) {
      const msg = `${platform}: account not connected with a valid OAuth token. Reconnect in Connected Accounts.`;
      outcomes.push({ platform, success: false, error: msg });
      await recordPlatformOutcome({
        jobId: input.jobId,
        projectId,
        opportunityId,
        providerId: platform,
        success: false,
        message: msg,
        error: msg,
      });
      continue;
    }

    if (account.connectionStatus === "expired" || (account.expiresAt && new Date(account.expiresAt) <= new Date())) {
      const msg = `${platform}: token expired. Reconnect in Connected Accounts.`;
      outcomes.push({ platform, success: false, error: msg });
      await recordPlatformOutcome({
        jobId: input.jobId,
        projectId,
        opportunityId,
        providerId: platform,
        success: false,
        message: msg,
        error: msg,
      });
      continue;
    }

    if (platform === "linkedin") {
      const result = await publishToLinkedIn({
        accessToken: token,
        externalId: account.externalId,
        caption: input.post.caption,
        imageUrl: input.post.imageUrl ?? publishImageUrl,
        appOrigin,
      });

      const imageSkipReason =
        !result.imageAttached && input.post.imageUrl
          ? describeImageSkipReason({
              imageUrl: input.post.imageUrl,
              appOrigin,
              platform: "linkedin",
              bufferLoaded: Boolean(publishImageBuffer),
            }) ?? "LinkedIn posted text only — image upload skipped."
          : undefined;

      if (result.success) {
        outcomes.push({
          platform,
          success: true,
          postId: result.postUrn,
          permalink: result.permalink,
          imageAttached: result.imageAttached,
          imageSkipReason,
        });
        await recordPlatformOutcome({
          jobId: input.jobId,
          projectId,
          opportunityId,
          providerId: platform,
          success: true,
          message: `LinkedIn post created${result.imageAttached ? " with image" : " (text only)"}${imageSkipReason ? ` — ${imageSkipReason}` : ""}.`,
          postId: result.postUrn,
          permalink: result.permalink,
          response: {
            postUrn: result.postUrn,
            permalink: result.permalink,
            imageAttached: result.imageAttached,
            imageSkipReason,
          },
        });
      } else {
        outcomes.push({ platform, success: false, error: result.error });
        const revoked = /revoked|expired|invalid.?token|unauthorized/i.test(result.error ?? "");
        if (revoked) {
          const ownerId = input.post.userId;
          if (ownerId) {
            try {
              const { upsertProjectSocialAccount } = await import("@/lib/storage/social-accounts");
              await upsertProjectSocialAccount(ownerId, projectId, {
                ...account,
                connectionStatus: "expired",
                connected: true,
              });
            } catch {
              // best-effort mark expired
            }
          }
          await logAppError({
            area: "oauth",
            event: "linkedin_token_revoked",
            projectId,
            platform: "linkedin",
            message: "LinkedIn token revoked — reconnect required in Connected Accounts",
            error: result.error,
          }).catch(() => undefined);
        }
        await recordPlatformOutcome({
          jobId: input.jobId,
          projectId,
          opportunityId,
          providerId: platform,
          success: false,
          message: result.error ?? "LinkedIn publish failed",
          error: result.error,
        });
      }
      continue;
    }

    if (platform === "facebook") {
      if (!account.externalId) {
        const msg = "Facebook Page ID missing. Reconnect your Facebook Page.";
        outcomes.push({ platform, success: false, error: msg });
        continue;
      }

      const result = await publishToFacebook({
        pageId: account.externalId,
        pageAccessToken: token,
        caption: input.post.caption,
        imageUrl: input.post.imageUrl ?? publishImageUrl,
        appOrigin,
      });

      const fbImageSkipReason =
        !result.imageAttached && input.post.imageUrl
          ? result.imageSkipReason ??
            describeImageSkipReason({
              imageUrl: input.post.imageUrl,
              appOrigin,
              platform: "facebook",
              bufferLoaded: Boolean(publishImageBuffer),
            }) ??
            "Facebook posted text only — image upload skipped."
          : undefined;

      if (result.success) {
        outcomes.push({
          platform,
          success: true,
          postId: result.postId,
          permalink: result.permalink,
          imageAttached: result.imageAttached,
          imageSkipReason: fbImageSkipReason,
        });
        await recordPlatformOutcome({
          jobId: input.jobId,
          projectId,
          opportunityId,
          providerId: platform,
          success: true,
          message: `Facebook Page post created${result.imageAttached ? " with image" : " (text only)"}${fbImageSkipReason ? ` — ${fbImageSkipReason}` : ""}.`,
          postId: result.postId,
          permalink: result.permalink,
          response: {
            postId: result.postId,
            permalink: result.permalink,
            imageAttached: result.imageAttached,
            imageSkipReason: fbImageSkipReason,
          },
        });
      } else {
        outcomes.push({ platform, success: false, error: result.error });
        await recordPlatformOutcome({
          jobId: input.jobId,
          projectId,
          opportunityId,
          providerId: platform,
          success: false,
          message: result.error ?? "Facebook publish failed",
          error: result.error,
        });
      }
      continue;
    }

    if (platform === "instagram") {
      if (!account.externalId) {
        const msg = "Instagram account ID missing. Reconnect your Instagram Business account.";
        outcomes.push({ platform, success: false, error: msg });
        continue;
      }

      const result = await publishToInstagram({
        igUserId: account.externalId,
        pageAccessToken: token,
        caption: input.post.caption,
        imageUrl: publishImageUrl,
        appOrigin,
      });

      const igImageSkipReason =
        !result.success && input.post.imageUrl
          ? result.error ??
            describeImageSkipReason({
              imageUrl: input.post.imageUrl,
              appOrigin,
              platform: "instagram",
              bufferLoaded: Boolean(publishImageBuffer),
            })
          : undefined;

      if (result.success) {
        outcomes.push({
          platform,
          success: true,
          postId: result.postId,
          permalink: result.permalink,
          imageAttached: true,
        });
        await recordPlatformOutcome({
          jobId: input.jobId,
          projectId,
          opportunityId,
          providerId: platform,
          success: true,
          message: "Instagram post published.",
          postId: result.postId,
          permalink: result.permalink,
          response: { postId: result.postId, permalink: result.permalink },
        });
      } else {
        outcomes.push({
          platform,
          success: false,
          error: result.error,
          imageAttached: false,
          imageSkipReason: igImageSkipReason ?? undefined,
        });
        await recordPlatformOutcome({
          jobId: input.jobId,
          projectId,
          opportunityId,
          providerId: platform,
          success: false,
          message: result.error ?? "Instagram publish failed",
          error: result.error,
        });
      }
      continue;
    }

    if (platform === "youtube") {
      const result = await publishToYouTube({
        accessToken: token,
        channelId: account.externalId ?? "",
        caption: input.post.caption,
        videoUrl: input.post.videoUrl,
        title: input.post.title,
      });

      outcomes.push({
        platform,
        success: result.success,
        skipped: !result.success,
        postId: result.postId,
        permalink: result.permalink,
        error: result.error,
      });
      await writePublishLog({
        level: result.success ? "success" : "warn",
        action: result.success ? "published" : "publish_skipped",
        projectId: input.post.projectId,
        postId: input.post.id,
        platform,
        message: result.error ?? "YouTube publish completed.",
      });
      continue;
    }

    const msg = `${platform}: OAuth publish is not available in this release.`;
    outcomes.push({ platform, success: false, error: msg });
    await writePublishLog({
      level: "warn",
      action: "publish_skipped",
      projectId: input.post.projectId,
      postId: input.post.id,
      platform,
      message: msg,
    });
  }

  const anySuccess = outcomes.some((o) => o.success);
  if (!anySuccess) {
    const firstError =
      outcomes.find((o) => o.error && !o.skipped)?.error ??
      outcomes.find((o) => o.error)?.error ??
      "All platform publishes failed.";
    await logAutoPost({
      level: "ERROR",
      event: "posting_failed",
      jobId: input.jobId,
      projectId,
      opportunityId,
      status: "failed",
      message: firstError,
      details: { outcomes },
    });
    return { ok: false, outcomes, error: firstError };
  }

  return { ok: true, outcomes };
}
