"use server";

import { prisma } from "@/lib/prisma";
import { createAuditLog } from "@/lib/audit";
import { getCurrentUser } from "@/actions/auth.actions";
import { revalidatePath } from "next/cache";
import { generateNextDIN } from "@/lib/din";
import { dinLookupSchema, revokeDINSchema } from "@/lib/validators/document-integrity";
import { createHash } from "crypto";

type ActionResult<T = unknown> =
  | { success: true; data: T }
  | { success: false; error: string };

// ────────────────────────────────────────────────────────────
// Issue DIN
// ────────────────────────────────────────────────────────────

/**
 * Issue a Document Identification Number for a finalized document pack.
 * Called automatically when a pack is finalized.
 */
export async function issueDIN(
  documentPackId: string
): Promise<ActionResult<{ din: string; id: string }>> {
  try {
    const user = await getCurrentUser();
    if (!user) {
      return { success: false, error: "Authentication required" };
    }

    // Verify ownership and get pack details
    const pack = await prisma.documentPack.findFirst({
      where: { id: documentPackId, userId: user.id },
      include: { documentIdentifier: true },
    });

    if (!pack) {
      return { success: false, error: "Document pack not found" };
    }

    if (pack.status !== "FINAL") {
      return { success: false, error: "Document pack must be finalized before issuing a DIN" };
    }

    // Check if DIN already issued
    if (pack.documentIdentifier) {
      return {
        success: true,
        data: { din: pack.documentIdentifier.din, id: pack.documentIdentifier.id },
      };
    }

    // Generate next DIN for the state
    const stateCode = pack.state as string;
    const { din, year, sequence } = await generateNextDIN(stateCode);

    // Create DocumentIdentifier
    const identifier = await prisma.documentIdentifier.create({
      data: {
        din,
        year,
        stateCode,
        sequence,
        documentPackId: pack.id,
        calculationId: pack.calculationId,
        userId: user.id,
        status: "ACTIVE",
      },
    });

    await createAuditLog({
      userId: user.id,
      action: "DIN_ISSUED",
      entityType: "DocumentIdentifier",
      entityId: identifier.id,
      afterState: { din, documentPackId, calculationId: pack.calculationId, stateCode },
    });

    revalidatePath("/dashboard/documents");

    return { success: true, data: { din, id: identifier.id } };
  } catch (error) {
    console.error("DIN issue error:", error);
    return { success: false, error: "Failed to issue DIN" };
  }
}

// ────────────────────────────────────────────────────────────
// Lookup by DIN (Public — no auth required)
// ────────────────────────────────────────────────────────────

interface VerificationData {
  din: string;
  status: string;
  issuedAt: string;
  declarantName: string;
  doctrine: string;
  state: string;
  calculationDate: string;
  heirs: Array<{ name: string; relationship: string; percentage: number }>;
  tamperFree: boolean;
  certificateHash: string | null;
  revokedAt?: string | null;
  revokedReason?: string | null;
}

export async function lookupByDIN(
  dinInput: string
): Promise<ActionResult<VerificationData>> {
  try {
    const parsed = dinLookupSchema.safeParse({ din: dinInput });
    if (!parsed.success) {
      return { success: false, error: "Invalid DIN format. Expected: WV/YYYY/XX/NNNNN" };
    }

    const identifier = await prisma.documentIdentifier.findUnique({
      where: { din: parsed.data.din },
      include: {
        user: { select: { name: true } },
        calculation: {
          select: {
            doctrine: true,
            certificateHash: true,
            calculationTrace: true,
            createdAt: true,
          },
        },
        documentPack: {
          select: {
            state: true,
            packHash: true,
          },
        },
      },
    });

    if (!identifier) {
      return { success: false, error: "No document found with this DIN" };
    }

    // Fetch heir shares
    const shares = await prisma.calculationShare.findMany({
      where: { calculationId: identifier.calculationId },
      select: { heirName: true, relationship: true, percentage: true },
    });

    // Tamper detection: recompute hash from trace
    let tamperFree = true;
    if (identifier.calculation.certificateHash && identifier.calculation.calculationTrace) {
      const traceStr = JSON.stringify(identifier.calculation.calculationTrace);
      const recomputedHash = createHash("sha256").update(traceStr).digest("hex");
      tamperFree = recomputedHash === identifier.calculation.certificateHash;
    }

    return {
      success: true,
      data: {
        din: identifier.din,
        status: identifier.status,
        issuedAt: identifier.issuedAt.toISOString(),
        declarantName: identifier.user.name,
        doctrine: identifier.calculation.doctrine,
        state: identifier.documentPack.state,
        calculationDate: identifier.calculation.createdAt.toISOString(),
        heirs: shares.map((s) => ({
          name: s.heirName,
          relationship: s.relationship,
          percentage: s.percentage,
        })),
        tamperFree,
        certificateHash: identifier.calculation.certificateHash,
        revokedAt: identifier.revokedAt?.toISOString() || null,
        revokedReason: identifier.revokedReason,
      },
    };
  } catch (error) {
    console.error("DIN lookup error:", error);
    return { success: false, error: "Failed to look up DIN" };
  }
}

// ────────────────────────────────────────────────────────────
// Lookup by Hash (Public — no auth required)
// ────────────────────────────────────────────────────────────

export async function lookupByHash(
  certificateHash: string
): Promise<ActionResult<VerificationData>> {
  try {
    if (!certificateHash || certificateHash.length < 10) {
      return { success: false, error: "Invalid certificate hash" };
    }

    const calculation = await prisma.inheritanceCalculation.findFirst({
      where: { certificateHash },
      include: {
        user: { select: { name: true } },
        shares: { select: { heirName: true, relationship: true, percentage: true } },
        documentIdentifier: { select: { din: true, status: true, issuedAt: true, revokedAt: true, revokedReason: true } },
        documentPacks: { select: { state: true }, take: 1 },
      },
    });

    if (!calculation) {
      return { success: false, error: "No document found with this certificate hash" };
    }

    // Tamper detection
    let tamperFree = true;
    if (calculation.calculationTrace) {
      const traceStr = JSON.stringify(calculation.calculationTrace);
      const recomputedHash = createHash("sha256").update(traceStr).digest("hex");
      tamperFree = recomputedHash === certificateHash;
    }

    const din = calculation.documentIdentifier?.din || "Not yet issued";
    const status = calculation.documentIdentifier?.status || "PENDING";

    return {
      success: true,
      data: {
        din,
        status,
        issuedAt: calculation.documentIdentifier?.issuedAt?.toISOString() || calculation.createdAt.toISOString(),
        declarantName: calculation.user.name,
        doctrine: calculation.doctrine,
        state: calculation.documentPacks[0]?.state || "N/A",
        calculationDate: calculation.createdAt.toISOString(),
        heirs: calculation.shares.map((s) => ({
          name: s.heirName,
          relationship: s.relationship,
          percentage: s.percentage,
        })),
        tamperFree,
        certificateHash,
        revokedAt: calculation.documentIdentifier?.revokedAt?.toISOString() || null,
        revokedReason: calculation.documentIdentifier?.revokedReason || null,
      },
    };
  } catch (error) {
    console.error("Hash lookup error:", error);
    return { success: false, error: "Failed to look up certificate hash" };
  }
}

// ────────────────────────────────────────────────────────────
// Revoke DIN
// ────────────────────────────────────────────────────────────

export async function revokeDIN(
  id: string,
  reason: string
): Promise<ActionResult> {
  try {
    const user = await getCurrentUser();
    if (!user) {
      return { success: false, error: "Authentication required" };
    }

    const validated = revokeDINSchema.safeParse({ id, reason });
    if (!validated.success) {
      return { success: false, error: validated.error.errors[0]?.message || "Invalid input" };
    }

    const identifier = await prisma.documentIdentifier.findFirst({
      where: { id, userId: user.id },
    });

    if (!identifier) {
      return { success: false, error: "Document identifier not found" };
    }

    if (identifier.status === "REVOKED") {
      return { success: false, error: "DIN is already revoked" };
    }

    const updated = await prisma.documentIdentifier.update({
      where: { id },
      data: {
        status: "REVOKED",
        revokedAt: new Date(),
        revokedReason: reason,
      },
    });

    await createAuditLog({
      userId: user.id,
      action: "DIN_REVOKED",
      entityType: "DocumentIdentifier",
      entityId: id,
      beforeState: { status: identifier.status },
      afterState: { status: "REVOKED", reason },
    });

    revalidatePath("/dashboard/documents");

    return { success: true, data: updated };
  } catch (error) {
    console.error("DIN revoke error:", error);
    return { success: false, error: "Failed to revoke DIN" };
  }
}
