"""
Common utility functions for the cable routing system.

This module contains frequently used helper functions for:
- File and path operations
- Environment variable handling
- JSON operations
- DXF file handling
- Dictionary operations
"""

import json
import os
import sys
from pathlib import Path
from typing import Any, Tuple

import ezdxf
from ezdxf.lldxf.const import DXFStructureError


# ============================================================================
# File Operations
# ============================================================================

def check_file_in_work(work_dir, filename) -> Tuple[Path, bool]:
    """
    Check if a file exists, either at the given path or in the work directory.

    Args:
        work_dir: Working directory to check if file not found at filename path (str or Path)
        filename: Path to the file to check (str or Path)

    Returns:
        Tuple of (resolved_path, exists_flag)
        - resolved_path: Path where file was found (or would be)
        - exists_flag: True if file exists, False otherwise
    """
    work_dir = Path(work_dir)
    filename = Path(filename)

    fexists = True
    if not filename.exists():
        mypath = work_dir.joinpath(filename)
        if not mypath.exists():
            fexists = False
    else:
        mypath = filename
    return mypath, fexists


# ============================================================================
# Environment Variable Operations
# ============================================================================

def check_environment_var(env_str: str) -> Path:
    """
    Get and validate an environment variable as a Path.

    Args:
        env_str: Name of the environment variable

    Returns:
        Path object from the environment variable

    Exits:
        Exits the program if the environment variable is not set or empty
    """
    out_path = os.environ.get(env_str)
    if out_path:
        return Path(out_path)
    else:
        print(f"Umgebungsvariable {env_str} ist nicht gesetzt oder leer.")
        exit()


# ============================================================================
# JSON Operations
# ============================================================================

def to_json(d: Any, pretty: bool = True) -> str:
    """
    Convert a Python object to a JSON string.

    Args:
        d: Object to convert to JSON
        pretty: If True, format with indentation for readability

    Returns:
        JSON string representation of the object

    Note:
        Uses ensure_ascii=False to properly display German umlauts (ä, ö, ü)
    """
    return json.dumps(d, indent=2 if pretty else None, ensure_ascii=False, default=str)


def load_json(jsonfilename: str) -> dict:
    """
    Load JSON data from a file.

    Args:
        jsonfilename: Path to the JSON file

    Returns:
        Parsed JSON data as a dictionary
    """
    with open(jsonfilename, encoding='utf-8') as fh:
        return json.load(fh)


def write_results(jsn_results: str, out_dir: Path, filename: str) -> None:
    """
    Write JSON results to a file.

    Args:
        jsn_results: JSON string to write
        out_dir: Output directory path
        filename: Name of the output file
    """
    print("writing results to a json file ...")
    outfile = Path(out_dir) / filename
    with open(outfile, 'w', encoding='utf-8') as fh:
        fh.write(jsn_results)
    print("done")


# ============================================================================
# DXF File Operations
# ============================================================================

def dxf_is_binary(dxf_path: Path) -> bool:
    """
    Check if a DXF file is in binary format.

    Args:
        dxf_path: Path to the DXF file

    Returns:
        True if the file is a binary DXF, False otherwise
    """
    with open(dxf_path, 'rb') as f:
        header = f.read(22)
        return b'AutoCAD Binary DXF' in header


def get_dxf_file(filepath: Path):
    """
    Load a DXF file using ezdxf.

    Args:
        filepath: Path to the DXF file

    Returns:
        ezdxf Drawing object

    Exits:
        Exits with code 1 for I/O errors
        Exits with code 2 for invalid/corrupted DXF files
    """
    try:
        print("reading file ..", end='')
        doc = ezdxf.filemanagement.readfile(filepath)
        print("done")
    except IOError:
        print("Not a DXF file or a generic I/O error.")
        sys.exit(1)
    except DXFStructureError:
        print("Invalid or corrupted DXF file.")
        sys.exit(2)
    return doc


# ============================================================================
# Dictionary Operations
# ============================================================================

def merge_two_dicts(x: dict, y: dict) -> dict:
    """
    Merge two dictionaries, with values from y overwriting those in x.

    Args:
        x: First dictionary (base)
        y: Second dictionary (override values)

    Returns:
        New dictionary with merged values
    """
    z = x.copy()
    z.update(y)
    return z
