"""Implementazione `countings` usando il CLI `pygount` (JSON) con fallback.

Questo modulo fornisce `analyze_file_counts` e `analyze_paths`.
"""
from pathlib import Path
from typing import Dict, Any, Iterable, List
import json
import subprocess
import logging

try:
    import pygount  # type: ignore
    _HAS_PYGOUNT = True
except Exception:
    _HAS_PYGOUNT = False

_LOG = logging.getLogger(__name__)


def _map_pygount_json_item(item: Dict[str, Any]) -> Dict[str, Any]:
    # Support multiple pygount JSON shapes and key names
    physical = (
        item.get("raw_total_lines")
        or item.get("n_lines")
        or item.get("lines")
        or item.get("raw_lines")
        or item.get("lineCount")
        or item.get("line_count")
        or 0
    )
    code = (
        item.get("code")
        or item.get("n_code")
        or item.get("n_code_lines")
        or item.get("code_lines")
        or item.get("codeCount")
        or item.get("sourceCount")
        or 0
    )
    comment = (
        item.get("comment")
        or item.get("n_comment")
        or item.get("n_comment_lines")
        or item.get("comment_lines")
        or item.get("documentationCount")
        or 0
    )
    blank = (
        item.get("blank")
        or item.get("n_blank")
        or item.get("blank_lines")
        or item.get("emptyCount")
        or item.get("empty_count")
        or 0
    )
    language = item.get("language") or item.get("lang") or item.get("languageName") or "unknown"

    file_path = (
        item.get("filename")
        or item.get("file")
        or item.get("path")
        or item.get("name")
        or ""
    )

    return {
        "file": file_path,
        "physical_lines": int(physical),
        "code_lines": int(code),
        "comment_lines": int(comment),
        "blank_lines": int(blank),
        "language": language,
    }


def analyze_file_counts(path: Path) -> Dict[str, Any]:
    if not path.exists():
        raise FileNotFoundError(f"File non trovato: {path}")

    result: Dict[str, Any] = {
        "file": str(path),
        "physical_lines": 0,
        "code_lines": 0,
        "comment_lines": 0,
        "blank_lines": 0,
        "language": "unknown",
    }

    if _HAS_PYGOUNT:
        try:
            proc = subprocess.run(["pygount", "--format", "json", str(path)], check=True, capture_output=True, text=True)
            parsed = json.loads(proc.stdout)
            # Support expected JSON shapes from pygount: list or dict with 'files'
            if isinstance(parsed, list) and parsed:
                item = parsed[0]
                result.update(_map_pygount_json_item(item))
                return result
            if isinstance(parsed, dict):
                files = parsed.get("files")
                if files and isinstance(files, list) and files:
                    item = files[0]
                    result.update(_map_pygount_json_item(item))
                    return result
            # If pygount ran but returned no usable data, log stdout/stderr at DEBUG
            _LOG.debug("pygount returned empty or unexpected JSON for %s", path)
            _LOG.debug("pygount stdout:\n%s", proc.stdout)
            _LOG.debug("pygount stderr:\n%s", proc.stderr)
            # force fallback to simple counting
            raise RuntimeError("pygount returned no data")
        except Exception:
            # Log exception and stderr if available, then fall back to simple counting
            try:
                # If proc exists, include its stderr for diagnostics
                if 'proc' in locals():
                    _LOG.exception("pygount invocation failed for %s; stderr:\n%s", path, getattr(proc, 'stderr', None))
                else:
                    _LOG.exception("pygount invocation failed for %s", path)
            except Exception:
                # ensure we don't break on logging
                pass
            # fall back to simple counting
            pass

    # Fallback: basic counting
    with path.open("r", errors="ignore") as fh:
        lines = fh.readlines()
    physical = len(lines)
    blanks = sum(1 for l in lines if l.strip() == "")
    code_lines = physical - blanks

    result.update({
        "physical_lines": physical,
        "code_lines": code_lines,
        "comment_lines": 0,
        "blank_lines": blanks,
        "language": "unknown",
    })
    return result


def analyze_paths(paths: Iterable[Path]) -> List[Dict[str, Any]]:
    results: List[Dict[str, Any]] = []
    for p in paths:
        path = Path(p)
        try:
            results.append(analyze_file_counts(path))
        except Exception as e:
            results.append({"file": str(path), "error": str(e)})
    return results
