/**
 * Serialization utilities for converting Prisma objects to plain JavaScript objects
 * that can be safely passed from Server Components to Client Components in Next.js.
 * 
 * Handles:
 * - Decimal objects -> numbers
 * - Date objects -> ISO strings
 * - Nested structures (photos, faces, people, tags)
 */

type Decimal = {
  toNumber(): number;
  toString(): string;
};

/**
 * Checks if a value is a Prisma Decimal object
 */
function isDecimal(value: any): value is Decimal {
  return (
    value !== null &&
    typeof value === 'object' &&
    typeof value.toNumber === 'function' &&
    typeof value.toString === 'function'
  );
}

/**
 * Converts a Decimal to a number, handling null/undefined
 */
function decimalToNumber(value: any): number | null {
  if (value === null || value === undefined) {
    return null;
  }
  if (isDecimal(value)) {
    return value.toNumber();
  }
  if (typeof value === 'number') {
    return value;
  }
  // Fallback: try to parse as number
  const parsed = Number(value);
  return isNaN(parsed) ? null : parsed;
}

/**
 * Serializes a Date object to an ISO string
 */
function serializeDate(value: any): string | null {
  if (value === null || value === undefined) {
    return null;
  }
  if (value instanceof Date) {
    return value.toISOString();
  }
  if (typeof value === 'string') {
    return value;
  }
  return null;
}

/**
 * Serializes a Person object
 */
function serializePerson(person: any): any {
  if (!person) {
    return null;
  }

  return {
    id: person.id,
    first_name: person.first_name,
    last_name: person.last_name,
    middle_name: person.middle_name ?? null,
    maiden_name: person.maiden_name ?? null,
    date_of_birth: serializeDate(person.date_of_birth),
    created_date: serializeDate(person.created_date),
  };
}

/**
 * Serializes a Face object, converting Decimal fields to numbers
 */
function serializeFace(face: any): any {
  if (!face) {
    return null;
  }

  const serialized: any = {
    id: face.id,
    photo_id: face.photo_id,
    person_id: face.person_id ?? null,
    location: face.location,
    confidence: decimalToNumber(face.confidence) ?? 0,
    quality_score: decimalToNumber(face.quality_score) ?? 0,
    is_primary_encoding: face.is_primary_encoding ?? false,
    detector_backend: face.detector_backend,
    model_name: face.model_name,
    face_confidence: decimalToNumber(face.face_confidence) ?? 0,
    exif_orientation: face.exif_orientation ?? null,
    pose_mode: face.pose_mode,
    yaw_angle: decimalToNumber(face.yaw_angle),
    pitch_angle: decimalToNumber(face.pitch_angle),
    roll_angle: decimalToNumber(face.roll_angle),
    landmarks: face.landmarks ?? null,
    identified_by_user_id: face.identified_by_user_id ?? null,
    excluded: face.excluded ?? false,
  };

  // Handle nested Person object (if present)
  if (face.Person) {
    serialized.Person = serializePerson(face.Person);
  } else if (face.person) {
    serialized.person = serializePerson(face.person);
  }

  return serialized;
}

/**
 * Serializes a Tag object
 */
function serializeTag(tag: any): any {
  if (!tag) {
    return null;
  }

  return {
    id: tag.id,
    tagName: tag.tag_name || tag.tagName,
    created_date: serializeDate(tag.created_date),
  };
}

/**
 * Serializes a PhotoTagLinkage object
 */
function serializePhotoTagLinkage(linkage: any): any {
  if (!linkage) {
    return null;
  }

  const serialized: any = {
    linkage_id: linkage.linkage_id ?? linkage.id,
    photo_id: linkage.photo_id,
    tag_id: linkage.tag_id,
    linkage_type: linkage.linkage_type ?? 0,
    created_date: serializeDate(linkage.created_date),
  };

  // Handle nested Tag object (if present)
  if (linkage.Tag || linkage.tag) {
    serialized.tag = serializeTag(linkage.Tag || linkage.tag);
    // Also keep Tag for backward compatibility
    serialized.Tag = serialized.tag;
  }
  if (linkage.Tag || linkage.tag) {
    // Also keep Tag for backward compatibility
    serialized.Tag = serialized.tag;
  }

  return serialized;
}

/**
 * Serializes a single Photo object with all nested structures
 */
export function serializePhoto(photo: any): any {
  if (!photo) {
    return null;
  }

  const serialized: any = {
    id: photo.id,
    path: photo.path,
    filename: photo.filename,
    date_added: serializeDate(photo.date_added),
    date_taken: serializeDate(photo.date_taken),
    processed: photo.processed ?? false,
    media_type: photo.media_type ?? null,
  };

  // Handle Face array (can be named Face or faces)
  if (photo.Face && Array.isArray(photo.Face)) {
    serialized.Face = photo.Face.map((face: any) => serializeFace(face));
  } else if (photo.faces && Array.isArray(photo.faces)) {
    serialized.faces = photo.faces.map((face: any) => serializeFace(face));
  }

  // Handle PhotoTagLinkage array (can be named PhotoTagLinkage or photoTags)
  if (photo.PhotoTagLinkage && Array.isArray(photo.PhotoTagLinkage)) {
    serialized.PhotoTagLinkage = photo.PhotoTagLinkage.map((linkage: any) =>
      serializePhotoTagLinkage(linkage)
    );
  } else if (photo.photoTags && Array.isArray(photo.photoTags)) {
    serialized.photoTags = photo.photoTags.map((linkage: any) =>
      serializePhotoTagLinkage(linkage)
    );
  }

  return serialized;
}

/**
 * Serializes an array of Photo objects
 */
export function serializePhotos(photos: any[]): any[] {
  if (!Array.isArray(photos)) {
    return [];
  }

  return photos.map((photo) => serializePhoto(photo));
}

/**
 * Serializes an array of Person objects
 */
export function serializePeople(people: any[]): any[] {
  if (!Array.isArray(people)) {
    return [];
  }
  return people.map((person) => serializePerson(person));
}

/**
 * Serializes an array of Tag objects
 */
export function serializeTags(tags: any[]): any[] {
  if (!Array.isArray(tags)) {
    return [];
  }
  return tags.map((tag) => serializeTag(tag));
}
