import os
import sys
import subprocess
from pathlib import Path

import click

from abcforge._batch import run_batch

MUSICXML_EXTENSIONS = {".xml", ".mxl", ".musicxml"}
_VENDOR_XML2ABC = Path(__file__).parent.parent / "vendor" / "xml2abc" / "xml2abc.py"


def _make_convert_fn(input_dir, output_dir):
    def convert_one(file_path):
        rel = os.path.relpath(os.path.dirname(file_path), input_dir)
        out_dir = os.path.join(output_dir, rel)
        os.makedirs(out_dir, exist_ok=True)
        result = subprocess.run(
            [sys.executable, str(_VENDOR_XML2ABC), "-d", "8", "-x", file_path],
            stdout=subprocess.PIPE,
            stderr=subprocess.PIPE,
            check=False,
        )
        output = result.stdout.decode("utf-8", errors="replace")
        if not output.strip():
            err = result.stderr.decode("utf-8", errors="replace").strip()
            return file_path, False, err or "no output"
        out_path = os.path.join(out_dir, Path(file_path).stem + ".abc")
        with open(out_path, "w", encoding="utf-8") as f:
            f.write(output)
        return file_path, True, None

    return convert_one


@click.command()
@click.argument("input_dir", type=click.Path(exists=True, file_okay=False))
@click.argument("output_dir", type=click.Path(file_okay=False))
@click.option("--workers", default=None, type=int, help="Parallel workers (default: cpu count)")
@click.option("--log", "log_path", default=None, type=click.Path(), help="Write errors to file instead of stderr")
@click.option("--resume", is_flag=True, default=False, help="Skip files whose output already exists")
def command(input_dir, output_dir, workers, log_path, resume):
    """Convert MusicXML files to ABC notation."""
    input_dir = os.path.abspath(input_dir)
    output_dir = os.path.abspath(output_dir)
    os.makedirs(output_dir, exist_ok=True)

    files = [
        os.path.join(root, f)
        for root, _, fnames in os.walk(input_dir)
        for f in fnames
        if Path(f).suffix.lower() in MUSICXML_EXTENSIONS
    ]
    if not files:
        raise click.ClickException(f"No MusicXML files found in {input_dir}")

    if resume:
        def _out(fp):
            rel = os.path.relpath(os.path.dirname(fp), input_dir)
            return os.path.join(output_dir, rel, Path(fp).stem + ".abc")
        skipped = sum(1 for f in files if os.path.exists(_out(f)))
        files = [f for f in files if not os.path.exists(_out(f))]
        click.echo(f"Resuming: {skipped} already done, {len(files)} remaining.", err=True)

    if not files:
        click.echo("Nothing to do.", err=True)
        return

    click.echo(f"Found {len(files)} files.", err=True)
    ok, failed = run_batch(
        files,
        _make_convert_fn(input_dir, output_dir),
        desc="xml2abc",
        workers=workers or os.cpu_count(),
        log_path=log_path,
    )
    click.echo(f"\nDone. Converted: {ok}, Failed: {failed}", err=True)
    if failed and log_path:
        click.echo(f"Failures logged to: {log_path}", err=True)
