import { ok, err, type CommandContext } from "@tailor-platform/erp-kit/core";
import type { Transaction } from "../generated/kysely-tailordb";
import {
  MissingRequiredFieldError,
  TemplateNotFoundError,
  UndeclaredVariableReferenceError,
} from "../lib/errors.generated";
import { extractPlaceholders, parseVariableSchema } from "./createNotificationTemplate";

export interface UpdateNotificationTemplateInput {
  templateId?: string;
  eventType?: string;
  channelId?: string;
  locale?: string;
  subject?: string;
  body?: string;
  htmlBody?: string | null;
  /** JSON-encoded VariableSchemaShape string */
  variableSchema?: string;
}

/**
 * Updates a NotificationTemplate's mutable fields, re-validating placeholder references against the variable schema. Does not rewrite already-sent Notifications.
 */
export async function run(
  db: Transaction,
  input: UpdateNotificationTemplateInput,
  _ctx: CommandContext,
) {
  const { templateId, eventType, channelId, locale, subject, body, htmlBody, variableSchema } =
    input;

  // Identify the row
  let lookup = db.selectFrom("NotificationTemplate").selectAll();
  if (templateId) {
    lookup = lookup.where("id", "=", templateId);
  } else if (eventType && channelId && locale) {
    lookup = lookup
      .where("eventType", "=", eventType)
      .where("channelId", "=", channelId)
      .where("locale", "=", locale);
  } else {
    return err(new TemplateNotFoundError("(no identifier supplied)"));
  }

  const existing = await lookup.forUpdate().executeTakeFirst();
  if (!existing) {
    return err(new TemplateNotFoundError(templateId ?? `${eventType}:${channelId}:${locale}`));
  }

  const hasMutation =
    subject !== undefined ||
    body !== undefined ||
    htmlBody !== undefined ||
    variableSchema !== undefined;
  if (!hasMutation) {
    return err(new MissingRequiredFieldError("at least one mutable field"));
  }

  if (subject !== undefined && subject.trim() === "") {
    return err(new MissingRequiredFieldError("subject"));
  }
  if (body !== undefined && body.trim() === "") {
    return err(new MissingRequiredFieldError("body"));
  }

  const nextSubject = subject !== undefined ? subject : existing.subject;
  const nextBody = body !== undefined ? body : existing.body;
  const nextHtmlBody = htmlBody !== undefined ? htmlBody : existing.htmlBody;
  const nextVariableSchemaRaw =
    variableSchema !== undefined ? variableSchema : existing.variableSchema;

  const schema = parseVariableSchema(nextVariableSchemaRaw);
  if (!schema) {
    return err(new MissingRequiredFieldError("variableSchema"));
  }

  const declared = new Set(schema.variables.map((v) => v.name));
  const referenced = extractPlaceholders(nextSubject, nextBody, nextHtmlBody);
  for (const ref of referenced) {
    if (!declared.has(ref)) {
      return err(new UndeclaredVariableReferenceError(ref));
    }
  }

  const now = new Date();
  let update = db
    .updateTable("NotificationTemplate")
    .set("updatedAt", now)
    .where("id", "=", existing.id);

  if (subject !== undefined) update = update.set("subject", subject);
  if (body !== undefined) update = update.set("body", body);
  if (htmlBody !== undefined) update = update.set("htmlBody", htmlBody);
  if (variableSchema !== undefined) update = update.set("variableSchema", variableSchema);

  await update.execute();

  return ok({
    template: {
      ...existing,
      subject: nextSubject,
      body: nextBody,
      htmlBody: nextHtmlBody,
      variableSchema: nextVariableSchemaRaw,
      updatedAt: now,
    },
  });
}
