/**
 * AeThex Avatar Format Handlers
 * Import/export handlers for various 3D avatar formats
 */

import { AvatarPlatformId, avatarPlatforms, AvatarConstraints } from './avatar-platforms';
import { validateRig, autoMapBones, RigValidationResult } from './avatar-rigging';

export type AvatarFileFormat =
  | 'glb'
  | 'gltf'
  | 'fbx'
  | 'vrm'
  | 'obj'
  | 'pmx'
  | 'vroid'
  | 'aeth'
  | 'vox';

export interface AvatarMetadata {
  name: string;
  author?: string;
  version?: string;
  description?: string;
  license?: string;
  thumbnail?: string;
  createdAt: Date;
  updatedAt: Date;
  sourcePlatform?: AvatarPlatformId;
  tags?: string[];
  customData?: Record<string, unknown>;
}

export interface AvatarMeshInfo {
  name: string;
  vertexCount: number;
  triangleCount: number;
  materialIndex: number;
  hasNormals: boolean;
  hasUVs: boolean;
  hasTangents: boolean;
  hasVertexColors: boolean;
  hasSkinning: boolean;
  boneInfluenceCount: number;
}

export interface AvatarMaterialInfo {
  name: string;
  type: 'standard' | 'pbr' | 'toon' | 'unlit' | 'custom';
  color?: { r: number; g: number; b: number; a: number };
  metallic?: number;
  roughness?: number;
  textures: {
    diffuse?: string;
    normal?: string;
    metallic?: string;
    roughness?: string;
    emission?: string;
    occlusion?: string;
  };
}

export interface AvatarTextureInfo {
  name: string;
  width: number;
  height: number;
  format: 'png' | 'jpg' | 'webp' | 'basis' | 'ktx2';
  sizeBytes: number;
  mipLevels: number;
}

export interface AvatarStats {
  totalVertices: number;
  totalTriangles: number;
  totalBones: number;
  totalMaterials: number;
  totalTextures: number;
  totalBlendShapes: number;
  fileSizeMB: number;
  maxTextureSize: number;
}

export interface ParsedAvatar {
  id: string;
  format: AvatarFileFormat;
  metadata: AvatarMetadata;
  stats: AvatarStats;
  meshes: AvatarMeshInfo[];
  materials: AvatarMaterialInfo[];
  textures: AvatarTextureInfo[];
  bones: string[];
  blendShapes: string[];
  animations: string[];
  rawData?: ArrayBuffer;
}

export interface ValidationIssue {
  type: 'error' | 'warning' | 'info';
  code: string;
  message: string;
  details?: string;
  autoFix?: boolean;
}

export interface AvatarValidationResult {
  isValid: boolean;
  score: number;
  issues: ValidationIssue[];
  rigValidation: RigValidationResult;
  constraints: {
    polygons: { current: number; max: number; passed: boolean };
    bones: { current: number; max: number; passed: boolean };
    materials: { current: number; max: number; passed: boolean };
    textureSize: { current: number; max: number; passed: boolean };
    fileSize: { current: number; max: number; passed: boolean };
  };
  optimizationSuggestions: string[];
}

export interface ExportOptions {
  format: AvatarFileFormat;
  platform: AvatarPlatformId;
  optimizeForPlatform: boolean;
  embedTextures: boolean;
  compressTextures: boolean;
  targetTextureSize?: number;
  preserveBlendShapes: boolean;
  preserveAnimations: boolean;
  generateLODs: boolean;
  lodLevels?: number[];
}

export interface ImportResult {
  success: boolean;
  avatar?: ParsedAvatar;
  errors: string[];
  warnings: string[];
}

export interface ExportResult {
  success: boolean;
  data?: Blob;
  filename: string;
  errors: string[];
  warnings: string[];
  stats?: {
    originalSize: number;
    exportedSize: number;
    reductionPercent: number;
  };
}

// Format specifications
export const FORMAT_SPECS: Record<AvatarFileFormat, {
  name: string;
  extension: string;
  mimeType: string;
  description: string;
  supportsAnimations: boolean;
  supportsSkinning: boolean;
  supportsBlendShapes: boolean;
  binary: boolean;
}> = {
  glb: {
    name: 'glTF Binary',
    extension: '.glb',
    mimeType: 'model/gltf-binary',
    description: 'Optimized binary format, ideal for web and real-time applications',
    supportsAnimations: true,
    supportsSkinning: true,
    supportsBlendShapes: true,
    binary: true,
  },
  gltf: {
    name: 'glTF',
    extension: '.gltf',
    mimeType: 'model/gltf+json',
    description: 'JSON-based format with external resources',
    supportsAnimations: true,
    supportsSkinning: true,
    supportsBlendShapes: true,
    binary: false,
  },
  fbx: {
    name: 'FBX',
    extension: '.fbx',
    mimeType: 'application/octet-stream',
    description: 'Autodesk format, widely used in game development',
    supportsAnimations: true,
    supportsSkinning: true,
    supportsBlendShapes: true,
    binary: true,
  },
  vrm: {
    name: 'VRM',
    extension: '.vrm',
    mimeType: 'model/gltf-binary',
    description: 'VR avatar format based on glTF with humanoid extensions',
    supportsAnimations: true,
    supportsSkinning: true,
    supportsBlendShapes: true,
    binary: true,
  },
  obj: {
    name: 'Wavefront OBJ',
    extension: '.obj',
    mimeType: 'text/plain',
    description: 'Simple geometry format, no rigging support',
    supportsAnimations: false,
    supportsSkinning: false,
    supportsBlendShapes: false,
    binary: false,
  },
  pmx: {
    name: 'PMX',
    extension: '.pmx',
    mimeType: 'application/octet-stream',
    description: 'MikuMikuDance format for anime-style characters',
    supportsAnimations: true,
    supportsSkinning: true,
    supportsBlendShapes: true,
    binary: true,
  },
  vroid: {
    name: 'VRoid',
    extension: '.vroid',
    mimeType: 'application/octet-stream',
    description: 'VRoid Studio project format',
    supportsAnimations: true,
    supportsSkinning: true,
    supportsBlendShapes: true,
    binary: true,
  },
  aeth: {
    name: 'AeThex Universal',
    extension: '.aeth',
    mimeType: 'application/x-aethex-avatar',
    description: 'AeThex universal avatar format with full platform metadata',
    supportsAnimations: true,
    supportsSkinning: true,
    supportsBlendShapes: true,
    binary: true,
  },
  vox: {
    name: 'MagicaVoxel',
    extension: '.vox',
    mimeType: 'application/octet-stream',
    description: 'Voxel format for blocky/pixelated avatars',
    supportsAnimations: false,
    supportsSkinning: false,
    supportsBlendShapes: false,
    binary: true,
  },
};

/**
 * Detect file format from file extension or MIME type
 */
export function detectFormat(filename: string, mimeType?: string): AvatarFileFormat | null {
  const ext = filename.toLowerCase().split('.').pop();

  for (const [format, spec] of Object.entries(FORMAT_SPECS)) {
    if (spec.extension === `.${ext}` || spec.mimeType === mimeType) {
      return format as AvatarFileFormat;
    }
  }

  return null;
}

/**
 * Check if a format is supported for import
 */
export function canImport(format: AvatarFileFormat): boolean {
  return ['glb', 'gltf', 'fbx', 'vrm', 'obj', 'pmx', 'aeth'].includes(format);
}

/**
 * Check if a format is supported for export to a specific platform
 */
export function canExport(format: AvatarFileFormat, platform: AvatarPlatformId): boolean {
  const platformData = avatarPlatforms[platform];
  return platformData.importFormats.includes(format);
}

/**
 * Get recommended export format for a platform
 */
export function getRecommendedFormat(platform: AvatarPlatformId): AvatarFileFormat {
  return avatarPlatforms[platform].exportFormat as AvatarFileFormat;
}

/**
 * Validate avatar against platform constraints
 */
export function validateForPlatform(
  avatar: ParsedAvatar,
  platform: AvatarPlatformId
): AvatarValidationResult {
  const constraints = avatarPlatforms[platform].constraints;
  const issues: ValidationIssue[] = [];
  const optimizationSuggestions: string[] = [];

  // Polygon check
  const polygonsPassed = avatar.stats.totalTriangles <= constraints.maxPolygons;
  if (!polygonsPassed) {
    issues.push({
      type: 'error',
      code: 'POLY_LIMIT',
      message: `Triangle count (${avatar.stats.totalTriangles}) exceeds platform limit (${constraints.maxPolygons})`,
      details: `Reduce by ${avatar.stats.totalTriangles - constraints.maxPolygons} triangles`,
      autoFix: true,
    });
    optimizationSuggestions.push(
      `Use mesh decimation to reduce triangle count to ${constraints.maxPolygons}`
    );
  }

  // Bone check
  const bonesPassed = avatar.stats.totalBones <= constraints.maxBones;
  if (!bonesPassed) {
    issues.push({
      type: 'error',
      code: 'BONE_LIMIT',
      message: `Bone count (${avatar.stats.totalBones}) exceeds platform limit (${constraints.maxBones})`,
      details: `Remove ${avatar.stats.totalBones - constraints.maxBones} bones`,
      autoFix: false,
    });
    optimizationSuggestions.push(
      `Remove non-essential bones or merge small bone chains`
    );
  }

  // Material check
  const materialsPassed = avatar.stats.totalMaterials <= constraints.maxMaterials;
  if (!materialsPassed) {
    issues.push({
      type: 'warning',
      code: 'MATERIAL_LIMIT',
      message: `Material count (${avatar.stats.totalMaterials}) exceeds platform limit (${constraints.maxMaterials})`,
      details: `Merge ${avatar.stats.totalMaterials - constraints.maxMaterials} materials`,
      autoFix: true,
    });
    optimizationSuggestions.push(
      `Merge materials using texture atlasing`
    );
  }

  // Texture size check
  const textureSizePassed = avatar.stats.maxTextureSize <= constraints.maxTextureSize;
  if (!textureSizePassed) {
    issues.push({
      type: 'warning',
      code: 'TEXTURE_SIZE',
      message: `Max texture size (${avatar.stats.maxTextureSize}px) exceeds platform limit (${constraints.maxTextureSize}px)`,
      autoFix: true,
    });
    optimizationSuggestions.push(
      `Resize textures to ${constraints.maxTextureSize}x${constraints.maxTextureSize}`
    );
  }

  // File size check
  const fileSizePassed = avatar.stats.fileSizeMB <= constraints.maxFileSize;
  if (!fileSizePassed) {
    issues.push({
      type: 'error',
      code: 'FILE_SIZE',
      message: `File size (${avatar.stats.fileSizeMB.toFixed(2)}MB) exceeds platform limit (${constraints.maxFileSize}MB)`,
      autoFix: true,
    });
    optimizationSuggestions.push(
      `Compress textures and reduce mesh complexity`
    );
  }

  // Rig validation
  const rigValidation = validateRig(avatar.bones, platform);

  if (!rigValidation.isValid) {
    issues.push({
      type: 'error',
      code: 'RIG_INVALID',
      message: `Rig is missing ${rigValidation.missingRequiredBones.length} required bones`,
      details: `Missing: ${rigValidation.missingRequiredBones.join(', ')}`,
      autoFix: false,
    });
  }

  if (rigValidation.warnings.length > 0) {
    for (const warning of rigValidation.warnings) {
      issues.push({
        type: 'warning',
        code: 'RIG_WARNING',
        message: warning,
        autoFix: false,
      });
    }
  }

  // Calculate overall score
  const passedChecks = [polygonsPassed, bonesPassed, materialsPassed, textureSizePassed, fileSizePassed]
    .filter(Boolean).length;
  const constraintScore = (passedChecks / 5) * 50;
  const rigScore = rigValidation.score * 0.5;
  const overallScore = Math.round(constraintScore + rigScore);

  return {
    isValid: polygonsPassed && bonesPassed && fileSizePassed && rigValidation.isValid,
    score: overallScore,
    issues,
    rigValidation,
    constraints: {
      polygons: { current: avatar.stats.totalTriangles, max: constraints.maxPolygons, passed: polygonsPassed },
      bones: { current: avatar.stats.totalBones, max: constraints.maxBones, passed: bonesPassed },
      materials: { current: avatar.stats.totalMaterials, max: constraints.maxMaterials, passed: materialsPassed },
      textureSize: { current: avatar.stats.maxTextureSize, max: constraints.maxTextureSize, passed: textureSizePassed },
      fileSize: { current: avatar.stats.fileSizeMB, max: constraints.maxFileSize, passed: fileSizePassed },
    },
    optimizationSuggestions,
  };
}

/**
 * Create a mock/demo parsed avatar for testing
 */
export function createDemoAvatar(name: string, platform: AvatarPlatformId): ParsedAvatar {
  const platformData = avatarPlatforms[platform];

  return {
    id: `demo-${Date.now()}`,
    format: platformData.exportFormat as AvatarFileFormat,
    metadata: {
      name,
      author: 'AeThex Studio',
      version: '1.0.0',
      description: `Demo avatar for ${platformData.displayName}`,
      createdAt: new Date(),
      updatedAt: new Date(),
      sourcePlatform: platform,
      tags: ['demo', platform],
    },
    stats: {
      totalVertices: Math.floor(platformData.constraints.maxPolygons * 0.6),
      totalTriangles: Math.floor(platformData.constraints.maxPolygons * 0.5),
      totalBones: Math.floor(platformData.constraints.maxBones * 0.8),
      totalMaterials: Math.min(4, platformData.constraints.maxMaterials),
      totalTextures: 4,
      totalBlendShapes: platformData.skeleton.blendShapeSupport ? 52 : 0,
      fileSizeMB: platformData.constraints.maxFileSize * 0.3,
      maxTextureSize: platformData.constraints.maxTextureSize,
    },
    meshes: [
      {
        name: 'Body',
        vertexCount: 5000,
        triangleCount: 4000,
        materialIndex: 0,
        hasNormals: true,
        hasUVs: true,
        hasTangents: true,
        hasVertexColors: false,
        hasSkinning: true,
        boneInfluenceCount: 4,
      },
    ],
    materials: [
      {
        name: 'Skin',
        type: 'pbr',
        color: { r: 255, g: 224, b: 189, a: 255 },
        metallic: 0,
        roughness: 0.8,
        textures: { diffuse: 'skin_diffuse.png', normal: 'skin_normal.png' },
      },
    ],
    textures: [
      { name: 'skin_diffuse.png', width: 1024, height: 1024, format: 'png', sizeBytes: 512000, mipLevels: 10 },
      { name: 'skin_normal.png', width: 1024, height: 1024, format: 'png', sizeBytes: 512000, mipLevels: 10 },
    ],
    bones: platformData.skeleton.bones.map(b => b.name),
    blendShapes: platformData.skeleton.blendShapeSupport
      ? ['Blink', 'Smile', 'Frown', 'Surprise', 'Angry']
      : [],
    animations: ['Idle', 'Walk', 'Run', 'Jump'],
  };
}

/**
 * Generate export filename
 */
export function generateExportFilename(
  avatarName: string,
  platform: AvatarPlatformId,
  format: AvatarFileFormat
): string {
  const sanitizedName = avatarName.toLowerCase().replace(/[^a-z0-9]/g, '_');
  const timestamp = new Date().toISOString().slice(0, 10).replace(/-/g, '');
  const extension = FORMAT_SPECS[format].extension;

  return `${sanitizedName}_${platform}_${timestamp}${extension}`;
}

/**
 * Get supported import formats
 */
export function getSupportedImportFormats(): AvatarFileFormat[] {
  return ['glb', 'gltf', 'fbx', 'vrm', 'obj', 'pmx', 'aeth'];
}

/**
 * Get supported export formats for a platform
 */
export function getSupportedExportFormats(platform: AvatarPlatformId): AvatarFileFormat[] {
  const platformData = avatarPlatforms[platform];
  return platformData.importFormats.filter(f =>
    ['glb', 'gltf', 'fbx', 'vrm', 'aeth'].includes(f)
  ) as AvatarFileFormat[];
}

/**
 * Estimate export file size based on avatar stats and target format
 */
export function estimateExportSize(
  avatar: ParsedAvatar,
  targetFormat: AvatarFileFormat,
  options: Partial<ExportOptions>
): number {
  let baseSize = avatar.stats.fileSizeMB;

  // Adjust for format
  if (targetFormat === 'glb' && avatar.format !== 'glb') {
    baseSize *= 0.7; // GLB is usually more compact
  } else if (targetFormat === 'fbx') {
    baseSize *= 1.2; // FBX can be larger
  }

  // Adjust for compression
  if (options.compressTextures) {
    baseSize *= 0.5;
  }

  // Adjust for LODs
  if (options.generateLODs) {
    baseSize *= 1.3;
  }

  return Math.round(baseSize * 100) / 100;
}

/**
 * Get optimization recommendations for an avatar
 */
export function getOptimizationRecommendations(
  avatar: ParsedAvatar,
  targetPlatform: AvatarPlatformId
): string[] {
  const recommendations: string[] = [];
  const validation = validateForPlatform(avatar, targetPlatform);

  recommendations.push(...validation.optimizationSuggestions);

  // Additional recommendations
  if (avatar.textures.some(t => t.format !== 'png' && t.format !== 'jpg')) {
    recommendations.push('Convert textures to PNG or JPG for maximum compatibility');
  }

  if (avatar.meshes.length > 5) {
    recommendations.push(`Consider merging ${avatar.meshes.length} meshes to reduce draw calls`);
  }

  if (avatar.materials.some(m => m.type !== 'pbr')) {
    recommendations.push('Convert materials to PBR for consistent appearance across platforms');
  }

  return recommendations;
}
