import { PDFPage } from 'pdf-lib';
import { PDFElement, Size } from './PDFElement.js';
import { Margin, Padding } from './Layout.js';

export interface FlexOptions {
  page: PDFPage;
  width: number;
  height: number;
  x?: number;
  y?: number;
  direction?: 'row' | 'column';
  justify?: 'start' | 'center' | 'end' | 'space-between' | 'space-around';
  align?: 'start' | 'center' | 'end' | 'stretch';
  gap?: number;
  children?: PDFElement[];
	margin?: Margin,
	padding?: Padding
}

export class Flex extends PDFElement {
  private page: PDFPage;
  private x: number;
  private y: number;
  private direction: 'row' | 'column';
  private justifyContent: 'start' | 'center' | 'end' | 'space-between' | 'space-around';
  private alignItems: 'start' | 'center' | 'end' | 'stretch';
  private gap: number;
  private children: PDFElement[];

  constructor(private options: FlexOptions) {
    super();
    this.page = options.page;
    this._width = options.width;
    this._height = options.height;
    this.x = options.x ?? 0;
    this.y = options.y ?? 0;
    this.direction = options.direction ?? 'row';
    this.justifyContent = options.justify ?? 'start';
    this.alignItems = options.align ?? 'start';
    this.gap = options.gap ?? 0;
    this.children = options.children ?? [];
		this.padding = options.padding || { top: 0, left: 0, right: 0, bottom: 0}
		this.margin = options.margin || { top: 0, left: 0, right: 0, bottom: 0}
  }

	addChild(...children: PDFElement[]): void {
			this.children.push(...children)
	}

	get height(): number {
		if (typeof this._height === "number") {
			return this._height + this.padding.top + this.padding.bottom;
		}

		let currentHeight = 0

		if (this.direction === "column") {
			for (const child of this.children) {
				currentHeight += child.height;
			}

			currentHeight += this.gap * this.children.length - 1;
		} else {
			for (const child of this.children) {
				if (child.height > currentHeight) {
					currentHeight = child.height
				}
			}
		}

		return currentHeight + this.padding.bottom + this.padding.top;
	}

	set width(value: Size) {
		this._width = value;
	}

	get width(): number {
		return this.innerWidth + this.padding.left + this.padding.right
	}

	get innerWidth() {
		if (typeof this._width === "number") {
			return this._width;
		}

		let currentWidth = 0

		if (this.direction === "row") {
			for (const child of this.children) {
				currentWidth += child.width;
			}

			currentWidth += this.gap * this.children.length - 1;
		} else {
			for (const child of this.children) {
				if (child.width > currentWidth) {
					currentWidth = child.width
				}
			}
		}

		return currentWidth;
	}

  draw(page: PDFPage, x: number = this.x, y: number = this.y): void {
    const childPositions = this.calculateChildPositions(x + this.margin.left + this.padding.left, y - this.margin.top - this.padding.top);

    // Draw each child
    this.children.forEach((child, i) => {
      const pos = childPositions[i];
			
      child.draw(page, pos.x, pos.y);
    });
  }

  private calculateChildPositions(x: number, y: number): { x: number; y: number }[] {
    const positions: { x: number; y: number }[] = [];
    let currentX = x;
    let currentY = y;

    // Calculate total size of children and gaps
    const totalChildrenSize = this.children.reduce(
      (sum, child) => sum + (this.direction === 'row' ? child.width : child.height),
      0
    );

    const totalGaps = this.gap * (this.children.length - 1);

    // Since the origin is at X: 0 Y: page height and everything will be drawn
		// above the current position we first need to subtract the height of the
		// element from the current Y
    this.children.forEach((child, i) => {
			const remainingSpace =
      this.direction === 'row' ? this.width - totalChildrenSize - totalGaps : this.height - totalChildrenSize - totalGaps;

      let childX = this.direction === 'row' ? currentX : currentX + this.calculateAlignOffset(child.width);
      let childY = this.direction === 'row' ? currentY - this.calculateAlignOffset(child.height) : currentY;			

			if (this.direction === "row") {
        currentX += child.width + this.gap;

				switch (this.justifyContent) {
					case 'center':
						childX += remainingSpace / 2;
						break;
					case 'end':
						childX += remainingSpace;
						break;
					case 'space-between':
						currentX += (this.innerWidth - totalChildrenSize - totalGaps) / (this.children.length - 1);
						break;
					case 'space-around':
						const adjustment = remainingSpace / (this.children.length + 1);
						childX += adjustment;
						currentX += adjustment;
						break;
					case 'start':
					default:
						break;
				}
			}

			if (this.direction === 'column') {
        currentY -= child.height + this.gap;

				switch (this.justifyContent) {
					case 'center':
						childY -= remainingSpace / 2;
						break;
					case 'end':
						childY -= remainingSpace;
						break;
					case 'space-between':
						currentY -= (this.innerHeight - totalChildrenSize - totalGaps) / (this.children.length - 1);
						break;
					case 'space-around':
						const adjustment = remainingSpace / (this.children.length + 1);
						childY -= adjustment;
						currentY -= adjustment;
						break;
					case 'start':
					default:
						break;
				}
      }
			
      positions.push({ x: childX, y: childY });
    });

    return positions;
  }

  private calculateAlignOffset(childSize: number): number {
    switch (this.alignItems) {
      case 'center':
        return (this.direction === 'row' ? this.height - childSize : this.width - childSize) / 2;
      case 'end':
        return this.direction === 'row' ? this.height - childSize : this.width - childSize;
      case 'stretch':
        return 0;
      default:
        return 0;
    }
  }
}
