"""Human-safe command catalog for validating generated artifacts."""

from __future__ import annotations

from dataclasses import dataclass
from pathlib import Path
from typing import Sequence

from .models import PlatformHumanReport, as_plain_data


@dataclass(slots=True)
class CommandSpec:
    command_id: str
    title: str
    shell: str
    cwd: str
    purpose: str
    expected_result: str
    risk: str = "low"

    def to_dict(self) -> dict[str, object]:
        return as_plain_data(self)


def quote_path(path: Path | str) -> str:
    text = str(path).replace("'", "''")
    return f"'{text}'"


def base_validation_commands(project_root: Path, central_folder: Path | None = None) -> tuple[CommandSpec, ...]:
    commands = [
        CommandSpec(
            command_id="python-compileall",
            title="Compilar pacote Python",
            shell=f"$env:PYTHONPATH={quote_path(project_root / 'src')}; $env:MAIS_HUMANA_TEMP={quote_path(project_root / '.test-tmp')}; & $env:CODEX_PYTHON -m compileall {quote_path(project_root / 'src')}",
            cwd=str(project_root),
            purpose="Detectar erro de sintaxe nos modulos da plataforma.",
            expected_result="compileall finaliza sem erro.",
        ),
        CommandSpec(
            command_id="python-unittest",
            title="Executar testes unitarios",
            shell=f"$env:PYTHONPATH={quote_path(project_root / 'src')}; $env:MAIS_HUMANA_TEMP={quote_path(project_root / '.test-tmp')}; & $env:CODEX_PYTHON -m unittest discover -s {quote_path(project_root / 'tests')}",
            cwd=str(project_root),
            purpose="Validar scanner, matriz, DOCX, SQLite, ordens, contratos e redaction.",
            expected_result="todos os testes passam.",
        ),
        CommandSpec(
            command_id="generate-reports",
            title="Regenerar relatorios humanos",
            shell=(
                f"$env:PYTHONPATH={quote_path(project_root / 'src')}; & $env:CODEX_PYTHON -m mais_humana.cli generate --ecosystem-root "
                f"{quote_path(project_root.parent)} --project-root {quote_path(project_root)}"
            ),
            cwd=str(project_root),
            purpose="Gerar relatorios, graficos, matrizes e dados.",
            expected_result="JSON de bundle emitido e artefatos atualizados.",
        ),
        CommandSpec(
            command_id="count-code-lines",
            title="Contar linhas de codigo da plataforma",
            shell="rg --files -g '*.py' | ForEach-Object { Get-Content -LiteralPath $_ | Measure-Object -Line }",
            cwd=str(project_root),
            purpose="Comprovar volume de codigo util produzido.",
            expected_result="soma de linhas Python acima da meta da rodada.",
        ),
    ]
    if central_folder is not None:
        commands.append(
            CommandSpec(
                command_id="semantic-sql-counts",
                title="Contar registros do SQL semantico",
                shell=(
                    f"& $env:CODEX_PYTHON -m mais_humana.cli sql-counts --sqlite "
                    f"{quote_path(central_folder / 'controle-semantico.sqlite')}"
                ),
                cwd=str(project_root),
                purpose="Verificar memoria semantica compacta da pasta gerencial.",
                expected_result="contagens de files, service_orders, platform_reports e recommendations.",
            )
        )
    return tuple(commands)


def platform_validation_commands(reports: Sequence[PlatformHumanReport]) -> tuple[CommandSpec, ...]:
    commands: list[CommandSpec] = []
    for report in reports:
        scripts = {script.name: script.command for script in report.scan.scripts}
        if "test" in scripts:
            commands.append(
                CommandSpec(
                    command_id=f"{report.platform.platform_id}-test",
                    title=f"Testar {report.platform.title}",
                    shell="npm test",
                    cwd=report.scan.repo_path,
                    purpose="Executar teste canonico declarado pelo repositorio real.",
                    expected_result="suite do repositorio finaliza sem erro.",
                    risk="medium",
                )
            )
        if "build" in scripts:
            commands.append(
                CommandSpec(
                    command_id=f"{report.platform.platform_id}-build",
                    title=f"Build {report.platform.title}",
                    shell="npm run build",
                    cwd=report.scan.repo_path,
                    purpose="Validar empacotamento ou typecheck do repositorio real.",
                    expected_result="build finaliza sem erro.",
                    risk="medium",
                )
            )
    return tuple(commands)


def commands_markdown(commands: Sequence[CommandSpec]) -> str:
    lines = ["# Comandos humanos equivalentes", ""]
    for command in commands:
        lines.append(f"## {command.title}")
        lines.append("")
        lines.append(f"- id: `{command.command_id}`")
        lines.append(f"- cwd: `{command.cwd}`")
        lines.append(f"- risco: `{command.risk}`")
        lines.append(f"- finalidade: {command.purpose}")
        lines.append(f"- esperado: {command.expected_result}")
        lines.append("")
        lines.append("```powershell")
        lines.append(command.shell)
        lines.append("```")
        lines.append("")
    return "\n".join(lines).strip() + "\n"


def commands_by_risk(commands: Sequence[CommandSpec]) -> dict[str, int]:
    counts: dict[str, int] = {}
    for command in commands:
        counts[command.risk] = counts.get(command.risk, 0) + 1
    return dict(sorted(counts.items()))


def command_ids(commands: Sequence[CommandSpec]) -> tuple[str, ...]:
    return tuple(command.command_id for command in commands)


def command_summary(commands: Sequence[CommandSpec]) -> str:
    counts = commands_by_risk(commands)
    parts = [f"{risk}={count}" for risk, count in counts.items()]
    return f"{len(commands)} comandos; " + ", ".join(parts)


def filter_commands(commands: Sequence[CommandSpec], risk: str | None = None, text: str | None = None) -> tuple[CommandSpec, ...]:
    output: list[CommandSpec] = []
    lowered = text.lower() if text else None
    for command in commands:
        if risk and command.risk != risk:
            continue
        if lowered and lowered not in f"{command.title} {command.purpose} {command.expected_result}".lower():
            continue
        output.append(command)
    return tuple(output)


def first_command(commands: Sequence[CommandSpec], command_id: str) -> CommandSpec | None:
    for command in commands:
        if command.command_id == command_id:
            return command
    return None


def require_command(commands: Sequence[CommandSpec], command_id: str) -> CommandSpec:
    command = first_command(commands, command_id)
    if command is None:
        raise KeyError(command_id)
    return command


def command_titles(commands: Sequence[CommandSpec]) -> tuple[str, ...]:
    return tuple(command.title for command in commands)


def has_command(commands: Sequence[CommandSpec], command_id: str) -> bool:
    return first_command(commands, command_id) is not None


def command_count(commands: Sequence[CommandSpec]) -> int:
    return len(commands)


def command_count_label(commands: Sequence[CommandSpec]) -> str:
    return f"{command_count(commands)} comandos"
