import { EmpruntAvecMateriel } from '../repositories/emprunt.repository';
import { MaterielResponse, toMaterielResponse } from './materiel.dto';
import { AppError } from '../errors/app-error';
import { ETAT_CHECKLIST_ELEMENT, MODE_IDENTIFICATION } from '../models/enums';

export interface EmpruntResponse {
  id: number;
  dateEmprunt: Date;
  dateRetourPrevue: Date;
  dateRetourReelle: Date | null;
  statut: string;
  materiel: MaterielResponse;
}

export function toEmpruntResponse(emprunt: EmpruntAvecMateriel): EmpruntResponse {
  return {
    id: emprunt.id,
    dateEmprunt: emprunt.dateEmprunt,
    dateRetourPrevue: emprunt.dateRetourPrevue,
    dateRetourReelle: emprunt.dateRetourReelle,
    statut: emprunt.statut,
    materiel: toMaterielResponse(emprunt.materiel),
  };
}

export interface ChecklistElementRequest {
  accessoireId?: number;
  nomElement: string;
  etat: string;
  quantiteConstatee: number;
  commentaire?: string;
}

export interface CreerEmpruntRequest {
  materielId: number;
  posteEmpruntId: number;
  modeIdentification: string;
  commentaireDepart?: string;
  elements: ChecklistElementRequest[];
}

function asObject(value: unknown, message: string): Record<string, unknown> {
  if (typeof value !== 'object' || value === null) {
    throw new AppError(400, message);
  }
  return value as Record<string, unknown>;
}

function asPositiveInt(value: unknown, champ: string): number {
  if (typeof value !== 'number' || !Number.isInteger(value) || value <= 0) {
    throw new AppError(400, `${champ} doit etre un entier positif`);
  }
  return value;
}

function asIntOrZero(value: unknown, champ: string): number {
  if (typeof value !== 'number' || !Number.isInteger(value) || value < 0) {
    throw new AppError(400, `${champ} doit etre un entier positif ou nul`);
  }
  return value;
}

function asEnum<T extends string>(value: unknown, autorisees: readonly T[], champ: string): T {
  if (typeof value !== 'string' || !(autorisees as readonly string[]).includes(value)) {
    throw new AppError(400, `${champ} invalide`);
  }
  return value as T;
}

function parseElement(value: unknown, index: number): ChecklistElementRequest {
  const obj = asObject(value, `elements[${index}] invalide`);

  if (typeof obj.nomElement !== 'string' || obj.nomElement.trim() === '') {
    throw new AppError(400, `elements[${index}].nomElement requis`);
  }

  const element: ChecklistElementRequest = {
    nomElement: obj.nomElement,
    etat: asEnum(obj.etat, ETAT_CHECKLIST_ELEMENT, `elements[${index}].etat`),
    quantiteConstatee: asIntOrZero(obj.quantiteConstatee, `elements[${index}].quantiteConstatee`),
  };

  if (obj.accessoireId !== undefined) {
    element.accessoireId = asPositiveInt(obj.accessoireId, `elements[${index}].accessoireId`);
  }
  if (obj.commentaire !== undefined) {
    if (typeof obj.commentaire !== 'string') {
      throw new AppError(400, `elements[${index}].commentaire invalide`);
    }
    element.commentaire = obj.commentaire;
  }

  return element;
}

export function parseCreerEmpruntRequest(body: unknown): CreerEmpruntRequest {
  const obj = asObject(body, 'Corps de requete invalide');

  if (!Array.isArray(obj.elements) || obj.elements.length === 0) {
    throw new AppError(400, 'La checklist de depart est obligatoire (RG11)');
  }

  const request: CreerEmpruntRequest = {
    materielId: asPositiveInt(obj.materielId, 'materielId'),
    posteEmpruntId: asPositiveInt(obj.posteEmpruntId, 'posteEmpruntId'),
    modeIdentification: asEnum(obj.modeIdentification, MODE_IDENTIFICATION, 'modeIdentification'),
    elements: obj.elements.map((element, index) => parseElement(element, index)),
  };

  if (obj.commentaireDepart !== undefined) {
    if (typeof obj.commentaireDepart !== 'string') {
      throw new AppError(400, 'commentaireDepart invalide');
    }
    request.commentaireDepart = obj.commentaireDepart;
  }

  return request;
}

export interface RestituerEmpruntRequest {
  modeIdentification: string;
  commentaireRetour?: string;
  elements: ChecklistElementRequest[];
}

export function parseRestituerEmpruntRequest(body: unknown): RestituerEmpruntRequest {
  const obj = asObject(body, 'Corps de requete invalide');

  if (!Array.isArray(obj.elements) || obj.elements.length === 0) {
    throw new AppError(400, 'La checklist de retour est obligatoire (RG16)');
  }

  const request: RestituerEmpruntRequest = {
    modeIdentification: asEnum(obj.modeIdentification, MODE_IDENTIFICATION, 'modeIdentification'),
    elements: obj.elements.map((element, index) => parseElement(element, index)),
  };

  if (obj.commentaireRetour !== undefined) {
    if (typeof obj.commentaireRetour !== 'string') {
      throw new AppError(400, 'commentaireRetour invalide');
    }
    request.commentaireRetour = obj.commentaireRetour;
  }

  return request;
}
