"""
Deflato Python SDK — compress images with one line of code.

Usage:
    from deflato import Deflato

    client = Deflato("your-api-key")
    result = client.compress("photo.jpg", output="compressed.webp", quality=75, format="WEBP")
    print(f"Saved {result.savings_percent}%")
"""

__version__ = "1.1.0"

import os
import httpx
from dataclasses import dataclass
from typing import Optional


@dataclass
class CompressResult:
    """Result of compression."""

    success: bool
    input_size: int
    output_size: int
    input_dimensions: tuple
    output_dimensions: tuple
    savings_percent: int
    output_format: str
    output_path: Optional[str] = None
    error: Optional[str] = None


class Deflato:
    """Deflato API client."""

    def __init__(self, api_key: str, base_url: str = "https://deflato.com"):
        self.api_key = api_key
        self.base_url = base_url.rstrip("/")
        self._client = httpx.Client(
            headers={"Authorization": f"Bearer {api_key}"},
            timeout=120,
        )

    def compress(
        self,
        input_path: str,
        output: Optional[str] = None,
        quality: int = 80,
        max_dimension: int = 0,
        format: str = "JPEG",
        strip_exif: bool = True,
    ) -> CompressResult:
        """
        Compress an image file.

        Args:
            input_path: Path to input image
            output: Output path (default: {name}_compressed.{ext})
            quality: 1-100 (default: 80)
            max_dimension: Max width/height in px, 0 = no resize
            format: JPEG, PNG, WEBP, or AVIF
            strip_exif: Remove EXIF metadata

        Returns:
            CompressResult with sizes, dimensions, savings
        """
        if not os.path.exists(input_path):
            return CompressResult(
                success=False,
                input_size=0,
                output_size=0,
                input_dimensions=(0, 0),
                output_dimensions=(0, 0),
                savings_percent=0,
                output_format=format.upper(),
                error=f"File not found: {input_path}",
            )

        fmt = format.upper()
        ext_map = {"JPEG": ".jpg", "PNG": ".png", "WEBP": ".webp", "AVIF": ".avif"}

        if not output:
            base = os.path.splitext(input_path)[0]
            output = base + "_compressed" + ext_map.get(fmt, ".jpg")

        input_size = os.path.getsize(input_path)

        with open(input_path, "rb") as f:
            resp = self._client.post(
                f"{self.base_url}/api/v1/compress",
                files={"file": (os.path.basename(input_path), f)},
                data={
                    "quality": str(quality),
                    "max_dimension": str(max_dimension),
                    "output_format": fmt,
                    "strip_exif": "true" if strip_exif else "false",
                },
            )

        if resp.status_code != 200:
            try:
                err = resp.json().get("error", resp.text)
            except Exception:
                err = resp.text
            return CompressResult(
                success=False,
                input_size=input_size,
                output_size=0,
                input_dimensions=(0, 0),
                output_dimensions=(0, 0),
                savings_percent=0,
                output_format=fmt,
                error=err,
            )

        with open(output, "wb") as f:
            f.write(resp.content)

        output_size = os.path.getsize(output)
        savings = round((1 - output_size / input_size) * 100) if input_size > 0 else 0

        return CompressResult(
            success=True,
            input_size=input_size,
            output_size=output_size,
            input_dimensions=(0, 0),  # Not available from binary response
            output_dimensions=(0, 0),
            savings_percent=savings,
            output_format=fmt,
            output_path=output,
        )

    def info(
        self,
        input_path: str,
        quality: int = 80,
        max_dimension: int = 0,
        format: str = "JPEG",
    ) -> CompressResult:
        """
        Get compression info without saving the output.

        Returns:
            CompressResult with sizes, dimensions, savings
        """
        if not os.path.exists(input_path):
            return CompressResult(
                success=False,
                input_size=0,
                output_size=0,
                input_dimensions=(0, 0),
                output_dimensions=(0, 0),
                savings_percent=0,
                output_format=format.upper(),
                error=f"File not found: {input_path}",
            )

        with open(input_path, "rb") as f:
            resp = self._client.post(
                f"{self.base_url}/api/v1/info",
                files={"file": (os.path.basename(input_path), f)},
                data={
                    "quality": str(quality),
                    "max_dimension": str(max_dimension),
                    "output_format": format.upper(),
                },
            )

        if resp.status_code != 200:
            try:
                err = resp.json().get("error", resp.text)
            except Exception:
                err = resp.text
            return CompressResult(
                success=False,
                input_size=0,
                output_size=0,
                input_dimensions=(0, 0),
                output_dimensions=(0, 0),
                savings_percent=0,
                output_format=format.upper(),
                error=err,
            )

        data = resp.json()
        return CompressResult(
            success=data.get("success", False),
            input_size=data.get("input_size", 0),
            output_size=data.get("output_size", 0),
            input_dimensions=tuple(data.get("input_dimensions", (0, 0))),
            output_dimensions=tuple(data.get("output_dimensions", (0, 0))),
            savings_percent=data.get("savings_percent", 0),
            output_format=data.get("output_format", format.upper()),
            error=data.get("error"),
        )

    def compress_batch(
        self,
        input_paths: list,
        output_dir: Optional[str] = None,
        quality: int = 80,
        format: str = "JPEG",
    ) -> list:
        """
        Compress multiple images.

        Args:
            input_paths: List of file paths
            output_dir: Output directory (default: same as input)
            quality: 1-100
            format: JPEG, PNG, WEBP, or AVIF

        Returns:
            List of CompressResult
        """
        results = []
        ext_map = {"JPEG": ".jpg", "PNG": ".png", "WEBP": ".webp", "AVIF": ".avif"}
        ext = ext_map.get(format.upper(), ".jpg")

        for path in input_paths:
            if output_dir:
                base = os.path.splitext(os.path.basename(path))[0]
                output = os.path.join(output_dir, base + ext)
            else:
                output = None

            result = self.compress(path, output=output, quality=quality, format=format)
            results.append(result)

        return results

    def compress_document(
        self,
        input_path: str,
        output: Optional[str] = None,
        quality: int = 75,
        max_dimension: int = 1920,
    ) -> CompressResult:
        """
        Compress images inside a PDF, DOCX, PPTX, or XLSX document.
        Text and layout stay intact — only embedded images are optimized.

        Args:
            input_path: Path to document
            output: Output path (default: {name}_compressed.{ext})
            quality: Image quality 1-100 (default: 75)
            max_dimension: Max image dimension in px (default: 1920)

        Returns:
            CompressResult with sizes and savings
        """
        if not os.path.exists(input_path):
            return CompressResult(
                success=False,
                input_size=0,
                output_size=0,
                input_dimensions=(0, 0),
                output_dimensions=(0, 0),
                savings_percent=0,
                output_format="",
                error=f"File not found: {input_path}",
            )

        ext = os.path.splitext(input_path)[1].lower()
        if ext not in (".pdf", ".docx", ".pptx", ".xlsx"):
            return CompressResult(
                success=False,
                input_size=0,
                output_size=0,
                input_dimensions=(0, 0),
                output_dimensions=(0, 0),
                savings_percent=0,
                output_format="",
                error="Supported: PDF, DOCX, PPTX, XLSX",
            )

        if not output:
            base = os.path.splitext(input_path)[0]
            output = base + "_compressed" + ext

        input_size = os.path.getsize(input_path)

        with open(input_path, "rb") as f:
            resp = self._client.post(
                f"{self.base_url}/api/v1/compress-document",
                files={"file": (os.path.basename(input_path), f)},
                data={
                    "quality": str(quality),
                    "max_dimension": str(max_dimension),
                },
            )

        if resp.status_code != 200:
            try:
                err = resp.json().get("error", resp.text)
            except Exception:
                err = resp.text
            return CompressResult(
                success=False,
                input_size=input_size,
                output_size=0,
                input_dimensions=(0, 0),
                output_dimensions=(0, 0),
                savings_percent=0,
                output_format=ext,
                error=err,
            )

        with open(output, "wb") as f:
            f.write(resp.content)

        output_size = os.path.getsize(output)
        savings = round((1 - output_size / input_size) * 100) if input_size > 0 else 0

        return CompressResult(
            success=True,
            input_size=input_size,
            output_size=output_size,
            input_dimensions=(0, 0),
            output_dimensions=(0, 0),
            savings_percent=savings,
            output_format=ext,
            output_path=output,
        )

    def close(self):
        self._client.close()

    def __enter__(self):
        return self

    def __exit__(self, *args):
        self.close()
