import { deriveYearAndTerm, formatEventDate, generateRequestId, slugify } from "./eventPayload";
import { publishEvent, type BackendConfig, type PublishEventPayload, type PublishResult } from "./backendClient";
import type { EventerSession } from "./auth-oidc/types";

export interface SubmitFormBody {
  name?: unknown;
  short?: unknown;
  startLocal?: unknown;
  endLocal?: unknown;
  online?: unknown;
  location?: unknown;
  descriptionMarkdown?: unknown;
  registerLink?: unknown;
  poster?: { sourceUrl?: unknown; filename?: unknown; contentType?: unknown } | null;
}

export interface SubmitOutcome {
  ok: boolean;
  status: number;
  message: string;
  pullRequestUrl?: string | null;
}

type Publish = (config: BackendConfig, payload: PublishEventPayload) => Promise<PublishResult>;

function requireString(value: unknown, field: string): string {
  if (typeof value !== "string" || value.trim().length === 0) {
    throw new Error(`${field} is required`);
  }
  return value;
}

// Builds backend/'s expected payload from the raw form body and calls its
// publish endpoint. backend/src/validateRequest.ts remains the
// authoritative validator for the event fields themselves -- this only
// does the minimal shape checks needed to build a well-formed request
// (missing fields, bad dates) and relays backend/'s own validation errors
// back rather than re-implementing its rules a second time.
export async function handleEventSubmission(
  body: SubmitFormBody,
  session: EventerSession,
  backend: BackendConfig,
  publish: Publish = publishEvent
): Promise<SubmitOutcome> {
  let name: string;
  let short: string;
  let startLocal: string;
  let location: string;
  let descriptionMarkdown: string;
  try {
    name = requireString(body.name, "name");
    short = requireString(body.short, "short");
    startLocal = requireString(body.startLocal, "startLocal");
    location = requireString(body.location, "location");
    descriptionMarkdown = requireString(body.descriptionMarkdown, "descriptionMarkdown");
  } catch (error) {
    return { ok: false, status: 400, message: error instanceof Error ? error.message : String(error) };
  }

  const startDate = formatEventDate(startLocal);
  const yearAndTerm = deriveYearAndTerm(startLocal);
  if (!startDate || !yearAndTerm) {
    return { ok: false, status: 400, message: "startLocal must be a valid date/time" };
  }

  let endDate: string | null = null;
  if (typeof body.endLocal === "string" && body.endLocal.length > 0) {
    endDate = formatEventDate(body.endLocal);
    if (!endDate) {
      return { ok: false, status: 400, message: "endLocal must be a valid date/time" };
    }
  }

  const slug = slugify(name);
  // WatIAM usernames are the local part of a uwaterloo.ca email -- used as
  // a fallback since the id_token's email claim isn't guaranteed to be
  // populated depending on how the Keycloak realm is configured.
  const submittedByEmail = session.email ?? `${session.username}@uwaterloo.ca`;

  let poster: { sourceUrl: string; filename: string; contentType: string } | null = null;
  if (body.poster && typeof body.poster === "object") {
    const p = body.poster as Record<string, unknown>;
    if (typeof p.sourceUrl === "string" && typeof p.filename === "string" && typeof p.contentType === "string") {
      poster = { sourceUrl: p.sourceUrl, filename: p.filename, contentType: p.contentType };
    }
  }

  const result = await publish(backend, {
    requestId: generateRequestId(slug),
    submittedByEmail,
    event: {
      name,
      short,
      startDate,
      endDate,
      online: body.online === true,
      location,
      descriptionMarkdown,
      registerLink: typeof body.registerLink === "string" && body.registerLink.length > 0 ? body.registerLink : null,
      year: yearAndTerm.year,
      term: yearAndTerm.term,
      slug,
      poster,
    },
  });

  if (!result.ok) {
    const errorBody = result.body as { error?: { message?: string } } | null;
    return {
      ok: false,
      status: result.status,
      message: errorBody?.error?.message ?? `backend returned ${result.status}`,
    };
  }

  const responseBody = result.body as { status?: string; pullRequestUrl?: string | null };
  return {
    ok: true,
    status: 201,
    message: `Submitted (status: ${responseBody.status ?? "unknown"})`,
    pullRequestUrl: responseBody.pullRequestUrl ?? null,
  };
}
