/**
 * Base Element class for all report elements
 * Provides common properties and methods
 */
export class Element {
  constructor({ id, type, x, y }) {
    this.id = id || `${type}-${Date.now()}`;
    this.type = type;
    this.x = x;
    this.y = y;
  }

  /**
   * Calculate pixel position from grid coordinates
   */
  getPixelPosition(charWidth, charHeight) {
    return {
      left: this.x * charWidth,
      top: this.y * charHeight
    };
  }

  /**
   * Update element properties
   */
  update(updates) {
    Object.assign(this, updates);
    return this;
  }

  /**
   * Check if element intersects with a rectangle
   */
  intersectsRect(rect) {
    const bounds = this.getBounds();
    return !(
      bounds.maxX < rect.minX ||
      bounds.minX > rect.maxX ||
      bounds.maxY < rect.minY ||
      bounds.minY > rect.maxY
    );
  }

  /**
   * Get element bounds (must be overridden by subclasses)
   */
  getBounds() {
    return {
      minX: this.x,
      minY: this.y,
      maxX: this.x,
      maxY: this.y
    };
  }

  /**
   * Get element dimensions in grid units (must be overridden by subclasses)
   */
  getDimensions() {
    return { width: 1, height: 1 };
  }

  /**
   * Get style object for rendering
   */
  getStyle(charWidth, charHeight) {
    const pos = this.getPixelPosition(charWidth, charHeight);
    const dims = this.getDimensions();
    return {
      left: `${pos.left}px`,
      top: `${pos.top}px`,
      width: `${dims.width * charWidth}px`,
      height: `${dims.height * charHeight}px`
    };
  }

  /**
   * Serialize to JSON
   */
  toJSON() {
    return {
      id: this.id,
      type: this.type,
      x: this.x,
      y: this.y
    };
  }

  /**
   * Create element from JSON
   */
  static fromJSON(json) {
    const elementClasses = {
      text: TextElement,
      dbtext: DBTextElement,
      expr: ExpressionElement,
      frame: FrameElement,
      hline: HorizontalLineElement,
      vline: VerticalLineElement,
      symbol: SymbolElement,
      band: BandElement
    };

    const ElementClass = elementClasses[json.type];
    if (!ElementClass) {
      throw new Error(`Unknown element type: ${json.type}`);
    }

    return new ElementClass(json);
  }
}

export class ExpressionElement extends Element {
  constructor({ id, x, y, width = 10, height = 1, expression = '', dataType = 'general', alignment = 'left' }) {
    super({ id, type: 'expr', x, y });
    this.width = width;
    this.height = height;
    this.expression = expression;
    this.dataType = dataType;
    this.alignment = alignment;
  }
  getBounds() { return { minX: this.x, minY: this.y, maxX: this.x + this.width - 1, maxY: this.y + this.height - 1 }; }
  getDimensions() { return { width: this.width, height: this.height }; }
  toJSON() { return { ...super.toJSON(), width: this.width, height: this.height, expression: this.expression, dataType: this.dataType, alignment: this.alignment }; }
}

/**
 * Database-bound text field element
 */
export class DBTextElement extends Element {
  constructor({ id, x, y, width = 10, height = 1, objectKey = '', fieldPath = '', dataType = 'general', alignment = 'left' }) {
    super({ id, type: 'dbtext', x, y });
    this.width = width;
    this.height = height;
    this.objectKey = objectKey;
    this.fieldPath = fieldPath;
    this.dataType = dataType;
    this.alignment = alignment;
  }

  getBounds() {
    return {
      minX: this.x,
      minY: this.y,
      maxX: this.x + this.width - 1,
      maxY: this.y + this.height - 1
    };
  }

  getDimensions() {
    return { width: this.width, height: this.height };
  }

  isMultiLine() {
    return this.height > 1;
  }

  getWhiteSpace() {
    return this.isMultiLine() ? 'pre-wrap' : 'nowrap';
  }

  toJSON() {
    return {
      ...super.toJSON(),
      width: this.width,
      height: this.height,
      objectKey: this.objectKey,
      fieldPath: this.fieldPath,
      dataType: this.dataType,
      alignment: this.alignment
    };
  }
}

/**
 * Text field element
 */
export class TextElement extends Element {
  constructor({ id, x, y, content = '', width = 10, height = 1 }) {
    super({ id, type: 'text', x, y });
    this.content = content;
    this.width = width;
    this.height = height;
  }

  getBounds() {
    return {
      minX: this.x,
      minY: this.y,
      maxX: this.x + this.width - 1,
      maxY: this.y + this.height - 1
    };
  }

  getDimensions() {
    return { width: this.width, height: this.height };
  }

  isMultiLine() {
    return this.height > 1;
  }

  getWhiteSpace() {
    return this.isMultiLine() ? 'pre-wrap' : 'nowrap';
  }

  toJSON() {
    return {
      ...super.toJSON(),
      content: this.content,
      width: this.width,
      height: this.height
    };
  }
}

/**
 * Frame element
 */
export class FrameElement extends Element {
  constructor({ id, x, y, width = 10, height = 5, borderStyle = 'single' }) {
    super({ id, type: 'frame', x, y });
    this.width = width;
    this.height = height;
    this.borderStyle = borderStyle;
  }

  getBounds() {
    return {
      minX: this.x,
      minY: this.y,
      maxX: this.x + this.width - 1,
      maxY: this.y + this.height - 1
    };
  }

  getDimensions() {
    return { width: this.width, height: this.height };
  }

  /**
   * Check if a position (relative to element) is on the border
   */
  isPositionOnBorder(relativeCol, relativeRow) {
    return (
      relativeRow === 0 ||
      relativeRow === this.height - 1 ||
      relativeCol === 0 ||
      relativeCol === this.width - 1
    );
  }

  toJSON() {
    return {
      ...super.toJSON(),
      width: this.width,
      height: this.height,
      borderStyle: this.borderStyle
    };
  }
}

/**
 * Horizontal line element
 */
export class HorizontalLineElement extends Element {
  constructor({ id, x, y, length = 5, lineStyle = 'single' }) {
    super({ id, type: 'hline', x, y });
    this.length = length;
    this.lineStyle = lineStyle;
  }

  getBounds() {
    return {
      minX: this.x,
      minY: this.y,
      maxX: this.x + this.length - 1,
      maxY: this.y
    };
  }

  getDimensions() {
    return { width: this.length, height: 1 };
  }

  toJSON() {
    return {
      ...super.toJSON(),
      length: this.length,
      lineStyle: this.lineStyle
    };
  }
}

/**
 * Vertical line element
 */
export class VerticalLineElement extends Element {
  constructor({ id, x, y, length = 5, lineStyle = 'single' }) {
    super({ id, type: 'vline', x, y });
    this.length = length;
    this.lineStyle = lineStyle;
  }

  getBounds() {
    return {
      minX: this.x,
      minY: this.y,
      maxX: this.x,
      maxY: this.y + this.length - 1
    };
  }

  getDimensions() {
    return { width: 1, height: this.length };
  }

  toJSON() {
    return {
      ...super.toJSON(),
      length: this.length,
      lineStyle: this.lineStyle
    };
  }
}

/**
 * Symbol element (single character)
 */
export class SymbolElement extends Element {
  constructor({ id, x, y, char = '─' }) {
    super({ id, type: 'symbol', x, y });
    this.char = char;
  }

  getBounds() {
    return {
      minX: this.x,
      minY: this.y,
      maxX: this.x,
      maxY: this.y
    };
  }

  getDimensions() {
    return { width: 1, height: 1 };
  }

  toJSON() {
    return {
      ...super.toJSON(),
      char: this.char
    };
  }
}

/**
 * Band element (repeating section for data arrays)
 */
export class BandElement extends Element {
  constructor({
    id,
    y,
    height = 3,
    bandType = 'detail',
    dataSource = '',
    caption = '',
    children = [],
    parentBandId = null
  }) {
    super({ id, type: 'band', x: 0, y });
    this.width = 80; // Always full width
    this.height = height;
    this.bandType = bandType;
    this.dataSource = dataSource;
    this.caption = caption;
    this.children = children; // Array of child element IDs
    this.parentBandId = parentBandId; // For nested bands
  }

  getBounds() {
    return {
      minX: 0,
      minY: this.y,
      maxX: 79,
      maxY: this.y + this.height - 1
    };
  }

  getDimensions() {
    return { width: 80, height: this.height };
  }

  /**
   * Check if this band overlaps with another band
   */
  overlaps(otherBand) {
    if (!otherBand || otherBand.type !== 'band') {
      return false;
    }

    const thisBounds = this.getBounds();
    const otherBounds = otherBand.getBounds();

    return !(
      thisBounds.maxY < otherBounds.minY ||
      thisBounds.minY > otherBounds.maxY
    );
  }

  toJSON() {
    return {
      ...super.toJSON(),
      width: this.width,
      height: this.height,
      bandType: this.bandType,
      dataSource: this.dataSource,
      caption: this.caption,
      children: this.children,
      parentBandId: this.parentBandId
    };
  }
}
