"""Utilities for ADFMentor — file handling, ZIP extraction, and JSON reading."""

import json
import zipfile
import tempfile
from pathlib import Path
from typing import Any, Dict, List, Optional


# ── File handling ──────────────────────────────────────────────────────

def get_file_by_type(directory: str, extension: str) -> Optional[str]:
    """Find the first file with the given extension in a directory.

    Args:
        directory: Path to the directory to search
        extension: File extension to search for (e.g., '.json')

    Returns:
        Filename if found, None otherwise
    """
    try:
        dir_path = Path(directory).resolve()

        if not dir_path.exists() or not dir_path.is_dir():
            return None

        extension_lower = extension.lower()
        for file in dir_path.rglob(f"*{extension_lower}"):
            if file.is_file():
                return str(file.relative_to(dir_path))

        return None
    except (OSError, PermissionError):
        return None


# ── ZIP extraction ─────────────────────────────────────────────────────

def extract_zip_to_temp(zip_path: str) -> str:
    """Extract a ZIP file to a temporary directory.

    Args:
        zip_path: Path to the ZIP file

    Returns:
        Path to the temporary directory containing extracted files

    Raises:
        ValueError: If the path doesn't exist or is not a valid ZIP file
    """
    zip_path_obj = Path(zip_path).resolve()

    if not zip_path_obj.exists():
        raise FileNotFoundError(
            f"ZIP file not found: {zip_path}\n"
            f"Resolved to: {zip_path_obj}\n"
            f"Current working directory: {Path.cwd()}"
        )

    if not zip_path_obj.is_file():
        raise ValueError(
            f"Path is not a file: {zip_path}\n"
            f"Expected a ZIP file, but got a directory or other type."
        )

    if not zipfile.is_zipfile(zip_path_obj):
        raise ValueError(
            f"Invalid ZIP file: {zip_path}\n"
            f"The file exists but is not a valid ZIP archive.\n"
            f"File size: {zip_path_obj.stat().st_size} bytes"
        )

    temp_dir = tempfile.mkdtemp(prefix="adf_submission_")

    try:
        with zipfile.ZipFile(zip_path_obj, "r") as z:
            z.extractall(temp_dir)
    except zipfile.BadZipFile as e:
        import shutil
        shutil.rmtree(temp_dir, ignore_errors=True)
        raise ValueError(
            f"Corrupted ZIP file: {zip_path}\n"
            f"Error: {e}"
        )

    return temp_dir


# ── Path preparation ──────────────────────────────────────────────────

def prepare_answer_path(answer_path: str) -> str:
    """Resolve and prepare a submission path.

    Handles ZIP files and single JSON files.

    Args:
        answer_path: Path to the student submission

    Returns:
        Resolved path (directory or file) ready for processing
    """
    path = Path(answer_path).resolve()

    if not path.exists():
        raise FileNotFoundError(
            f"Submission path not found: {answer_path}\n"
            f"Resolved to: {path}\n"
            f"Current working directory: {Path.cwd()}\n"
            f"Please check that the path is correct."
        )

    if path.is_dir():
        return str(path)

    if path.is_file() and path.suffix.lower() == ".zip":
        return extract_zip_to_temp(str(path))

    if path.is_file() and path.suffix.lower() == ".json":
        return str(path)

    raise ValueError(
        f"Invalid submission path: {answer_path}\n"
        f"Path must be a directory, ZIP file, or a single JSON file.\n"
        f"Found: {path.suffix} file"
    )


# ── JSON reading ──────────────────────────────────────────────────────

def read_json_files(adf_path: str) -> str:
    """Read JSON file(s) and return their content as a string.

    For a single file: reads and returns its content.
    For a directory: finds all .json files recursively and returns all contents.

    Args:
        adf_path: Path to a single JSON file or directory containing JSON files

    Returns:
        JSON string with all file contents (keyed by filename)

    Raises:
        ValueError: If the path is invalid
    """
    p = Path(adf_path).resolve()

    if p.is_file() and p.suffix.lower() == ".json":
        data = json.loads(p.read_text(encoding="utf-8"))
        return json.dumps({p.name: data}, indent=2)

    if p.is_dir():
        all_files: Dict[str, Any] = {}
        for json_file in sorted(p.rglob("*.json")):
            try:
                data = json.loads(json_file.read_text(encoding="utf-8"))
                all_files[json_file.name] = data
            except (json.JSONDecodeError, OSError):
                continue
        return json.dumps(all_files, indent=2)

    raise ValueError(
        f"Invalid ADF path: {adf_path}\n"
        f"Expected a directory or .json file."
    )
