/**
 * Avatar Service
 * Generate and manage character avatars using DiceBear API
 * Ported from Python character/character_manager.py
 */

import { Person, Sex } from '../../models/Person.js';
import { avatarUrlFor } from './avatarLibrary.js';

// Avatar configuration types
export interface AvatarSettings {
  hair: string;
  hairColor: string;
  facialHair: string;
  accessory: string;
  skinColor: string;
  clothing: string;
  mouth: string;
  eyes: string;
  eyebrows: string;
}

// Hair color options
const HAIR_COLORS = [
  { name: 'Black', hex: '2c1b18' },
  { name: 'Dark Brown', hex: '4a312c' },
  { name: 'Chestnut Brown', hex: '724133' },
  { name: 'Auburn', hex: 'a55728' },
  { name: 'Golden Brown', hex: 'b58143' },
  { name: 'Red', hex: 'c93305' },
  { name: 'Blonde', hex: 'd6b370' },
  { name: 'Light Grey', hex: 'e8e1e1' },
  { name: 'Dark Grey', hex: 'ecdcbf' },
  { name: 'Copper', hex: 'f59797' },
];

// Skin color options
const SKIN_COLORS = ['614335', 'ae5d29', 'd08b5b', 'edb98a', 'f8d25c', 'fd9841', 'ffdbb4'];

// Clothes color options
const CLOTHES_COLORS = [
  '3c4f5c', '65c9ff', '262e33', '5199e4', '25557c', '929598', 'a7ffc4',
  'b1e2ff', 'e6e6e6', 'ff5c5c', 'ff488e', 'ffafb9', 'ffffb1', 'ffffff',
];

// Clothing graphic options
const CLOTHING_GRAPHICS = [
  'bat', 'bear', 'cumbia', 'deer', 'diamond', 'hola', 'pizza', 'resist', 'skull', 'skullOutline',
];

// Clothing options (DiceBear 7.x avataaars valid values)
const CLOTHING_OPTIONS = [
  'blazerAndShirt', 'blazerAndSweater', 'collarAndSweater', 'graphicShirt',
  'hoodie', 'overall', 'shirtCrewNeck', 'shirtScoopNeck', 'shirtVNeck'
];

// Mouth options
const MOUTH_OPTIONS = ['concerned', 'default', 'disbelief', 'sad', 'serious', 'smile', 'twinkle'];

// Accessory options
const ACCESSORY_OPTIONS = ['NONE', 'round', 'round', 'round'];

// Male hair types
const MALE_HAIR_TYPES = [
  'dreads', 'dreads01', 'dreads02', 'fro', 'shaggy', 'shaggyMullet',
  'shortFlat', 'theCaesar', 'theCaesarAndSidePart', 'shavedSides',
  'sides', 'shortWaved', 'frizzle', 'shortRound', 'shortCurly',
];

// Female hair types
const FEMALE_HAIR_TYPES = [
  'bigHair', 'bob', 'bun', 'curly', 'fro', 'curvy', 'froBand',
  'longButNotTooLong', 'miaWallace', 'straight01', 'straight02', 'straightAndStrand',
];

// Facial hair options
const FACIAL_HAIR_OPTIONS = ['beardLight', 'beardMedium', 'moustacheMagnum'];

/**
 * Get a random element from an array
 */
function randomChoice<T>(arr: T[]): T {
  return arr[Math.floor(Math.random() * arr.length)];
}

/**
 * Get hex color code by name
 */
function getHexByName(colorName: string): string | undefined {
  const color = HAIR_COLORS.find((c) => c.name === colorName);
  return color?.hex;
}

/**
 * Get appropriate hair color based on person's age and skin tone
 */
export function getHairColor(person: Person): string {
  const skinColor = (person as unknown as { avatar_settings?: AvatarSettings }).avatar_settings?.skinColor ?? 'LIGHT';
  let hairColors: (string | undefined)[];

  if (skinColor === 'PALE' || skinColor === 'LIGHT') {
    hairColors = [
      getHexByName('Auburn'),
      getHexByName('Blonde'),
      getHexByName('Chestnut Brown'),
      getHexByName('Blonde'),
    ];
  } else if (skinColor === 'BROWN') {
    hairColors = [
      getHexByName('Black'),
      getHexByName('Chestnut Brown'),
      getHexByName('Dark Brown'),
    ];
  } else if (skinColor === 'DARK_BROWN' || skinColor === 'BLACK') {
    hairColors = [getHexByName('Black'), getHexByName('Dark Brown')];
  } else {
    hairColors = [
      getHexByName('Auburn'),
      getHexByName('Golden Brown'),
      getHexByName('Red'),
    ];
  }

  // Filter out undefined values
  const validColors = hairColors.filter((c): c is string => c !== undefined);

  // Adjusting for older people to have a higher chance of lighter hair
  if (person.ageYears > 50) {
    const blonde = getHexByName('Blonde');
    if (blonde && validColors.includes(blonde)) {
      validColors.push(blonde, blonde, blonde, blonde);
    }
    const grey = getHexByName('Light Grey');
    if (grey) {
      validColors.push(grey, grey);
    }
  }

  return randomChoice(validColors);
}

/**
 * Get appropriate hair type based on person's age and sex
 */
export function getHairType(person: Person): string {
  if (person.ageYears < 1) {
    return 'NONE';
  }

  if (person.ageYears > 50 && person.sex === 'Male') {
    return randomChoice(['NONE', 'sides']);
  }

  if (person.sex === 'Male') {
    return randomChoice(MALE_HAIR_TYPES);
  } else {
    return randomChoice(FEMALE_HAIR_TYPES);
  }
}

/**
 * Get appropriate facial hair based on person's age and sex
 */
export function getFacialHair(person: Person): string {
  if (person.sex === 'Male' && person.ageYears >= 18 && person.ageYears <= 50) {
    // 30% chance of having facial hair
    if (Math.random() < 0.3) {
      return randomChoice(FACIAL_HAIR_OPTIONS);
    }
  }
  return 'NONE';
}

/**
 * Get appropriate accessory (glasses) based on person's age
 */
export function getAccessory(person: Person): string {
  let probs: number[];

  if (person.ageYears < 18) {
    // 20% of children wear glasses
    probs = [0.8, 0.067, 0.067, 0.067];
  } else if (person.ageYears <= 39) {
    // 40% of young adults wear glasses
    probs = [0.6, 0.133, 0.133, 0.133];
  } else {
    // 60% of older adults wear glasses
    probs = [0.4, 0.2, 0.2, 0.2];
  }

  // Weighted random choice
  const random = Math.random();
  let cumulative = 0;
  for (let i = 0; i < probs.length; i++) {
    cumulative += probs[i];
    if (random < cumulative) {
      return ACCESSORY_OPTIONS[i];
    }
  }

  return 'NONE';
}

/**
 * Get random skin color
 */
export function getRandomSkinColor(): string {
  return randomChoice(SKIN_COLORS);
}

/**
 * Get random clothing
 */
export function getRandomClothing(): string {
  return randomChoice(CLOTHING_OPTIONS);
}

/**
 * Get random mouth
 */
export function getRandomMouth(): string {
  return randomChoice(MOUTH_OPTIONS);
}

/**
 * Generate default avatar settings for a person
 */
export function generateAvatarSettings(person: Person): AvatarSettings {
  return {
    hair: getHairType(person),
    hairColor: getHairColor(person),
    facialHair: getFacialHair(person),
    accessory: getAccessory(person),
    skinColor: getRandomSkinColor(),
    clothing: getRandomClothing(),
    mouth: getRandomMouth(),
    eyes: 'default',
    eyebrows: 'default',
  };
}

/**
 * Generate and set avatar URL for a person using DiceBear API
 */
export function setAvatar(person: Person): string {
  // Preferred: pick from the curated, pre-generated avatar library (GCS-hosted).
  // Deterministic per character id, so the look is stable and ages correctly.
  const libraryUrl = avatarUrlFor(person);
  if (libraryUrl) {
    person.image = libraryUrl;
    return libraryUrl;
  }

  // Fallback (no library match / manifest missing): legacy DiceBear generation.
  // Ensure avatar settings exist
  const settings = (person as unknown as { avatar_settings?: AvatarSettings }).avatar_settings ??
    generateAvatarSettings(person);

  // Store settings back on person
  (person as unknown as { avatar_settings: AvatarSettings }).avatar_settings = settings;

  // Construct the DiceBear URL for avataaars
  let baseUrl = `https://api.dicebear.com/7.x/avataaars/svg?seed=${person.id}`;

  // Hair
  if (settings.hair === 'NONE') {
    baseUrl += '&topProbability=0';
  } else {
    baseUrl += `&top=${settings.hair}`;
  }

  // Facial hair
  if (settings.facialHair === 'NONE') {
    baseUrl += '&facialHairProbability=0';
  } else {
    baseUrl += `&facialHair=${settings.facialHair}`;
  }

  // Accessory
  if (settings.accessory === 'NONE') {
    baseUrl += '&accessoryProbability=0';
  } else {
    baseUrl += `&accessory=${settings.accessory}`;
  }

  // Other settings
  baseUrl += `&hairColor=${settings.hairColor}`;
  baseUrl += `&clothing=${settings.clothing}`;
  baseUrl += `&clothesColor=${randomChoice(CLOTHES_COLORS)}`;
  baseUrl += `&clothingGraphic=${randomChoice(CLOTHING_GRAPHICS)}`;
  baseUrl += `&skinColor=${settings.skinColor}`;
  baseUrl += `&mouth=${settings.mouth}`;
  baseUrl += '&eyes=default';
  baseUrl += '&eyebrows=default';

  person.image = baseUrl;
  return baseUrl;
}
