/**
 * Character Template Utilities
 * 
 * Manages prompt templates for character consistency across panels
 * Based on research: IP-Adapter with frozen reference at 0.65 strength
 * and locked attribute order to prevent drift
 */

import type { Character, CharacterPromptTemplate } from '@/types';

/**
 * Standard template format with locked attribute order
 * Text encoders are order-sensitive - this prevents drift
 */
const CHARACTER_TEMPLATE_STRING = 
  "{age}-year-old {gender}, " +
  "{hairColor} {hairStyle} hair, " +
  "{eyeColor} eyes, " +
  "{skinTone} skin, " +
  "{bodyType} build, " +
  "wearing {outfit}, " +
  "{accessories}, " +
  "{distinguishingFeatures}";

/**
 * Parse a natural language description into a structured template
 * Uses regex patterns to extract key attributes
 */
export function parseDescriptionToTemplate(description: string): CharacterPromptTemplate {
  const template: CharacterPromptTemplate = {
    age: extractAge(description),
    gender: extractGender(description),
    hairColor: extractHairColor(description),
    hairStyle: extractHairStyle(description),
    eyeColor: extractEyeColor(description),
    skinTone: extractSkinTone(description),
    bodyType: extractBodyType(description),
    outfit: extractOutfit(description),
    accessories: extractAccessories(description),
    distinguishingFeatures: extractDistinguishingFeatures(description),
  };

  return template;
}

/**
 * Build character prompt from template with locked order
 * This ensures consistency across all panel generations
 */
export function buildCharacterPrompt(
  template: CharacterPromptTemplate,
  action: string,
  setting: string,
  lighting: string,
  artStyle: string
): string {
  const base = CHARACTER_TEMPLATE_STRING
    .replace('{age}', template.age || 'young adult')
    .replace('{gender}', template.gender || 'person')
    .replace('{hairColor}', template.hairColor || '')
    .replace('{hairStyle}', template.hairStyle || '')
    .replace('{eyeColor}', template.eyeColor || '')
    .replace('{skinTone}', template.skinTone || '')
    .replace('{bodyType}', template.bodyType || 'average')
    .replace('{outfit}', template.outfit || 'casual clothing')
    .replace('{accessories}', template.accessories || 'no accessories')
    .replace('{distinguishingFeatures}', template.distinguishingFeatures || '');

  return `${artStyle} style. ${base}, ${action}, ${setting}, ${lighting}`.replace(/\s+/g, ' ').trim();
}

/**
 * Generate a deterministic seed from character name
 * Ensures reproducibility while allowing variation
 */
export function deriveSeedFromString(str: string): number {
  let hash = 0;
  for (let i = 0; i < str.length; i++) {
    const char = str.charCodeAt(i);
    hash = ((hash << 5) - hash) + char;
    hash = hash & hash;
  }
  return Math.abs(hash) % 2147483647;
}

/**
 * Generate panel-specific seed for consistency
 */
export function generatePanelSeed(
  projectId: string,
  pageNumber: number,
  panelNumber: number,
  panelId: string
): number {
  return deriveSeedFromString(`${projectId}_${pageNumber}_${panelNumber}_${panelId}`);
}

// Helper extraction functions using regex patterns
function extractAge(description: string): string {
  const patterns = [
    /(\d+)[-\s]?year[\s-]?old/i,
    /(\d+)[-\s]?years?\s+old/i,
    /(young|teen|adult|middle[\s-]?aged|elderly|old)/i,
  ];
  
  for (const pattern of patterns) {
    const match = description.match(pattern);
    if (match) return match[1] || match[0];
  }
  
  return 'young adult';
}

function extractGender(description: string): string {
  if (/\b(female|woman|girl|lady)\b/i.test(description)) return 'female';
  if (/\b(male|man|boy|guy)\b/i.test(description)) return 'male';
  if (/\b(non[\s-]?binary|nb|enby)\b/i.test(description)) return 'non-binary';
  return 'person';
}

function extractHairColor(description: string): string {
  const colors = ['black', 'brown', 'blonde', 'blond', 'red', 'auburn', 'brunette', 'silver', 'gray', 'grey', 'white', 'blue', 'green', 'purple', 'pink', 'orange'];
  for (const color of colors) {
    const pattern = new RegExp(`\\b(${color}\\s+hair|${color}-haired)\\b`, 'i');
    if (pattern.test(description)) return color.replace('blond', 'blonde');
  }
  return '';
}

function extractHairStyle(description: string): string {
  const styles = ['long', 'short', 'medium', 'curly', 'straight', 'wavy', 'spikey', 'spiky', 'ponytail', 'bun', 'braid', 'buzz cut', 'bald'];
  for (const style of styles) {
    if (description.toLowerCase().includes(style)) return style;
  }
  return '';
}

function extractEyeColor(description: string): string {
  const colors = ['blue', 'brown', 'green', 'hazel', 'amber', 'gray', 'grey', 'violet', 'purple'];
  for (const color of colors) {
    if (new RegExp(`\\b${color}\\s+eyes?\\b`, 'i').test(description)) return color;
  }
  return '';
}

function extractSkinTone(description: string): string {
  const tones = ['fair', 'pale', 'light', 'medium', 'tan', 'olive', 'brown', 'dark'];
  for (const tone of tones) {
    if (new RegExp(`\\b${tone}\\s+(skin|complexion)\\b`, 'i').test(description)) return tone;
  }
  return '';
}

function extractBodyType(description: string): string {
  const types = ['slim', 'skinny', 'thin', 'average', 'athletic', 'muscular', 'heavy', 'large', 'tall', 'short', 'petite'];
  for (const type of types) {
    if (new RegExp(`\\b${type}\\s+(build|body|frame)\\b`, 'i').test(description)) return type;
    if (description.toLowerCase().includes(type)) return type;
  }
  return 'average';
}

function extractOutfit(description: string): string {
  const patterns = [
    /wearing\s+([^,.]+)/i,
    /dressed\s+(?:in|as)\s+([^,.]+)/i,
    /outfit[:\s]+([^,.]+)/i,
    /clothing[:\s]+([^,.]+)/i,
  ];
  
  for (const pattern of patterns) {
    const match = description.match(pattern);
    if (match) return match[1].trim();
  }
  
  return 'casual clothing';
}

function extractAccessories(description: string): string {
  const accessories: string[] = [];
  const accessoryPatterns = [
    /\b(glasses|sunglasses)\b/i,
    /\b(hat|cap|beanie)\b/i,
    /\b(scarf)\b/i,
    /\b(necklace|pendant)\b/i,
    /\b(earrings?)\b/i,
    /\b(bracelet|watch)\b/i,
    /\b(ring)\b/i,
    /\b(backpack|bag)\b/i,
    /\b(gloves)\b/i,
    /\b(belt)\b/i,
  ];
  
  for (const pattern of accessoryPatterns) {
    const match = description.match(pattern);
    if (match) accessories.push(match[0]);
  }
  
  return accessories.length > 0 ? accessories.join(', ') : 'no accessories';
}

function extractDistinguishingFeatures(description: string): string {
  const features: string[] = [];
  const featurePatterns = [
    /\b(scar|scars)\s+(?:on|across)\s+([^,.]+)/i,
    /\b(tattoo|tattoos)\s+(?:on|of)\s+([^,.]+)/i,
    /\b(freckles)\b/i,
    /\b(mole|birthmark)\b/i,
    /\b(beard|mustache|goatee)\b/i,
    /\b(glasses|monocle)\b/i,
    /\b(prosthetic)\b/i,
  ];
  
  for (const pattern of featurePatterns) {
    const match = description.match(pattern);
    if (match) features.push(match[0]);
  }
  
  return features.join(', ');
}

/**
 * Extract color palette from character description
 * Uses simple keyword matching - can be enhanced with image analysis
 */
export function extractColorPalette(description: string): { hair: string; eyes: string; skin: string; outfit: string } {
  return {
    hair: extractHairColor(description),
    eyes: extractEyeColor(description),
    skin: extractSkinTone(description),
    outfit: extractOutfit(description).split(' ')[0] || '',
  };
}

/**
 * Update character with parsed template from description
 */
export function updateCharacterFromDescription(character: Character): Character {
  const template = parseDescriptionToTemplate(character.description);
  const colorPalette = extractColorPalette(character.description);
  
  return {
    ...character,
    promptTemplate: template,
    colorPalette,
    seed: character.seed || deriveSeedFromString(character.name),
  };
}
