import { createHash } from "crypto";
import { headers } from "next/headers";
import { Op, type Transaction } from "sequelize";
import { initDb } from "@/lib/db";
import type { ConsentStatus } from "@/types/participant";

export const CONSENT_KEYS = {
  participantTerms: "participant_terms",
  domesticFeed: "domestic_feed",
} as const;

/** Latest non-retired version of a policy key. */
export async function getCurrentPolicy(key: string, transaction?: Transaction) {
  const { ConsentPolicy } = initDb();
  return ConsentPolicy.findOne({
    where: {
      key,
      retiredAt: null,
      effectiveFrom: { [Op.lte]: new Date() },
    },
    order: [["version", "DESC"]],
    transaction,
  });
}

export async function getCurrentPolicies(keys: string[]) {
  const policies = await Promise.all(keys.map((k) => getCurrentPolicy(k)));
  return policies.filter((p): p is NonNullable<typeof p> => Boolean(p));
}

/**
 * Consent records are append-only; the newest record for the current
 * policy version is the participant's effective decision.
 */
export async function getLatestConsentRecord(
  participantProfileId: string,
  policyId: string,
  transaction?: Transaction
) {
  const { ParticipantConsentRecord } = initDb();
  return ParticipantConsentRecord.findOne({
    where: { participantProfileId, consentPolicyId: policyId },
    order: [["createdAt", "DESC"]],
    transaction,
  });
}

/** Returns the granting record id, or null if consent is not active. */
export async function getActiveConsentRecordId(
  participantProfileId: string,
  key: string,
  transaction?: Transaction
): Promise<string | null> {
  const policy = await getCurrentPolicy(key, transaction);
  if (!policy) return null;
  const record = await getLatestConsentRecord(
    participantProfileId,
    policy.id,
    transaction
  );
  return record?.status === "granted" ? record.id : null;
}

function requestEvidence() {
  try {
    const h = headers();
    const ip =
      h.get("x-forwarded-for")?.split(",")[0]?.trim() ||
      h.get("x-real-ip") ||
      null;
    const salt = process.env.NEXTAUTH_SECRET ?? "";
    return {
      ipHash: ip ? createHash("sha256").update(`${salt}:${ip}`).digest("hex") : null,
      userAgent: h.get("user-agent")?.slice(0, 512) ?? null,
    };
  } catch {
    return { ipHash: null, userAgent: null };
  }
}

export async function recordConsentDecision(
  input: {
    participantProfileId: string;
    policyKey: string;
    status: Extract<ConsentStatus, "granted" | "withdrawn">;
    source: string;
  },
  transaction?: Transaction
) {
  const { ParticipantConsentRecord } = initDb();
  const policy = await getCurrentPolicy(input.policyKey, transaction);
  if (!policy) throw new Error("POLICY_NOT_FOUND");

  const latest = await getLatestConsentRecord(
    input.participantProfileId,
    policy.id,
    transaction
  );
  if (latest?.status === input.status) return latest;

  const now = new Date();
  const evidence = requestEvidence();
  return ParticipantConsentRecord.create(
    {
      participantProfileId: input.participantProfileId,
      consentPolicyId: policy.id,
      status: input.status,
      grantedAt: input.status === "granted" ? now : null,
      withdrawnAt: input.status === "withdrawn" ? now : null,
      source: input.source,
      ipHash: evidence.ipHash,
      userAgent: evidence.userAgent,
      evidence: { policyVersion: policy.version, contentHash: policy.contentHash },
    },
    { transaction }
  );
}
