<?php

declare(strict_types=1);

namespace App\Framework\Filesystem\ValueObjects;

/**
 * Value Object representing Unix file permissions
 */
final readonly class FilePermissions
{
    /**
     * @param int $mode Unix permission mode (e.g., 0644, 0755)
     */
    public function __construct(
        public int $mode
    ) {
        if ($mode < 0 || $mode > 0777) {
            throw new \InvalidArgumentException(
                "Invalid permission mode: {$mode}. Must be between 0 and 0777 (octal)."
            );
        }
    }

    /**
     * Create from octal string (e.g., "644", "755")
     */
    public static function fromOctal(string $octal): self
    {
        $mode = octdec($octal);

        return new self($mode);
    }

    /**
     * Create from symbolic notation (e.g., "rw-r--r--", "rwxr-xr-x")
     */
    public static function fromSymbolic(string $symbolic): self
    {
        if (strlen($symbolic) !== 9) {
            throw new \InvalidArgumentException(
                "Symbolic notation must be 9 characters (e.g., 'rw-r--r--')"
            );
        }

        $mode = 0;

        // Owner permissions
        if ($symbolic[0] === 'r') {
            $mode |= 0400;
        }
        if ($symbolic[1] === 'w') {
            $mode |= 0200;
        }
        if ($symbolic[2] === 'x') {
            $mode |= 0100;
        }

        // Group permissions
        if ($symbolic[3] === 'r') {
            $mode |= 0040;
        }
        if ($symbolic[4] === 'w') {
            $mode |= 0020;
        }
        if ($symbolic[5] === 'x') {
            $mode |= 0010;
        }

        // Others permissions
        if ($symbolic[6] === 'r') {
            $mode |= 0004;
        }
        if ($symbolic[7] === 'w') {
            $mode |= 0002;
        }
        if ($symbolic[8] === 'x') {
            $mode |= 0001;
        }

        return new self($mode);
    }

    /**
     * Predefined permissions for common use cases
     */

    public static function readWriteOwner(): self
    {
        return new self(0600); // rw-------
    }

    public static function readWriteOwnerReadGroup(): self
    {
        return new self(0640); // rw-r-----
    }

    public static function readWriteOwnerReadAll(): self
    {
        return new self(0644); // rw-r--r--
    }

    public static function readWriteAll(): self
    {
        return new self(0666); // rw-rw-rw-
    }

    public static function executable(): self
    {
        return new self(0755); // rwxr-xr-x
    }

    public static function executableOwnerOnly(): self
    {
        return new self(0700); // rwx------
    }

    public static function directoryDefault(): self
    {
        return new self(0755); // rwxr-xr-x
    }

    public static function directoryPrivate(): self
    {
        return new self(0700); // rwx------
    }

    /**
     * Get octal representation as string
     */
    public function toOctal(): string
    {
        return sprintf('%04o', $this->mode);
    }

    /**
     * Get symbolic representation (e.g., "rw-r--r--")
     */
    public function toSymbolic(): string
    {
        $perms = '';

        // Owner
        $perms .= ($this->mode & 0400) ? 'r' : '-';
        $perms .= ($this->mode & 0200) ? 'w' : '-';
        $perms .= ($this->mode & 0100) ? 'x' : '-';

        // Group
        $perms .= ($this->mode & 0040) ? 'r' : '-';
        $perms .= ($this->mode & 0020) ? 'w' : '-';
        $perms .= ($this->mode & 0010) ? 'x' : '-';

        // Others
        $perms .= ($this->mode & 0004) ? 'r' : '-';
        $perms .= ($this->mode & 0002) ? 'w' : '-';
        $perms .= ($this->mode & 0001) ? 'x' : '-';

        return $perms;
    }

    /**
     * Check if owner has read permission
     */
    public function ownerCanRead(): bool
    {
        return (bool) ($this->mode & 0400);
    }

    /**
     * Check if owner has write permission
     */
    public function ownerCanWrite(): bool
    {
        return (bool) ($this->mode & 0200);
    }

    /**
     * Check if owner has execute permission
     */
    public function ownerCanExecute(): bool
    {
        return (bool) ($this->mode & 0100);
    }

    /**
     * Check if group has read permission
     */
    public function groupCanRead(): bool
    {
        return (bool) ($this->mode & 0040);
    }

    /**
     * Check if others have read permission
     */
    public function othersCanRead(): bool
    {
        return (bool) ($this->mode & 0004);
    }

    /**
     * Check if file is readable by anyone
     */
    public function isReadable(): bool
    {
        return $this->ownerCanRead() || $this->groupCanRead() || $this->othersCanRead();
    }

    /**
     * Check if file is writable by anyone
     */
    public function isWritable(): bool
    {
        return (bool) ($this->mode & 0222); // Any write bit set
    }

    public function equals(self $other): bool
    {
        return $this->mode === $other->mode;
    }

    public function toString(): string
    {
        return $this->toOctal();
    }
}
