"use server";

import { revalidatePath } from "next/cache";
import { redirect } from "next/navigation";
import { eq } from "drizzle-orm";
import { z } from "zod";
import { db } from "@/lib/db/client";
import {
  researchStudiesTable,
  researchSites,
  researchInvestigators,
  researchDocuments,
  researchPublications,
  researchUpdates,
  researchApplications,
  researchProposals,
} from "@/lib/db/schema";
import { requireAdmin } from "@/lib/auth/admin-guard";
import { logAdminAction } from "@/lib/audit";
import { sendTemplatedEmail } from "@/lib/email/send";

function slugify(input: string): string {
  return input
    .toLowerCase()
    .trim()
    .replace(/[^a-z0-9\s-]/g, "")
    .replace(/\s+/g, "-")
    .replace(/-+/g, "-")
    .slice(0, 80);
}

function multiline(input: string | null | undefined): string[] {
  if (!input) return [];
  return input
    .split("\n")
    .map((l) => l.trim())
    .filter((l) => l.length > 0);
}

async function ensureAdmin() {
  const session = await requireAdmin();
  return (session.user as { id?: string }).id!;
}

const phaseValues = ["Planned", "Recruiting", "Active", "Completed"] as const;
const statusValues = ["draft", "published", "archived"] as const;

// -------------------------------------------------------------------------
// Studies
// -------------------------------------------------------------------------

const createStudySchema = z.object({
  title: z.string().min(3).max(300),
  phase: z.enum(phaseValues),
  conditionKeyword: z.string().max(200).optional(),
});

export async function createStudy(formData: FormData) {
  const adminId = await ensureAdmin();
  const parsed = createStudySchema.parse({
    title: formData.get("title"),
    phase: formData.get("phase") ?? "Planned",
    conditionKeyword: formData.get("conditionKeyword") ?? "",
  });

  let slug = slugify(parsed.title) || "study";
  const [existing] = await db
    .select({ id: researchStudiesTable.id })
    .from(researchStudiesTable)
    .where(eq(researchStudiesTable.slug, slug))
    .limit(1);
  if (existing) slug = `${slug}-${Math.random().toString(36).slice(2, 6)}`;

  const [row] = await db
    .insert(researchStudiesTable)
    .values({
      slug,
      title: parsed.title,
      phase: parsed.phase,
      conditionKeyword: parsed.conditionKeyword ?? "",
    })
    .returning({ id: researchStudiesTable.id });

  await logAdminAction({
    adminUserId: adminId,
    action: "create_study",
    targetId: row.id,
  });

  revalidatePath("/admin/research");
  redirect(`/admin/research/${row.id}`);
}

const updateStudySchema = z.object({
  id: z.string().uuid(),
  title: z.string().min(3).max(300),
  phase: z.enum(phaseValues),
  nctId: z.string().max(40),
  sponsor: z.string().max(300),
  principalInvestigator: z.string().max(200),
  conditionKeyword: z.string().max(200),
  targetEnrolment: z.coerce.number().int().min(0).max(100000),
  currentEnrolment: z.coerce.number().int().min(0).max(100000),
  countries: z.string().max(2000),
  background: z.string().max(8000),
  design: z.string().max(4000),
  intervention: z.string().max(4000),
  control: z.string().max(4000),
  inclusion: z.string().max(4000),
  exclusion: z.string().max(4000),
  primaryOutcomes: z.string().max(4000),
  secondaryOutcomes: z.string().max(4000),
  isRecruitingCollaborators: z.string().optional(),
  collaborationEmail: z.string().max(200),
  authorshipOpportunities: z.string().max(4000),
  openRoles: z.string().max(20_000).optional(),
  featured: z.string().optional(),
  specialty: z.string().max(200),
  themes: z.string().max(2000),
  timelineMilestones: z.string().max(20_000).optional(),
  ethicsRef: z.string().max(200),
  protocolVersion: z.string().max(100),
});

const milestoneStatusValues = ["done", "active", "upcoming"] as const;
const timelineMilestoneSchema = z.array(
  z.object({
    title: z.string().trim().max(200).default(""),
    date: z.string().trim().max(100).default(""),
    description: z.string().trim().max(1000).default(""),
    status: z.enum(milestoneStatusValues).default("upcoming"),
  }),
);

const openRoleSchema = z.array(
  z.object({
    title: z.string().trim().max(200).default(""),
    description: z.string().trim().max(2000).default(""),
    commitment: z.string().trim().max(200).default(""),
    compensation: z.string().trim().max(200).default(""),
  }),
);

export async function updateStudy(formData: FormData) {
  const adminId = await ensureAdmin();
  const parsed = updateStudySchema.parse({
    id: formData.get("id"),
    title: formData.get("title") ?? "",
    phase: formData.get("phase") ?? "Planned",
    nctId: formData.get("nctId") ?? "",
    sponsor: formData.get("sponsor") ?? "",
    principalInvestigator: formData.get("principalInvestigator") ?? "",
    conditionKeyword: formData.get("conditionKeyword") ?? "",
    targetEnrolment: formData.get("targetEnrolment") ?? 0,
    currentEnrolment: formData.get("currentEnrolment") ?? 0,
    countries: formData.get("countries") ?? "",
    background: formData.get("background") ?? "",
    design: formData.get("design") ?? "",
    intervention: formData.get("intervention") ?? "",
    control: formData.get("control") ?? "",
    inclusion: formData.get("inclusion") ?? "",
    exclusion: formData.get("exclusion") ?? "",
    primaryOutcomes: formData.get("primaryOutcomes") ?? "",
    secondaryOutcomes: formData.get("secondaryOutcomes") ?? "",
    isRecruitingCollaborators: (formData.get("isRecruitingCollaborators") as string) ?? undefined,
    collaborationEmail: formData.get("collaborationEmail") ?? "",
    authorshipOpportunities: formData.get("authorshipOpportunities") ?? "",
    openRoles: (formData.get("openRoles") as string) ?? "",
    featured: (formData.get("featured") as string) ?? undefined,
    specialty: formData.get("specialty") ?? "",
    themes: formData.get("themes") ?? "",
    timelineMilestones: (formData.get("timelineMilestones") as string) ?? "",
    ethicsRef: formData.get("ethicsRef") ?? "",
    protocolVersion: formData.get("protocolVersion") ?? "",
  });

  let openRoles: Array<{ title: string; description: string; commitment: string; compensation: string }> = [];
  if (parsed.openRoles && parsed.openRoles.length > 0) {
    try {
      const raw = JSON.parse(parsed.openRoles);
      const result = openRoleSchema.safeParse(raw);
      if (result.success) openRoles = result.data;
    } catch {
      openRoles = [];
    }
  }

  let timelineMilestones: Array<{ title: string; date: string; description: string; status: "done" | "active" | "upcoming" }> = [];
  if (parsed.timelineMilestones && parsed.timelineMilestones.length > 0) {
    try {
      const raw = JSON.parse(parsed.timelineMilestones);
      const result = timelineMilestoneSchema.safeParse(raw);
      if (result.success) timelineMilestones = result.data;
    } catch {
      timelineMilestones = [];
    }
  }

  const themes = parsed.themes
    .split(",")
    .map((t) => t.trim())
    .filter(Boolean);

  await db
    .update(researchStudiesTable)
    .set({
      title: parsed.title,
      phase: parsed.phase,
      nctId: parsed.nctId || null,
      sponsor: parsed.sponsor || "OrthoGlobe Charity Collaboration",
      principalInvestigator: parsed.principalInvestigator,
      conditionKeyword: parsed.conditionKeyword,
      targetEnrolment: parsed.targetEnrolment,
      currentEnrolment: parsed.currentEnrolment,
      countries: multiline(parsed.countries),
      background: parsed.background,
      design: parsed.design,
      intervention: parsed.intervention,
      control: parsed.control,
      inclusion: multiline(parsed.inclusion),
      exclusion: multiline(parsed.exclusion),
      primaryOutcomes: multiline(parsed.primaryOutcomes),
      secondaryOutcomes: multiline(parsed.secondaryOutcomes),
      isRecruitingCollaborators: parsed.isRecruitingCollaborators === "on",
      collaborationEmail: parsed.collaborationEmail || null,
      authorshipOpportunities: parsed.authorshipOpportunities,
      openRoles,
      featured: parsed.featured === "on",
      specialty: parsed.specialty,
      themes,
      timelineMilestones,
      ethicsRef: parsed.ethicsRef,
      protocolVersion: parsed.protocolVersion,
      updatedAt: new Date(),
    })
    .where(eq(researchStudiesTable.id, parsed.id));

  await logAdminAction({
    adminUserId: adminId,
    action: "update_study",
    targetId: parsed.id,
  });

  revalidatePath("/admin/research");
  revalidatePath(`/admin/research/${parsed.id}`);
  revalidatePath("/products/research/studies");
  revalidatePath("/products/research");
}

const setStudyStatusSchema = z.object({
  id: z.string().uuid(),
  status: z.enum(statusValues),
});

export async function setStudyStatus(formData: FormData) {
  const adminId = await ensureAdmin();
  const parsed = setStudyStatusSchema.parse({
    id: formData.get("id"),
    status: formData.get("status"),
  });

  await db
    .update(researchStudiesTable)
    .set({
      status: parsed.status,
      publishedAt: parsed.status === "published" ? new Date() : null,
      updatedAt: new Date(),
    })
    .where(eq(researchStudiesTable.id, parsed.id));

  await logAdminAction({
    adminUserId: adminId,
    action: `study_${parsed.status}`,
    targetId: parsed.id,
  });

  revalidatePath("/admin/research");
  revalidatePath(`/admin/research/${parsed.id}`);
  revalidatePath("/products/research/studies");
  revalidatePath("/products/research");
}

export async function deleteStudy(formData: FormData) {
  const adminId = await ensureAdmin();
  const id = z.string().uuid().parse(formData.get("id"));
  await db.delete(researchStudiesTable).where(eq(researchStudiesTable.id, id));
  await logAdminAction({
    adminUserId: adminId,
    action: "delete_study",
    targetId: id,
  });
  revalidatePath("/admin/research");
  revalidatePath("/products/research/studies");
  redirect("/admin/research");
}

// -------------------------------------------------------------------------
// Sites
// -------------------------------------------------------------------------

const addSiteSchema = z.object({
  studyId: z.string().uuid(),
  city: z.string().min(1).max(200),
  country: z.string().min(1).max(200),
  piName: z.string().min(1).max(200),
  enrolment: z.coerce.number().int().min(0).max(100000),
});

export async function addSite(formData: FormData) {
  const adminId = await ensureAdmin();
  const parsed = addSiteSchema.parse({
    studyId: formData.get("studyId"),
    city: formData.get("city"),
    country: formData.get("country"),
    piName: formData.get("piName"),
    enrolment: formData.get("enrolment") ?? 0,
  });
  const siblings = await db
    .select({ id: researchSites.id })
    .from(researchSites)
    .where(eq(researchSites.studyId, parsed.studyId));
  await db.insert(researchSites).values({
    studyId: parsed.studyId,
    city: parsed.city,
    country: parsed.country,
    piName: parsed.piName,
    enrolment: parsed.enrolment,
    position: siblings.length,
  });
  await logAdminAction({
    adminUserId: adminId,
    action: "add_study_site",
    targetId: parsed.studyId,
  });
  revalidatePath(`/admin/research/${parsed.studyId}`);
}

export async function deleteSite(formData: FormData) {
  const adminId = await ensureAdmin();
  const id = z.string().uuid().parse(formData.get("id"));
  const studyId = z.string().uuid().parse(formData.get("studyId"));
  await db.delete(researchSites).where(eq(researchSites.id, id));
  await logAdminAction({
    adminUserId: adminId,
    action: "delete_study_site",
    targetId: id,
  });
  revalidatePath(`/admin/research/${studyId}`);
}

// -------------------------------------------------------------------------
// Investigators
// -------------------------------------------------------------------------

const addInvestigatorSchema = z.object({
  studyId: z.string().uuid(),
  name: z.string().min(1).max(200),
  institution: z.string().min(1).max(300),
  role: z.string().min(1).max(100),
});

export async function addInvestigator(formData: FormData) {
  const adminId = await ensureAdmin();
  const parsed = addInvestigatorSchema.parse({
    studyId: formData.get("studyId"),
    name: formData.get("name"),
    institution: formData.get("institution"),
    role: formData.get("role"),
  });
  const siblings = await db
    .select({ id: researchInvestigators.id })
    .from(researchInvestigators)
    .where(eq(researchInvestigators.studyId, parsed.studyId));
  await db.insert(researchInvestigators).values({
    studyId: parsed.studyId,
    name: parsed.name,
    institution: parsed.institution,
    role: parsed.role,
    position: siblings.length,
  });
  await logAdminAction({
    adminUserId: adminId,
    action: "add_study_investigator",
    targetId: parsed.studyId,
  });
  revalidatePath(`/admin/research/${parsed.studyId}`);
}

export async function deleteInvestigator(formData: FormData) {
  const adminId = await ensureAdmin();
  const id = z.string().uuid().parse(formData.get("id"));
  const studyId = z.string().uuid().parse(formData.get("studyId"));
  await db.delete(researchInvestigators).where(eq(researchInvestigators.id, id));
  await logAdminAction({
    adminUserId: adminId,
    action: "delete_study_investigator",
    targetId: id,
  });
  revalidatePath(`/admin/research/${studyId}`);
}

// -------------------------------------------------------------------------
// Documents
// -------------------------------------------------------------------------

const addDocumentSchema = z.object({
  studyId: z.string().uuid(),
  name: z.string().min(1).max(300),
  type: z.string().min(1).max(100),
  url: z.string().max(500).optional(),
});

export async function addDocument(formData: FormData) {
  const adminId = await ensureAdmin();
  const parsed = addDocumentSchema.parse({
    studyId: formData.get("studyId"),
    name: formData.get("name"),
    type: formData.get("type"),
    url: (formData.get("url") as string) ?? "",
  });
  const siblings = await db
    .select({ id: researchDocuments.id })
    .from(researchDocuments)
    .where(eq(researchDocuments.studyId, parsed.studyId));
  await db.insert(researchDocuments).values({
    studyId: parsed.studyId,
    name: parsed.name,
    type: parsed.type,
    url: parsed.url ? parsed.url : null,
    position: siblings.length,
  });
  await logAdminAction({
    adminUserId: adminId,
    action: "add_study_document",
    targetId: parsed.studyId,
  });
  revalidatePath(`/admin/research/${parsed.studyId}`);
}

export async function deleteDocument(formData: FormData) {
  const adminId = await ensureAdmin();
  const id = z.string().uuid().parse(formData.get("id"));
  const studyId = z.string().uuid().parse(formData.get("studyId"));
  await db.delete(researchDocuments).where(eq(researchDocuments.id, id));
  await logAdminAction({
    adminUserId: adminId,
    action: "delete_study_document",
    targetId: id,
  });
  revalidatePath(`/admin/research/${studyId}`);
}

// -------------------------------------------------------------------------
// Publications
// -------------------------------------------------------------------------

const publicationTypeValues = [
  "journal",
  "preprint",
  "conference",
  "poster",
  "dataset",
  "report",
] as const;

const addPublicationSchema = z.object({
  studyId: z.string().uuid(),
  title: z.string().min(3).max(500),
  authors: z.string().max(2000),
  journal: z.string().max(300).optional().default(""),
  year: z.coerce.number().int().min(1950).max(2100).optional(),
  doi: z.string().max(200).optional().default(""),
  pmid: z.string().max(50).optional().default(""),
  url: z.string().max(500).optional().default(""),
  abstract: z.string().max(5000).optional().default(""),
  type: z.enum(publicationTypeValues).default("journal"),
});

export async function addPublication(formData: FormData) {
  const adminId = await ensureAdmin();
  const parsed = addPublicationSchema.parse({
    studyId: formData.get("studyId"),
    title: formData.get("title"),
    authors: formData.get("authors") ?? "",
    journal: formData.get("journal") ?? "",
    year: formData.get("year") || undefined,
    doi: formData.get("doi") ?? "",
    pmid: formData.get("pmid") ?? "",
    url: formData.get("url") ?? "",
    abstract: formData.get("abstract") ?? "",
    type: formData.get("type") ?? "journal",
  });
  const authors = parsed.authors
    .split(",")
    .map((a) => a.trim())
    .filter(Boolean);
  const siblings = await db
    .select({ id: researchPublications.id })
    .from(researchPublications)
    .where(eq(researchPublications.studyId, parsed.studyId));
  await db.insert(researchPublications).values({
    studyId: parsed.studyId,
    title: parsed.title,
    authors,
    journal: parsed.journal ?? "",
    year: parsed.year ?? null,
    doi: parsed.doi || null,
    pmid: parsed.pmid || null,
    url: parsed.url || null,
    abstract: parsed.abstract ?? "",
    type: parsed.type,
    position: siblings.length,
  });
  await logAdminAction({
    adminUserId: adminId,
    action: "add_study_publication",
    targetId: parsed.studyId,
  });
  revalidatePath(`/admin/research/${parsed.studyId}`);
  revalidatePath(`/products/research/studies`);
  revalidatePath(`/products/research/outputs`);
}

export async function deletePublication(formData: FormData) {
  const adminId = await ensureAdmin();
  const id = z.string().uuid().parse(formData.get("id"));
  const studyId = z.string().uuid().parse(formData.get("studyId"));
  await db.delete(researchPublications).where(eq(researchPublications.id, id));
  await logAdminAction({
    adminUserId: adminId,
    action: "delete_study_publication",
    targetId: id,
  });
  revalidatePath(`/admin/research/${studyId}`);
  revalidatePath(`/products/research/outputs`);
}

// -------------------------------------------------------------------------
// Updates (news posts attached to a study)
// -------------------------------------------------------------------------

const addUpdateSchema = z.object({
  studyId: z.string().uuid(),
  title: z.string().min(3).max(300),
  body: z.string().max(8000).optional().default(""),
  postedAt: z.string().max(40).optional(),
  postedByName: z.string().max(200).optional().default(""),
});

export async function addUpdate(formData: FormData) {
  const adminId = await ensureAdmin();
  const parsed = addUpdateSchema.parse({
    studyId: formData.get("studyId"),
    title: formData.get("title"),
    body: formData.get("body") ?? "",
    postedAt: (formData.get("postedAt") as string) ?? "",
    postedByName: formData.get("postedByName") ?? "",
  });
  const postedAt = parsed.postedAt ? new Date(parsed.postedAt) : new Date();
  await db.insert(researchUpdates).values({
    studyId: parsed.studyId,
    title: parsed.title,
    body: parsed.body ?? "",
    postedAt,
    postedByName: parsed.postedByName ?? "",
  });
  await logAdminAction({
    adminUserId: adminId,
    action: "add_study_update",
    targetId: parsed.studyId,
  });
  revalidatePath(`/admin/research/${parsed.studyId}`);
  revalidatePath(`/products/research/studies`);
}

export async function deleteUpdate(formData: FormData) {
  const adminId = await ensureAdmin();
  const id = z.string().uuid().parse(formData.get("id"));
  const studyId = z.string().uuid().parse(formData.get("studyId"));
  await db.delete(researchUpdates).where(eq(researchUpdates.id, id));
  await logAdminAction({
    adminUserId: adminId,
    action: "delete_study_update",
    targetId: id,
  });
  revalidatePath(`/admin/research/${studyId}`);
}

// -------------------------------------------------------------------------
// Application + proposal review queues
// -------------------------------------------------------------------------

const decideApplicationSchema = z.object({
  id: z.string().uuid(),
  decision: z.enum(["accepted", "declined"]),
  reviewNotes: z.string().max(2000).optional().default(""),
});

export async function decideApplication(formData: FormData) {
  const adminId = await ensureAdmin();
  const parsed = decideApplicationSchema.parse({
    id: formData.get("id"),
    decision: formData.get("decision"),
    reviewNotes: formData.get("reviewNotes") ?? "",
  });
  const [row] = await db
    .select()
    .from(researchApplications)
    .where(eq(researchApplications.id, parsed.id))
    .limit(1);
  if (!row) throw new Error("Application not found");

  await db
    .update(researchApplications)
    .set({
      status: parsed.decision,
      reviewedBy: adminId,
      reviewedAt: new Date(),
      reviewNotes: parsed.reviewNotes,
    })
    .where(eq(researchApplications.id, parsed.id));

  let studyTitle = "general collaboration interest";
  if (row.studyId) {
    const [s] = await db
      .select({ title: researchStudiesTable.title })
      .from(researchStudiesTable)
      .where(eq(researchStudiesTable.id, row.studyId))
      .limit(1);
    if (s) studyTitle = s.title;
  }

  const decisionLabel =
    parsed.decision === "accepted"
      ? "Your application has been accepted. A coordinator will reach out with next steps shortly."
      : "After review we're unable to bring you onto this study right now. We encourage you to apply for future studies — orthopaedic surgeons and trainees at every level contribute to the collaboration.";

  try {
    await sendTemplatedEmail({
      to: row.email,
      templateSlug: "research-application-decision",
      variables: {
        name: row.name,
        studyTitle,
        decision: parsed.decision,
        decisionBlock: decisionLabel,
        reviewNotes: parsed.reviewNotes
          ? `Reviewer notes:\n${parsed.reviewNotes}`
          : "",
      },
    });
  } catch (err) {
    console.error("[research-application] decision email failed", err);
  }

  await logAdminAction({
    adminUserId: adminId,
    action: `research_application_${parsed.decision}`,
    targetId: parsed.id,
  });
  revalidatePath("/admin/research/applications");
  revalidatePath(`/admin/research/applications/${parsed.id}`);
}

const decideProposalSchema = z.object({
  id: z.string().uuid(),
  decision: z.enum(["accepted", "declined"]),
  reviewNotes: z.string().max(2000).optional().default(""),
});

export async function decideProposal(formData: FormData) {
  const adminId = await ensureAdmin();
  const parsed = decideProposalSchema.parse({
    id: formData.get("id"),
    decision: formData.get("decision"),
    reviewNotes: formData.get("reviewNotes") ?? "",
  });
  await db
    .update(researchProposals)
    .set({
      status: parsed.decision,
      reviewedBy: adminId,
      reviewedAt: new Date(),
      reviewNotes: parsed.reviewNotes,
    })
    .where(eq(researchProposals.id, parsed.id));
  await logAdminAction({
    adminUserId: adminId,
    action: `research_proposal_${parsed.decision}`,
    targetId: parsed.id,
  });
  revalidatePath("/admin/research/proposals");
  revalidatePath(`/admin/research/proposals/${parsed.id}`);
}
