"""
Environment file parser for kvenv.

Supports:
- KEY=VALUE format
- Comments (lines starting with #)
- Blank lines
- Optional leading "export " prefix
- Quoted values ("..." or '...')
- Values containing '='
"""

from __future__ import annotations

import re
from dataclasses import dataclass
from pathlib import Path
from typing import Iterator

# Valid environment variable name pattern
# Must start with letter or underscore, followed by letters, digits, or underscores
ENV_VAR_PATTERN = re.compile(r"^[A-Za-z_][A-Za-z0-9_]*$")


@dataclass
class EnvEntry:
    """Represents a parsed environment variable entry."""

    key: str
    value: str
    line_number: int
    is_keyvault_ref: bool = False
    vault_name: str | None = None
    secret_name: str | None = None


class EnvParseError(Exception):
    """Raised when there's an error parsing the env file."""

    def __init__(self, message: str, line_number: int | None = None) -> None:
        self.line_number = line_number
        if line_number is not None:
            message = f"Line {line_number}: {message}"
        super().__init__(message)


def _trim(s: str) -> str:
    """Trim leading and trailing whitespace."""
    return s.strip()


def _unquote(value: str) -> str:
    """Remove surrounding single or double quotes if present."""
    if len(value) >= 2:
        if (value.startswith('"') and value.endswith('"')) or (
            value.startswith("'") and value.endswith("'")
        ):
            return value[1:-1]
    return value


def _is_valid_env_var_name(name: str) -> bool:
    """Check if the given name is a valid environment variable name."""
    return bool(ENV_VAR_PATTERN.match(name))


def _parse_keyvault_ref(
    value: str, default_vault: str | None
) -> tuple[str | None, str | None]:
    """
    Parse a kv:// reference and return (vault_name, secret_name).

    Formats:
    - kv://SECRET_NAME -> uses default vault
    - kv://VAULT_NAME/SECRET_NAME -> uses specified vault
    """
    if not value.startswith("kv://"):
        return None, None

    ref = value[5:]  # Remove "kv://" prefix

    if "/" in ref:
        # kv://VAULT/SECRET format
        parts = ref.split("/", 1)
        vault = parts[0]
        secret = parts[1] if len(parts) > 1 else ""
    else:
        # kv://SECRET format - use default vault
        vault = default_vault
        secret = ref

    return vault, secret


def parse_env_file(
    file_path: Path, default_vault: str | None = None
) -> Iterator[EnvEntry]:
    """
    Parse an environment file and yield EnvEntry objects.

    Args:
        file_path: Path to the environment file
        default_vault: Default Key Vault name for kv:// references without vault

    Yields:
        EnvEntry objects for each valid environment variable

    Raises:
        EnvParseError: If there's an error parsing the file
        FileNotFoundError: If the file doesn't exist
    """
    if not file_path.exists():
        raise FileNotFoundError(f"Environment file not found: {file_path}")

    with open(file_path, encoding="utf-8") as f:
        for line_number, raw_line in enumerate(f, start=1):
            # Trim whitespace
            line = _trim(raw_line)

            # Skip empty lines
            if not line:
                continue

            # Skip comments
            if line.startswith("#"):
                continue

            # Handle "export KEY=VALUE" format
            if line.startswith("export "):
                line = _trim(line[7:])  # Remove "export " prefix

            # Must contain '='
            if "=" not in line:
                # Warning: ignoring malformed line
                continue

            # Split on first '=' only
            key, value = line.split("=", 1)
            key = _trim(key)
            value = _trim(value)
            value = _unquote(value)

            # Validate key name
            if not _is_valid_env_var_name(key):
                # Warning: ignoring invalid variable name
                continue

            # Check for kv:// reference
            if value.startswith("kv://"):
                vault, secret = _parse_keyvault_ref(value, default_vault)

                if not secret:
                    raise EnvParseError(
                        f"Invalid kv reference (missing secret name): {value}",
                        line_number,
                    )

                yield EnvEntry(
                    key=key,
                    value=value,
                    line_number=line_number,
                    is_keyvault_ref=True,
                    vault_name=vault,
                    secret_name=secret,
                )
            else:
                yield EnvEntry(
                    key=key,
                    value=value,
                    line_number=line_number,
                    is_keyvault_ref=False,
                )
