"""Tool for file format conversion."""

from __future__ import annotations

import json
import subprocess
from pathlib import Path
from typing import Any

from .base import Tool


class FileConvertTool(Tool):
    """Convert between file formats: MD to HTML, HTML to PDF, image resize/convert."""

    def __init__(self, *, workspace: Path, allowed_dir: Path | None = None) -> None:
        self._workspace = workspace
        self._allowed_dir = allowed_dir

    @property
    def name(self) -> str:
        return "file_convert"

    @property
    def description(self) -> str:
        return (
            "Convert file formats. Supports: "
            "markdown_to_html, html_to_pdf (via wkhtmltopdf or weasyprint), "
            "image_convert (resize, change format via Pillow), "
            "csv_to_json, json_to_csv."
        )

    @property
    def parameters(self) -> dict[str, Any]:
        return {
            "type": "object",
            "properties": {
                "conversion": {
                    "type": "string",
                    "enum": [
                        "markdown_to_html",
                        "html_to_pdf",
                        "image_convert",
                        "csv_to_json",
                        "json_to_csv",
                    ],
                    "description": "Conversion type to perform.",
                },
                "input_file": {
                    "type": "string",
                    "description": "Workspace-relative input file path.",
                },
                "output_file": {
                    "type": "string",
                    "description": "Workspace-relative output file path.",
                },
                "width": {
                    "type": "integer",
                    "description": "Target width for image_convert.",
                },
                "height": {
                    "type": "integer",
                    "description": "Target height for image_convert.",
                },
                "quality": {
                    "type": "integer",
                    "description": "JPEG quality (1-100) for image_convert. Default 85.",
                },
            },
            "required": ["conversion", "input_file"],
        }

    def _resolve(self, rel: str) -> Path | None:
        path = (self._workspace / rel).resolve()
        if not str(path).startswith(str(self._workspace.resolve())):
            return None
        if self._allowed_dir and not str(path).startswith(str(self._allowed_dir.resolve())):
            return None
        return path

    async def execute(self, **kwargs: Any) -> str:
        conversion = kwargs.get("conversion", "")
        input_file = kwargs.get("input_file", "")
        output_file = kwargs.get("output_file", "")

        if not input_file:
            return json.dumps({"error": "input_file is required"})

        in_path = self._resolve(input_file)
        if in_path is None:
            return json.dumps({"error": "Input path outside allowed directory"})
        if not in_path.exists():
            return json.dumps({"error": f"Input file not found: {input_file}"})

        handlers = {
            "markdown_to_html": self._md_to_html,
            "html_to_pdf": self._html_to_pdf,
            "image_convert": self._image_convert,
            "csv_to_json": self._csv_to_json,
            "json_to_csv": self._json_to_csv,
        }

        handler = handlers.get(conversion)
        if not handler:
            return json.dumps({"error": f"Unknown conversion: {conversion}"})

        return await handler(in_path, output_file, kwargs)

    async def _md_to_html(self, in_path: Path, output_file: str, kwargs: dict) -> str:
        try:
            import markdown
        except ImportError:
            return json.dumps({"error": "markdown not installed. Run: pip install markdown"})

        md_text = in_path.read_text(encoding="utf-8")
        html = markdown.markdown(md_text, extensions=["tables", "fenced_code", "toc"])
        html_doc = f"<!DOCTYPE html><html><head><meta charset='utf-8'></head><body>{html}</body></html>"

        out_file = output_file or in_path.stem + ".html"
        out_path = self._resolve(out_file)
        if out_path is None:
            return json.dumps({"error": "Output path outside allowed directory"})
        out_path.parent.mkdir(parents=True, exist_ok=True)
        out_path.write_text(html_doc, encoding="utf-8")
        return json.dumps({"ok": True, "output": str(out_path), "size": len(html_doc)})

    async def _html_to_pdf(self, in_path: Path, output_file: str, kwargs: dict) -> str:
        out_file = output_file or in_path.stem + ".pdf"
        out_path = self._resolve(out_file)
        if out_path is None:
            return json.dumps({"error": "Output path outside allowed directory"})
        out_path.parent.mkdir(parents=True, exist_ok=True)

        try:
            from weasyprint import HTML
            HTML(filename=str(in_path)).write_pdf(str(out_path))
            return json.dumps({"ok": True, "output": str(out_path)})
        except ImportError:
            pass

        try:
            result = subprocess.run(
                ["wkhtmltopdf", "--quiet", str(in_path), str(out_path)],
                capture_output=True, text=True, timeout=60,
            )
            if result.returncode == 0:
                return json.dumps({"ok": True, "output": str(out_path)})
            return json.dumps({"error": f"wkhtmltopdf failed: {result.stderr[:500]}"})
        except FileNotFoundError:
            return json.dumps({"error": "Neither weasyprint nor wkhtmltopdf available"})

    async def _image_convert(self, in_path: Path, output_file: str, kwargs: dict) -> str:
        try:
            from PIL import Image
        except ImportError:
            return json.dumps({"error": "Pillow not installed. Run: pip install Pillow"})

        out_file = output_file or in_path.stem + ".png"
        out_path = self._resolve(out_file)
        if out_path is None:
            return json.dumps({"error": "Output path outside allowed directory"})
        out_path.parent.mkdir(parents=True, exist_ok=True)

        try:
            img = Image.open(in_path)
            orig_size = img.size
            width = kwargs.get("width")
            height = kwargs.get("height")
            if width or height:
                w = width or int(img.width * (height / img.height))
                h = height or int(img.height * (width / img.width))
                img = img.resize((w, h), Image.LANCZOS)

            quality = kwargs.get("quality", 85)
            save_kwargs: dict[str, Any] = {}
            if out_path.suffix.lower() in (".jpg", ".jpeg"):
                save_kwargs["quality"] = quality
            img.save(str(out_path), **save_kwargs)
            return json.dumps({
                "ok": True,
                "output": str(out_path),
                "original_size": list(orig_size),
                "new_size": list(img.size),
            })
        except Exception as exc:
            return json.dumps({"error": f"Image conversion failed: {exc}"})

    async def _csv_to_json(self, in_path: Path, output_file: str, kwargs: dict) -> str:
        import csv as csv_mod

        out_file = output_file or in_path.stem + ".json"
        out_path = self._resolve(out_file)
        if out_path is None:
            return json.dumps({"error": "Output path outside allowed directory"})
        out_path.parent.mkdir(parents=True, exist_ok=True)

        with open(in_path, newline="", encoding="utf-8") as f:
            reader = csv_mod.DictReader(f)
            rows = list(reader)

        out_path.write_text(json.dumps(rows, indent=2), encoding="utf-8")
        return json.dumps({"ok": True, "output": str(out_path), "rows": len(rows)})

    async def _json_to_csv(self, in_path: Path, output_file: str, kwargs: dict) -> str:
        import csv as csv_mod

        out_file = output_file or in_path.stem + ".csv"
        out_path = self._resolve(out_file)
        if out_path is None:
            return json.dumps({"error": "Output path outside allowed directory"})
        out_path.parent.mkdir(parents=True, exist_ok=True)

        data = json.loads(in_path.read_text(encoding="utf-8"))
        if not isinstance(data, list) or not data:
            return json.dumps({"error": "JSON must be an array of objects"})

        fieldnames = list(data[0].keys())
        with open(out_path, "w", newline="", encoding="utf-8") as f:
            writer = csv_mod.DictWriter(f, fieldnames=fieldnames)
            writer.writeheader()
            writer.writerows(data)

        return json.dumps({"ok": True, "output": str(out_path), "rows": len(data)})
