import { prisma } from "@/lib/prisma";
import type { Prisma } from "@/generated/prisma/client";
import {
  PROCEDURE_FOLLOWUP_TYPES,
  type ProcedureFollowupInput,
  type ProcedureRequestInput,
} from "@/validators/procedures";

const procedureInclude = {
  employee: {
    select: { COS: true, TIT: true, NSA: true, PRE: true },
  },
  created_by: {
    select: { id: true, username: true, email: true },
  },
  followup: {
    include: {
      completed_by: {
        select: { id: true, username: true, email: true },
      },
    },
  },
  history: {
    orderBy: { created_at: "desc" as const },
    include: {
      created_by: {
        select: { id: true, username: true, email: true },
      },
    },
  },
} satisfies Prisma.procedure_requestsInclude;

function dateOnly(value: string | null | undefined) {
  return value ? new Date(`${value}T00:00:00`) : null;
}

function dateTime(value: string | null | undefined) {
  return value ? new Date(value) : null;
}

function requestData(input: ProcedureRequestInput) {
  return {
    manager_name: input.managerName,
    request_type: input.requestType,
    other_type: input.requestType === "AUTRE" ? input.otherType : null,
    facts_date: dateOnly(input.factsDate),
    facts_time: input.factsTime,
    observed_by: input.observedBy,
    facts_location: input.factsLocation,
    facts_description: input.factsDescription,
    witnesses: input.witnesses,
    written_statements: input.writtenStatements ?? null,
  };
}

function followupData(input: ProcedureFollowupInput, userId: number) {
  return {
    interview_at: dateTime(input.interviewAt),
    interviewer_name: input.interviewerName,
    employee_present: input.employeePresent,
    accompanied_by: input.accompaniedBy,
    decision_type: input.decisionType,
    other_decision:
      input.decisionType === "AUTRE" ? input.otherDecision : null,
    suspension_days:
      input.decisionType === "MISE_A_PIED" ? input.suspensionDays : null,
    suspension_start:
      input.decisionType === "MISE_A_PIED"
        ? dateOnly(input.suspensionStart)
        : null,
    suspension_end:
      input.decisionType === "MISE_A_PIED"
        ? dateOnly(input.suspensionEnd)
        : null,
    decision_reasons: input.decisionReasons,
    facts_acknowledged: input.factsAcknowledged,
    employee_statement: input.employeeStatement,
    decision_maker_name: input.decisionMakerName,
    hr_validation_sent_at: dateTime(input.hrValidationSentAt),
    notification_sent_at: dateTime(input.notificationSentAt),
    completed_by_user_id: userId,
  };
}

function snapshot(value: unknown): Prisma.InputJsonValue {
  return JSON.parse(JSON.stringify(value)) as Prisma.InputJsonValue;
}

export function supportsProcedureFollowup(requestType: string) {
  return (PROCEDURE_FOLLOWUP_TYPES as readonly string[]).includes(requestType);
}

export async function listProceduresForEmployee(employeeCos: number) {
  return prisma.procedure_requests.findMany({
    where: { employee_cos: employeeCos },
    orderBy: { created_at: "desc" },
    include: procedureInclude,
  });
}

export async function getProcedureById(id: number) {
  return prisma.procedure_requests.findUnique({
    where: { id },
    include: procedureInclude,
  });
}

export async function createProcedure(
  employeeCos: number,
  input: ProcedureRequestInput,
  userId: number,
) {
  return prisma.$transaction(async (tx) => {
    const created = await tx.procedure_requests.create({
      data: {
        employee_cos: employeeCos,
        created_by_user_id: userId,
        status: "ENREGISTREE",
        ...requestData(input),
      },
    });

    await tx.procedure_request_history.create({
      data: {
        procedure_request_id: created.id,
        action: "CREATION",
        summary: "Demande de procédure enregistrée",
        snapshot: snapshot(created),
        created_by_user_id: userId,
      },
    });

    return tx.procedure_requests.findUniqueOrThrow({
      where: { id: created.id },
      include: procedureInclude,
    });
  });
}

export async function updateProcedure(
  id: number,
  input: ProcedureRequestInput,
  userId: number,
) {
  return prisma.$transaction(async (tx) => {
    const updated = await tx.procedure_requests.update({
      where: { id },
      data: requestData(input),
    });

    await tx.procedure_request_history.create({
      data: {
        procedure_request_id: id,
        action: "MODIFICATION",
        summary: "Demande initiale modifiée",
        snapshot: snapshot(updated),
        created_by_user_id: userId,
      },
    });

    return tx.procedure_requests.findUniqueOrThrow({
      where: { id },
      include: procedureInclude,
    });
  });
}

export async function saveProcedureFollowup(
  id: number,
  input: ProcedureFollowupInput,
  userId: number,
) {
  return prisma.$transaction(async (tx) => {
    const request = await tx.procedure_requests.findUniqueOrThrow({
      where: { id },
    });

    if (!supportsProcedureFollowup(request.request_type)) {
      throw new Error("FOLLOWUP_NOT_ALLOWED");
    }

    const saved = await tx.procedure_followups.upsert({
      where: { procedure_request_id: id },
      create: {
        procedure_request_id: id,
        ...followupData(input, userId),
      },
      update: followupData(input, userId),
    });

    await tx.procedure_requests.update({
      where: { id },
      data: { status: "SUITE_COMPLETEE" },
    });

    await tx.procedure_request_history.create({
      data: {
        procedure_request_id: id,
        action: request.status === "SUITE_COMPLETEE" ? "MODIFICATION_SUITE" : "COMPLETION_SUITE",
        summary: "Suite de procédure enregistrée",
        snapshot: snapshot(saved),
        created_by_user_id: userId,
      },
    });

    return tx.procedure_requests.findUniqueOrThrow({
      where: { id },
      include: procedureInclude,
    });
  });
}
