<?php

namespace StaticDeploy;

use GuzzleHttp\Psr7\Uri;
use GuzzleHttp\Psr7\UriNormalizer;
use GuzzleHttp\Psr7\Utils as Psr7Utils;
use Psr\Http\Message\UriInterface;

class PathInfo {
    // Values known upon detection.

    // If the path corresponds to a file on disk,
    // this will be the absolute path to the file.
    public ?string $filename;

    // The relative URL path, not containing the host.
    // E.g., "/author/user/"
    public readonly string $path;

    private ?string $content_hash;

    private ?string $path_hash = null;

    /**
     * array<string, string> Array of namespace to data_hash
     */
    public ?array $deploy_cache = null;

    public function __construct(
        string|UriInterface $path,
        ?string $filename = null,
        public ?string $body = null,
        ?string $content_hash = null,
        public ?string $content_type = null,
        public ?string $redirect_to = null,
        public ?int $status = null,
    ) {
        $uri = Psr7Utils::uriFor( $path );
        $msg = self::pathErrorMessage( $uri );
        if ( $msg ) {
            throw WsLog::ex( esc_html( "{$msg}: {$path}" ) );
        }

        $this->filename = $filename === '' ? null : $filename;

        $this->path = $path;

        if ( $content_hash !== null ) {
            $this->content_hash = $content_hash;
        }
    }

    /**
     * Check if a path is valid for use in a PathInfo object.
     *
     * Returns error message if any, or false if there are no errors.
     */
    public static function pathErrorMessage(
        UriInterface $uri,
    ): false|string {
        if ( ! Uri::isAbsolutePathReference( $uri ) ) {
            return 'Not an absolute path reference';
        }

        if ( $uri->getQuery() !== '' ) {
            return 'Path cannot contain query string';
        }

        if ( $uri->getFragment() !== '' ) {
            return 'Path cannot contain fragment';
        }

        return false;
    }

    /**
     * @return array<string, string|int>
     */
    public function toArray(): array {
        $arr = [
            'path' => $this->path,
        ];
        if ( isset( $this->body ) ) {
            $arr['body'] = $this->body;
        }
        if ( isset( $this->content_hash ) ) {
            $arr['content_hash'] = $this->content_hash;
        }
        if ( isset( $this->content_type ) ) {
            $arr['content_type'] = $this->content_type;
        }
        if ( isset( $this->filename ) ) {
            $arr['filename'] = $this->filename;
        }
        if ( isset( $this->redirect_to ) ) {
            $arr['redirect_to'] = $this->redirect_to;
        }
        if ( isset( $this->status ) ) {
            $arr['status'] = $this->status;
        }
        return $arr;
    }

    public function getContentHash(): ?string {
        if ( isset( $this->content_hash ) ) {
            return $this->content_hash;
        }

        if ( isset( $this->body ) ) {
            $this->content_hash = md5( $this->body );
        }

        if ( isset( $this->filename ) ) {
            $this->content_hash = md5_file( $this->filename );
        }

        return $this->content_hash ?? null;
    }

    public function getPathHash(): string {
        if ( $this->path_hash !== null ) {
            return $this->path_hash;
        }

        $this->path_hash = md5( $this->path );

        return $this->path_hash;
    }

    public function withBody( ?string $new_body ): self {
        $copy = clone $this;
        $copy->body = $new_body;
        $copy->content_hash = null;
        $copy->filename = null;
        return $copy;
    }

    public function withDeployCache( array $deploy_cache ): self {
        $copy = clone $this;
        $copy->deploy_cache = $deploy_cache;
        return $copy;
    }
}
