import io
import zipfile
from pathlib import Path

import pytest
from fastapi.testclient import TestClient

from c2m_by_nery.api.app import app

client = TestClient(app)


def _zip_single_file(filename: str, content: str) -> bytes:
    buf = io.BytesIO()
    with zipfile.ZipFile(buf, "w", zipfile.ZIP_DEFLATED) as zf:
        zf.writestr(filename, content)
    buf.seek(0)
    return buf.getvalue()


def _zip_directory(stem: str, files: dict[str, str]) -> bytes:
    buf = io.BytesIO()
    with zipfile.ZipFile(buf, "w", zipfile.ZIP_DEFLATED) as zf:
        for name, content in files.items():
            zf.writestr(f"{stem}/{name}", content)
    buf.seek(0)
    return buf.getvalue()


def test_api_convert_single_file_zip() -> None:
    zip_bytes = _zip_single_file("code.py", "x = 1")
    response = client.post(
        "/convert",
        files={"file": ("upload.zip", zip_bytes, "application/zip")},
    )
    assert response.status_code == 200
    assert response.headers["content-type"] == "application/zip"
    assert "attachment" in response.headers.get("content-disposition", "")
    out = io.BytesIO(response.content)
    with zipfile.ZipFile(out, "r") as zf:
        names = zf.namelist()
    assert any(n.endswith(".md") for n in names)
    assert any("report.json" in n for n in names)


def test_api_convert_repo_zip() -> None:
    zip_bytes = _zip_directory("myrepo", {"foo.py": "a = 1", "bar.py": "b = 2"})
    response = client.post(
        "/convert",
        files={"file": ("repo.zip", zip_bytes, "application/zip")},
    )
    assert response.status_code == 200
    assert response.headers["content-type"] == "application/zip"
    out = io.BytesIO(response.content)
    with zipfile.ZipFile(out, "r") as zf:
        names = zf.namelist()
    assert any("foo.md" in n or "foo" in n for n in names)
    assert any("report.json" in n for n in names)


def test_api_convert_accepts_single_file() -> None:
    response = client.post(
        "/convert",
        files={"file": ("backstage.log", b"line one\nline two", "text/plain")},
    )
    assert response.status_code == 200
    assert response.headers["content-type"] == "application/zip"
    out = io.BytesIO(response.content)
    with zipfile.ZipFile(out, "r") as zf:
        names = zf.namelist()
    assert any(n.endswith(".md") for n in names)


def test_api_convert_rejects_empty_zip() -> None:
    buf = io.BytesIO()
    with zipfile.ZipFile(buf, "w", zipfile.ZIP_DEFLATED) as zf:
        pass
    response = client.post(
        "/convert",
        files={"file": ("empty.zip", buf.getvalue(), "application/zip")},
    )
    assert response.status_code == 400


def test_api_convert_with_token_limit() -> None:
    zip_bytes = _zip_single_file("x.py", "a = 1")
    response = client.post(
        "/convert",
        files={"file": ("x.zip", zip_bytes, "application/zip")},
        data={"token_limit": "500"},
    )
    assert response.status_code == 200
    assert response.headers["content-type"] == "application/zip"


def test_api_convert_with_flat() -> None:
    zip_bytes = _zip_directory("p", {"a.py": "1", "b.py": "2"})
    response = client.post(
        "/convert",
        files={"file": ("p.zip", zip_bytes, "application/zip")},
        data={"flat": "true"},
    )
    assert response.status_code == 200


def test_api_convert_serve_command_help() -> None:
    from typer.testing import CliRunner

    from c2m_by_nery.cli.main import app as cli_app

    runner = CliRunner()
    result = runner.invoke(cli_app, ["--help"])
    assert result.exit_code == 0
    assert "serve" in result.stdout.lower()
    result_serve = runner.invoke(cli_app, ["serve", "--help"])
    assert result_serve.exit_code == 0
    assert "port" in result_serve.stdout.lower()
    assert "8000" in result_serve.stdout
