/**
 * Bubble Engine Service
 *
 * Handles speech bubble auto-placement and positioning logic
 * Implements comic book layout rules for bubble placement
 */

import type { SpeechBubble, Panel, Position, Size } from '@/types';

/**
 * Default bubble style
 */
export const DEFAULT_BUBBLE_STYLE: SpeechBubble['style'] = {
  backgroundColor: '#ffffff',
  borderColor: '#000000',
  borderWidth: 2,
  borderRadius: 16,
  fontFamily: 'Comic Sans MS, cursive, sans-serif',
  fontSize: 14,
  textColor: '#000000',
  padding: 12,
};

/**
 * Calculate bubble size based on text content
 */
export function calculateBubbleSize(text: string, style: SpeechBubble['style']): Size {
  // Estimate character dimensions (rough approximation)
  const charWidth = style.fontSize * 0.6;
  const charHeight = style.fontSize * 1.4;
  
  // Target line length (characters per line)
  const charsPerLine = 25;
  const lines = Math.ceil(text.length / charsPerLine);
  
  // Calculate dimensions
  const width = Math.min(
    Math.max(text.length * charWidth / Math.min(lines, 3) + style.padding * 2, 100),
    300
  );
  const height = lines * charHeight + style.padding * 2 + 20; // +20 for tail space
  
  return { width, height };
}

/**
 * Auto-place bubble based on type and panel composition
 * Implements comic book placement rules
 */
export function autoPlaceBubble(
  bubble: SpeechBubble,
  panelWidth: number,
  panelHeight: number,
  speakerPosition?: Position
): SpeechBubble {
  const updatedBubble = { ...bubble };
  const size = calculateBubbleSize(bubble.text, bubble.style);
  updatedBubble.size = size;

  // Default speaker position (center, lower half if not specified)
  const speaker: Position = speakerPosition || {
    x: panelWidth * 0.5,
    y: panelHeight * 0.7,
  };

  // Calculate position based on bubble type
  switch (bubble.type) {
    case 'thought':
      // Thought bubbles go above character's head
      updatedBubble.position = {
        x: Math.max(20, Math.min(speaker.x - size.width / 2, panelWidth - size.width - 20)),
        y: Math.max(20, speaker.y - size.height - 40),
      };
      updatedBubble.tailTarget = { x: speaker.x, y: speaker.y - 20 };
      updatedBubble.tailDirection = 'bottom';
      break;

    case 'shout':
      // Shout bubbles can be larger and more prominent
      updatedBubble.size = { width: size.width * 1.1, height: size.height * 1.1 };
      updatedBubble.position = {
        x: Math.max(10, Math.min(speaker.x - updatedBubble.size.width / 2, panelWidth - updatedBubble.size.width - 10)),
        y: Math.max(10, speaker.y - updatedBubble.size.height - 30),
      };
      updatedBubble.tailTarget = { x: speaker.x, y: speaker.y - 10 };
      updatedBubble.tailDirection = 'bottom';
      break;

    case 'whisper':
      // Whisper bubbles typically dashed border, positioned near character
      updatedBubble.position = {
        x: Math.max(20, Math.min(speaker.x - size.width / 2, panelWidth - size.width - 20)),
        y: Math.max(20, speaker.y - size.height - 30),
      };
      updatedBubble.tailTarget = { x: speaker.x, y: speaker.y - 20 };
      updatedBubble.tailDirection = 'bottom';
      break;

    case 'narration':
      // Narration boxes go at top of panel
      updatedBubble.position = {
        x: panelWidth * 0.1,
        y: 20,
      };
      updatedBubble.size = { width: panelWidth * 0.8, height: size.height };
      updatedBubble.tailDirection = 'bottom'; // No tail for narration
      break;

    case 'sound-effect':
      // Sound effects usually placed near action source
      updatedBubble.position = {
        x: Math.max(10, Math.min(speaker.x + 20, panelWidth - size.width - 10)),
        y: Math.max(10, speaker.y - size.height - 50),
      };
      updatedBubble.tailTarget = { x: speaker.x + 30, y: speaker.y - 30 };
      updatedBubble.tailDirection = 'bottom-left';
      break;

    default: // 'normal'
      // Normal speech bubble - avoid covering character face
      updatedBubble.position = {
        x: Math.max(20, Math.min(speaker.x - size.width / 2, panelWidth - size.width - 20)),
        y: Math.max(20, speaker.y - size.height - 30),
      };
      updatedBubble.tailTarget = { x: speaker.x, y: speaker.y - 20 };
      updatedBubble.tailDirection = 'bottom';
      break;
  }

  return updatedBubble;
}

/**
 * Auto-place all bubbles for a panel based on dialogue
 */
export function autoPlacePanelBubbles(
  panel: Panel,
  panelWidth: number,
  panelHeight: number
): SpeechBubble[] {
  const bubbles: SpeechBubble[] = [];
  
  // Track occupied areas to prevent overlapping
  const occupiedAreas: Array<{ x: number; y: number; width: number; height: number }> = [];

  panel.dialogue.forEach((dialogue, index) => {
    // Estimate speaker position (distribute characters across panel)
    const speakerCount = panel.charactersPresent.length;
    const speakerIndex = panel.charactersPresent.indexOf(dialogue.speakerId);
    const speakerX = speakerCount > 1
      ? (panelWidth / (speakerCount + 1)) * (speakerIndex + 1)
      : panelWidth * 0.5;
    const speakerY = panelHeight * 0.75; // Lower half of panel

    // Create bubble
    const bubble: SpeechBubble = {
      id: `bubble_${panel.panelId}_${index}`,
      panelId: panel.panelId,
      type: dialogue.bubbleType,
      text: dialogue.text,
      position: { x: 0, y: 0 },
      size: { width: 100, height: 50 },
      tailDirection: 'bottom',
      tailTarget: { x: speakerX, y: speakerY },
      style: { ...DEFAULT_BUBBLE_STYLE },
      speakerId: dialogue.speakerId,
    };

    // Auto-place with offset for multiple bubbles
    let placedBubble = autoPlaceBubble(bubble, panelWidth, panelHeight, { x: speakerX, y: speakerY });
    
    // Adjust position to avoid overlapping previous bubbles
    const verticalOffset = index * 10; // Slight stagger
    placedBubble.position.y = Math.max(20, placedBubble.position.y - verticalOffset);
    
    // Ensure bubble stays within panel bounds
    placedBubble.position.x = Math.max(
      10,
      Math.min(placedBubble.position.x, panelWidth - placedBubble.size.width - 10)
    );
    placedBubble.position.y = Math.max(
      10,
      Math.min(placedBubble.position.y, panelHeight - placedBubble.size.height - 10)
    );

    occupiedAreas.push({
      x: placedBubble.position.x,
      y: placedBubble.position.y,
      width: placedBubble.size.width,
      height: placedBubble.size.height,
    });

    bubbles.push(placedBubble);
  });

  // Add caption if present
  if (panel.caption) {
    const captionBubble: SpeechBubble = {
      id: `caption_${panel.panelId}`,
      panelId: panel.panelId,
      type: 'narration',
      text: panel.caption,
      position: { x: panelWidth * 0.1, y: 20 },
      size: { width: panelWidth * 0.8, height: 40 },
      tailDirection: 'bottom',
      tailTarget: { x: panelWidth * 0.5, y: 60 },
      style: {
        ...DEFAULT_BUBBLE_STYLE,
        backgroundColor: '#f5f5f5',
        borderColor: '#999999',
      },
      speakerId: '',
    };
    bubbles.push(captionBubble);
  }

  return bubbles;
}

/**
 * Create bubble SVG path based on type and tail direction
 */
export function createBubblePath(
  width: number,
  height: number,
  type: SpeechBubble['type'],
  tailDirection: SpeechBubble['tailDirection'],
  tailTarget: Position
): string {
  const r = 16; // border radius
  
  // Base rounded rectangle path
  let path = `M ${r} 0 `;
  path += `L ${width - r} 0 `;
  path += `Q ${width} 0 ${width} ${r} `;
  path += `L ${width} ${height - r} `;
  path += `Q ${width} ${height} ${width - r} ${height} `;
  
  // Add tail based on direction
  if (type !== 'narration') {
    const tailSize = 15;
    const tailWidth = 12;
    
    switch (tailDirection) {
      case 'bottom':
        const tailX = Math.max(tailWidth, Math.min(tailTarget.x, width - tailWidth));
        path += `L ${tailX + tailWidth} ${height} `;
        path += `L ${tailTarget.x} ${height + tailSize} `;
        path += `L ${tailX - tailWidth} ${height} `;
        break;
      case 'top':
        const topX = Math.max(tailWidth, Math.min(tailTarget.x, width - tailWidth));
        path += `L ${topX + tailWidth} ${height} `;
        path += `L ${width - r} ${height} `;
        path = `M ${r} 0 L ${topX - tailWidth} 0 L ${tailTarget.x} ${-tailSize} L ${topX + tailWidth} 0 L ${width - r} 0 ` + path.substring(path.indexOf('Q'));
        break;
      // Add more directions as needed
    }
  }
  
  path += `L ${r} ${height} `;
  path += `Q 0 ${height} 0 ${height - r} `;
  path += `L 0 ${r} `;
  path += `Q 0 0 ${r} 0 Z`;

  // Modify for thought bubbles (cloud shape)
  if (type === 'thought') {
    // Add small circles for thought bubble effect
    return path; // Simplified - full cloud shape would be more complex
  }

  // Modify for shout bubbles (spiky edges)
  if (type === 'shout') {
    // Add spikes around the edge
    return path; // Simplified - full spiky shape would be more complex
  }

  return path;
}

/**
 * Generate unique bubble ID
 */
export function generateBubbleId(panelId: string, index: number): string {
  return `bubble_${panelId}_${Date.now()}_${index}`;
}