"use server";

import { db } from "@/lib/db";
import { revalidatePath } from "next/cache";
import { mkdir, writeFile } from "node:fs/promises";
import path from "node:path";
import { createHash, randomUUID } from "node:crypto";
import {
  readJobApiSourcesConfig,
  readJobSlidesConfig,
  writeJobApiSourcesConfig,
  writeJobSlidesConfig,
  type JobApiSourceConfig,
  type JobSlideConfig,
} from "@/lib/jobs-admin-config";

function normalizeImageUrl(value: string): string {
  const input = value.trim();
  if (!input) return "";
  if (input.startsWith("http://") || input.startsWith("https://") || input.startsWith("data:") || input.startsWith("blob:")) return input;
  if (input.startsWith("//")) return `https:${input}`;
  if (input.startsWith("/")) return input;
  return `/${input}`;
}

async function storeUploadedImage(file: File): Promise<string> {
  if (!file || file.size === 0) throw new Error("No file uploaded");
  if (!file.type.startsWith("image/")) throw new Error("Only image uploads are allowed");

  const ext = path.extname(file.name || "").toLowerCase() || ".jpg";
  const safeExt = ext.replace(/[^.a-z0-9]/g, "") || ".jpg";
  const fileName = `${Date.now()}-${randomUUID()}${safeExt}`;
  const dir = path.join(process.cwd(), "public", "uploads", "job-slides");

  await mkdir(dir, { recursive: true });
  const bytes = Buffer.from(await file.arrayBuffer());
  await writeFile(path.join(dir, fileName), bytes);

  return `/uploads/job-slides/${fileName}`;
}

function getByPath(obj: unknown, pathExpr: string): unknown {
  if (!pathExpr.trim()) return obj;
  return pathExpr.split(".").reduce<unknown>((acc, key) => {
    if (acc && typeof acc === "object" && key in (acc as Record<string, unknown>)) {
      return (acc as Record<string, unknown>)[key];
    }
    return undefined;
  }, obj);
}

function getStringField(item: unknown, key: string): string {
  if (!item || typeof item !== "object" || !key.trim()) return "";
  const value = (item as Record<string, unknown>)[key];
  if (value == null) return "";
  return String(value).trim();
}

function makeExternalId(sourceKey: string, item: unknown, externalIdKey: string, fallbackSeed: string): string {
  const direct = getStringField(item, externalIdKey);
  if (direct) return `${sourceKey}:${direct}`;
  const hash = createHash("sha1").update(fallbackSeed).digest("hex").slice(0, 20);
  return `${sourceKey}:${hash}`;
}

// Jobs CRUD

export async function saveJobAction(formData: FormData) {
  const id = (formData.get("id") as string | null) || "";
  const title = String(formData.get("title") || "").trim();
  const company = String(formData.get("company") || "").trim() || null;
  const location = String(formData.get("location") || "").trim() || null;
  const jobType = String(formData.get("jobType") || "").trim() || null;
  const salary = String(formData.get("salary") || "").trim() || null;
  const description = String(formData.get("description") || "").trim() || null;
  const applyLink = String(formData.get("applyLink") || "").trim() || null;
  const isFeatured = String(formData.get("isFeatured") || "false") === "true";
  const isActive = String(formData.get("isActive") || "true") === "true";
  const source = String(formData.get("source") || "manual").trim() || "manual";

  if (!title) return { ok: false, error: "Title is required" };

  const payload = {
    title,
    company,
    location,
    jobType,
    salary,
    description,
    applyLink,
    isFeatured,
    isActive,
    source,
    postedAt: new Date(),
  };

  if (id) {
    await db.job.update({ where: { id }, data: payload });
  } else {
    await db.job.create({ data: payload });
  }

  revalidatePath("/admin/jobs");
  revalidatePath("/jobs");
  return { ok: true };
}

export async function deleteJobAction(id: string) {
  await db.job.delete({ where: { id } });
  revalidatePath("/admin/jobs");
  revalidatePath("/jobs");
  return { ok: true };
}

export async function toggleJobFeaturedAction(id: string, isFeatured: boolean) {
  await db.job.update({ where: { id }, data: { isFeatured } });
  revalidatePath("/admin/jobs");
  revalidatePath("/jobs");
  return { ok: true };
}

// Job applications and CRM

export async function updateApplicationStatusAction(id: string, status: "submitted" | "viewed" | "shortlisted" | "rejected") {
  await db.jobApplication.update({ where: { id }, data: { status } });
  revalidatePath("/admin/jobs");
  return { ok: true };
}

export async function deleteApplicationAction(id: string) {
  await db.jobApplication.delete({ where: { id } });
  revalidatePath("/admin/jobs");
  return { ok: true };
}

export async function toggleUserActiveAction(userId: string, isActive: boolean) {
  await db.user.update({ where: { id: userId }, data: { isActive } });
  revalidatePath("/admin/jobs");
  return { ok: true };
}

// Job slider config

export async function saveJobSlideAction(formData: FormData) {
  const id = String(formData.get("id") || "").trim();
  const imageUrlRaw = String(formData.get("imageUrl") || "").trim();
  const imageFile = formData.get("imageFile") as File | null;
  const label = String(formData.get("label") || "").trim();
  const title = String(formData.get("title") || "").trim();
  const subtitle = String(formData.get("subtitle") || "").trim();
  const sortOrder = Number(formData.get("sortOrder") || 0) || 0;
  const isActive = String(formData.get("isActive") || "true") === "true";

  const uploadedImage = imageFile && imageFile.size > 0 ? await storeUploadedImage(imageFile) : "";
  const imageUrl = normalizeImageUrl(uploadedImage || imageUrlRaw);
  if (!imageUrl) return { ok: false, error: "Provide image URL or upload an image." };

  const slides = await readJobSlidesConfig();
  const targetId = id || `jobs-slide-${randomUUID()}`;
  const next: JobSlideConfig = {
    id: targetId,
    imageUrl,
    label,
    title,
    subtitle,
    sortOrder,
    isActive,
  };

  const idx = slides.findIndex((s) => s.id === targetId);
  if (idx >= 0) slides[idx] = next;
  else slides.push(next);

  await writeJobSlidesConfig(slides);
  revalidatePath("/admin/jobs");
  revalidatePath("/jobs");
  return { ok: true };
}

export async function deleteJobSlideAction(id: string) {
  const slides = await readJobSlidesConfig();
  const next = slides.filter((s) => s.id !== id);
  await writeJobSlidesConfig(next);
  revalidatePath("/admin/jobs");
  revalidatePath("/jobs");
  return { ok: true };
}

// API sources config and sync

export async function saveJobApiSourceAction(formData: FormData) {
  const id = String(formData.get("id") || "").trim();
  const source: JobApiSourceConfig = {
    id: id || `source-${randomUUID()}`,
    name: String(formData.get("name") || "").trim(),
    sourceKey: String(formData.get("sourceKey") || "").trim(),
    endpoint: String(formData.get("endpoint") || "").trim(),
    isActive: String(formData.get("isActive") || "true") === "true",
    authHeader: String(formData.get("authHeader") || "").trim(),
    authToken: String(formData.get("authToken") || "").trim(),
    jobPath: String(formData.get("jobPath") || "").trim(),
    titleKey: String(formData.get("titleKey") || "title").trim(),
    companyKey: String(formData.get("companyKey") || "company").trim(),
    locationKey: String(formData.get("locationKey") || "location").trim(),
    typeKey: String(formData.get("typeKey") || "jobType").trim(),
    salaryKey: String(formData.get("salaryKey") || "salary").trim(),
    descriptionKey: String(formData.get("descriptionKey") || "description").trim(),
    applyLinkKey: String(formData.get("applyLinkKey") || "applyLink").trim(),
    postedAtKey: String(formData.get("postedAtKey") || "postedAt").trim(),
    externalIdKey: String(formData.get("externalIdKey") || "id").trim(),
  };

  if (!source.name || !source.sourceKey || !source.endpoint) {
    return { ok: false, error: "Name, source key and endpoint are required." };
  }

  const sources = await readJobApiSourcesConfig();
  const idx = sources.findIndex((s) => s.id === source.id);
  if (idx >= 0) sources[idx] = source;
  else sources.push(source);

  await writeJobApiSourcesConfig(sources);
  revalidatePath("/admin/jobs");
  return { ok: true };
}

export async function deleteJobApiSourceAction(id: string) {
  const sources = await readJobApiSourcesConfig();
  await writeJobApiSourcesConfig(sources.filter((s) => s.id !== id));
  revalidatePath("/admin/jobs");
  return { ok: true };
}

export async function syncJobsFromApiSourceAction(sourceId: string) {
  const sources = await readJobApiSourcesConfig();
  const source = sources.find((s) => s.id === sourceId);
  if (!source) return { ok: false, error: "API source not found." };

  const headers: Record<string, string> = {};
  if (source.authHeader && source.authToken) headers[source.authHeader] = source.authToken;

  const res = await fetch(source.endpoint, { headers, cache: "no-store" });
  if (!res.ok) return { ok: false, error: `API request failed (${res.status})` };

  const payload = await res.json();
  const listRaw = getByPath(payload, source.jobPath) ?? payload;
  if (!Array.isArray(listRaw)) return { ok: false, error: "Configured jobPath does not resolve to an array." };

  let created = 0;
  let updated = 0;
  let skipped = 0;

  for (const item of listRaw) {
    const title = getStringField(item, source.titleKey);
    if (!title) {
      skipped += 1;
      continue;
    }

    const company = getStringField(item, source.companyKey) || null;
    const location = getStringField(item, source.locationKey) || null;
    const jobType = getStringField(item, source.typeKey) || null;
    const salary = getStringField(item, source.salaryKey) || null;
    const description = getStringField(item, source.descriptionKey) || null;
    const applyLink = getStringField(item, source.applyLinkKey) || null;
    const postedAtRaw = getStringField(item, source.postedAtKey);
    const postedAt = postedAtRaw ? new Date(postedAtRaw) : new Date();

    const seed = `${title}|${company ?? ""}|${location ?? ""}|${applyLink ?? ""}`;
    const externalId = makeExternalId(source.sourceKey, item, source.externalIdKey, seed);

    const existing = await db.job.findUnique({ where: { externalId }, select: { id: true } });

    const data = {
      title,
      company,
      location,
      jobType,
      salary,
      description,
      applyLink,
      source: source.sourceKey,
      isActive: true,
      postedAt: Number.isNaN(postedAt.getTime()) ? new Date() : postedAt,
      externalId,
    };

    if (existing) {
      await db.job.update({ where: { id: existing.id }, data });
      updated += 1;
    } else {
      await db.job.create({ data });
      created += 1;
    }
  }

  revalidatePath("/admin/jobs");
  revalidatePath("/jobs");
  return { ok: true, created, updated, skipped };
}
