#!/usr/bin/env python3
"""
FreeExile WebP Texture Compressor.
Converts and optimizes PNG textures to WebP format for high-performance Web and Mobile deployment.
Target: Achieves <= 50% of original PNG file size, significantly reducing memory and transfer budgets.
"""

from __future__ import annotations

import argparse
from dataclasses import dataclass
from pathlib import Path
import sys
import time
from typing import List, Optional

from PIL import Image


@dataclass(slots=True, frozen=True)
class CompressionResult:
    """Stores metrics for a single texture compression operation."""
    input_path: Path
    output_path: Path
    original_size: int
    compressed_size: int
    compression_ratio: float      # compressed_size / original_size
    saved_bytes: int              # original_size - compressed_size
    reduction_percentage: float   # saved_bytes / original_size * 100
    duration_ms: float


def compress_to_webp(
    input_path: Path | str,
    output_path: Optional[Path | str] = None,
    quality: int = 80,
) -> CompressionResult:
    """Compresses a PNG/JPEG texture to WebP format.

    Args:
        input_path: Source image file path.
        output_path: Destination .webp path. If None, uses same stem + '.webp'.
        quality: WebP quality from 1 to 100 (default: 80).

    Returns:
        CompressionResult with file sizes and reduction statistics.
    """
    start_time = time.perf_counter()
    src = Path(input_path).resolve()
    if not src.exists() or not src.is_file():
        raise FileNotFoundError(f"Source texture not found: {src}")

    dst = Path(output_path).resolve() if output_path else src.with_suffix(".webp")
    dst.parent.mkdir(parents=True, exist_ok=True)

    orig_size = src.stat().st_size

    with Image.open(src) as img:
        # Preserve RGBA or convert appropriately
        if img.mode in ("RGBA", "LA") or ("transparency" in img.info):
            target_img = img.convert("RGBA")
        else:
            target_img = img.convert("RGB")

        # Save with optimal WebP settings
        target_img.save(
            dst,
            format="WEBP",
            quality=max(1, min(100, quality)),
            method=6,  # Maximum compression effort
        )

    compressed_size = dst.stat().st_size
    saved = orig_size - compressed_size
    ratio = compressed_size / max(1, orig_size)
    reduction_pct = (saved / max(1, orig_size)) * 100.0
    elapsed_ms = (time.perf_counter() - start_time) * 1000.0

    return CompressionResult(
        input_path=src,
        output_path=dst,
        original_size=orig_size,
        compressed_size=compressed_size,
        compression_ratio=ratio,
        saved_bytes=saved,
        reduction_percentage=reduction_pct,
        duration_ms=round(elapsed_ms, 2),
    )


def batch_compress_directory(
    source_dir: Path | str,
    output_dir: Optional[Path | str] = None,
    quality: int = 80,
    recursive: bool = True,
    pattern: str = "*.png",
) -> List[CompressionResult]:
    """Batch compresses all textures matching pattern in source_dir."""
    src_dir = Path(source_dir).resolve()
    if not src_dir.exists() or not src_dir.is_dir():
        raise NotADirectoryError(f"Directory not found: {src_dir}")

    target_root = Path(output_dir).resolve() if output_dir else None
    iterator = src_dir.rglob(pattern) if recursive else src_dir.glob(pattern)
    results: List[CompressionResult] = []

    for file_path in sorted(iterator):
        if file_path.suffix.lower() == ".webp":
            continue

        if target_root:
            rel = file_path.relative_to(src_dir)
            out_file = (target_root / rel).with_suffix(".webp")
        else:
            out_file = file_path.with_suffix(".webp")

        res = compress_to_webp(file_path, out_file, quality=quality)
        results.append(res)

    return results


def main() -> None:
    """CLI interface for texture compression."""
    parser = argparse.ArgumentParser(description="FreeExile WebP Texture Compressor")
    parser.add_argument("--input", "-i", type=Path, help="Input texture file")
    parser.add_argument("--output", "-o", type=Path, default=None, help="Output .webp path")
    parser.add_argument("--dir", "-d", type=Path, default=None, help="Directory to batch compress")
    parser.add_argument("--output-dir", type=Path, default=None, help="Target directory for batch")
    parser.add_argument("--quality", "-q", type=int, default=80, help="WebP quality (1-100)")
    parser.add_argument("--no-recursive", action="store_true", help="Disable recursive scan")
    parser.add_argument("--verify-budget", action="store_true", help="Assert WebP <= 50% of PNG size")
    args = parser.parse_args()

    if args.input:
        res = compress_to_webp(args.input, args.output, quality=args.quality)
        print(f"Compressed {res.input_path.name} -> {res.output_path.name}")
        print(f"  {res.original_size} -> {res.compressed_size} bytes ({res.compression_ratio*100:.1f}%)")
        print(f"  Saved: {res.saved_bytes} bytes ({res.reduction_percentage:.1f}%), Time: {res.duration_ms}ms")
        if args.verify_budget and res.compression_ratio > 0.50:
            print(f"[ERROR] Compression exceeded 50% budget: {res.compression_ratio*100:.1f}%", file=sys.stderr)
            sys.exit(1)
    elif args.dir:
        res_list = batch_compress_directory(
            args.dir,
            output_dir=args.output_dir,
            quality=args.quality,
            recursive=not args.no_recursive,
        )
        total_orig = sum(r.original_size for r in res_list)
        total_comp = sum(r.compressed_size for r in res_list)
        overall_ratio = total_comp / max(1, total_orig)
        print(f"Batch compressed {len(res_list)} files.")
        print(f"  Total: {total_orig} -> {total_comp} bytes ({overall_ratio*100:.1f}%)")
        print(f"  Saved: {total_orig - total_comp} bytes ({(1 - overall_ratio)*100:.1f}%)")
    else:
        parser.print_help()


if __name__ == "__main__":
    main()
