<?php

declare(strict_types=1);

namespace App\Framework\Core\ValueObjects;

use App\Framework\Exception\ErrorCode;
use App\Framework\Exception\FrameworkException;

/**
 * Value Object representing dimensions (width and height)
 * Can be used for images, videos, HTML elements, etc.
 */
final readonly class Dimensions
{
    public function __construct(
        public int $width,
        public int $height
    ) {
        if ($width <= 0) {
            throw FrameworkException::create(
                ErrorCode::VAL_INVALID_FORMAT,
                'Width must be greater than 0'
            )->withData(['width' => $width]);
        }

        if ($height <= 0) {
            throw FrameworkException::create(
                ErrorCode::VAL_INVALID_FORMAT,
                'Height must be greater than 0'
            )->withData(['height' => $height]);
        }
    }

    /**
     * Create from image file
     */
    public static function fromImageFile(string $filepath): self
    {
        $imageInfo = getimagesize($filepath);

        if ($imageInfo === false) {
            throw FrameworkException::create(
                ErrorCode::VAL_INVALID_FORMAT,
                'Could not read dimensions from image file'
            )->withData(['filepath' => $filepath]);
        }

        return new self($imageInfo[0], $imageInfo[1]);
    }

    /**
     * Create from array [width, height]
     */
    public static function fromArray(array $data): self
    {
        if (count($data) !== 2) {
            throw FrameworkException::create(
                ErrorCode::VAL_INVALID_FORMAT,
                'Dimensions array must contain exactly 2 elements [width, height]'
            )->withData(['data' => $data]);
        }

        return new self((int) $data[0], (int) $data[1]);
    }

    /**
     * Calculate aspect ratio
     */
    public function getAspectRatio(): float
    {
        return $this->width / $this->height;
    }

    /**
     * Get orientation
     */
    public function getOrientation(): Orientation
    {
        if ($this->height > $this->width) {
            return Orientation::PORTRAIT;
        }

        if ($this->width > $this->height) {
            return Orientation::LANDSCAPE;
        }

        return Orientation::SQUARE;
    }

    /**
     * Check if dimensions are portrait orientation
     */
    public function isPortrait(): bool
    {
        return $this->getOrientation() === Orientation::PORTRAIT;
    }

    /**
     * Check if dimensions are landscape orientation
     */
    public function isLandscape(): bool
    {
        return $this->getOrientation() === Orientation::LANDSCAPE;
    }

    /**
     * Check if dimensions are square
     */
    public function isSquare(): bool
    {
        return $this->getOrientation() === Orientation::SQUARE;
    }

    /**
     * Get total area (width * height)
     */
    public function getArea(): int
    {
        return $this->width * $this->height;
    }

    /**
     * Scale dimensions proportionally to fit within max dimensions
     */
    public function scaleToFit(int $maxWidth, int $maxHeight): self
    {
        $ratio = min($maxWidth / $this->width, $maxHeight / $this->height);

        if ($ratio >= 1) {
            return $this; // Already fits
        }

        return new self(
            (int) round($this->width * $ratio),
            (int) round($this->height * $ratio)
        );
    }

    /**
     * Scale dimensions proportionally to fill max dimensions (may crop)
     */
    public function scaleToFill(int $maxWidth, int $maxHeight): self
    {
        $ratio = max($maxWidth / $this->width, $maxHeight / $this->height);

        return new self(
            (int) round($this->width * $ratio),
            (int) round($this->height * $ratio)
        );
    }

    /**
     * Scale by factor
     */
    public function scaleBy(float $factor): self
    {
        if ($factor <= 0) {
            throw FrameworkException::create(
                ErrorCode::VAL_INVALID_FORMAT,
                'Scale factor must be greater than 0'
            )->withData(['factor' => $factor]);
        }

        return new self(
            (int) round($this->width * $factor),
            (int) round($this->height * $factor)
        );
    }

    /**
     * Check if dimensions match
     */
    public function equals(self $other): bool
    {
        return $this->width === $other->width && $this->height === $other->height;
    }

    /**
     * Convert to array
     */
    public function toArray(): array
    {
        return [
            'width' => $this->width,
            'height' => $this->height,
            'aspect_ratio' => $this->getAspectRatio(),
            'orientation' => $this->getOrientation()->value,
            'area' => $this->getArea(),
        ];
    }

    /**
     * String representation (e.g., "1920x1080")
     */
    public function __toString(): string
    {
        return "{$this->width}x{$this->height}";
    }
}
