# Copyright 2024 Klira AI
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#     http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

"""Input validation utilities for URLs and filesystem paths.

Provides security validation to prevent SSRF (PROD-472), URL path traversal
(PROD-473), and filesystem path traversal (PROD-474).
"""

from __future__ import annotations

import urllib.parse
from pathlib import Path, PurePosixPath

from klira.sdk.exceptions import KliraConfigError

ALLOWED_HOSTNAMES: frozenset[str] = frozenset({"api.getklira.com", "dev.api.getklira.com"})


def validate_endpoint(
    url: str,
    *,
    allowed_hostnames: frozenset[str] = ALLOWED_HOSTNAMES,
) -> str:
    """Validate that a URL uses HTTPS and points to an allowed hostname.

    Args:
        url: The endpoint URL to validate.
        allowed_hostnames: Set of permitted hostnames.

    Returns:
        The URL string unchanged if valid.

    Raises:
        KliraConfigError: If the URL scheme is not HTTPS or hostname
            is not in the allowlist.
    """
    parsed = urllib.parse.urlparse(url)

    if parsed.scheme != "https":
        raise KliraConfigError(
            "Endpoint URL must use HTTPS scheme"
        )

    if not parsed.hostname or parsed.hostname not in allowed_hostnames:
        raise KliraConfigError(
            f"Endpoint hostname not allowed. "
            f"Permitted hostnames: {', '.join(sorted(allowed_hostnames))}"
        )

    return url


def validate_path(path: str) -> Path:
    """Validate a filesystem path and reject path traversal attempts.

    Rejects paths containing '..' segments to prevent directory traversal.
    Returns a resolved (canonicalized) Path object.

    Args:
        path: The filesystem path string to validate.

    Returns:
        A resolved Path object.

    Raises:
        KliraConfigError: If the path contains '..' traversal segments.
    """
    parts = PurePosixPath(path).parts
    if ".." in parts:
        raise KliraConfigError(
            "Path must not contain '..' traversal segments"
        )

    return Path(path).resolve(strict=False)


def sanitize_url_path_segment(value: str) -> str:
    """Percent-encode a value for safe use in a URL path segment.

    Args:
        value: The string to encode (e.g., a dataset ID).

    Returns:
        The percent-encoded string with all special characters escaped.
    """
    return urllib.parse.quote(value, safe="")
