/**
 * Publishing job model + JSON persistence (MariaDB optional via migration 011).
 * Jobs wrap existing publishPostToPlatforms without changing provider APIs.
 */

import { promises as fs } from "fs";
import path from "path";
import { randomUUID } from "crypto";
import type {
  PlatformPublishOutcome,
  PlatformResultStatus,
  PublishJobOverallStatus,
} from "@/lib/social/publish-result";
import { computeOverallPublishStatus } from "@/lib/social/publish-result";

const DATA_DIR = path.join(process.cwd(), "data", "publish-jobs");

export interface PublishJobPlatformState {
  platform: string;
  status: PlatformResultStatus;
  attempts: number;
  errorCode?: string;
  message?: string;
  action?: string;
  retryable?: boolean;
  providerPostId?: string;
  providerUrl?: string;
  updatedAt: string;
}

export interface SocialPublishJob {
  id: string;
  projectId: string;
  postId: string;
  userId?: string;
  status: PublishJobOverallStatus;
  platforms: string[];
  platformResults: PublishJobPlatformState[];
  outcomes: PlatformPublishOutcome[];
  createdAt: string;
  updatedAt: string;
  startedAt?: string;
  completedAt?: string;
  error?: string;
}

async function ensureDir() {
  await fs.mkdir(DATA_DIR, { recursive: true });
}

function jobPath(id: string) {
  return path.join(DATA_DIR, `${id}.json`);
}

export async function createPublishJob(input: {
  projectId: string;
  postId: string;
  userId?: string;
  platforms: string[];
}): Promise<SocialPublishJob> {
  await ensureDir();
  const now = new Date().toISOString();
  const job: SocialPublishJob = {
    id: `pj_${randomUUID().replace(/-/g, "").slice(0, 16)}`,
    projectId: input.projectId,
    postId: input.postId,
    userId: input.userId,
    status: "QUEUED",
    platforms: input.platforms,
    platformResults: input.platforms.map((platform) => ({
      platform,
      status: "pending",
      attempts: 0,
      updatedAt: now,
    })),
    outcomes: [],
    createdAt: now,
    updatedAt: now,
  };
  await fs.writeFile(jobPath(job.id), JSON.stringify(job, null, 2), "utf-8");
  return job;
}

export async function getPublishJob(id: string): Promise<SocialPublishJob | null> {
  try {
    const raw = await fs.readFile(jobPath(id), "utf-8");
    return JSON.parse(raw) as SocialPublishJob;
  } catch {
    return null;
  }
}

export async function savePublishJob(job: SocialPublishJob): Promise<SocialPublishJob> {
  await ensureDir();
  const next = { ...job, updatedAt: new Date().toISOString() };
  await fs.writeFile(jobPath(next.id), JSON.stringify(next, null, 2), "utf-8");
  return next;
}

export function markJobProcessing(job: SocialPublishJob): SocialPublishJob {
  return {
    ...job,
    status: "PROCESSING",
    startedAt: job.startedAt ?? new Date().toISOString(),
    updatedAt: new Date().toISOString(),
  };
}

export function applyOutcomesToJob(
  job: SocialPublishJob,
  outcomes: PlatformPublishOutcome[],
): SocialPublishJob {
  const now = new Date().toISOString();
  const platformResults: PublishJobPlatformState[] = outcomes.map((o) => ({
    platform: o.platform,
    status: o.success ? "success" : o.skipped ? "skipped" : "failed",
    attempts: o.attempt ?? 1,
    errorCode: o.errorCode,
    message: o.error,
    action: o.action,
    retryable: o.retryable,
    providerPostId: o.postId,
    providerUrl: o.permalink,
    updatedAt: now,
  }));

  const overall = computeOverallPublishStatus(outcomes);
  return {
    ...job,
    outcomes,
    platformResults,
    status: overall,
    completedAt: now,
    updatedAt: now,
    error:
      overall === "FAILED"
        ? outcomes.find((o) => o.error)?.error
        : overall === "PARTIALLY_PUBLISHED"
          ? "One or more platforms failed or were skipped"
          : undefined,
  };
}
