"""
Pheser — Scanner
Reads and displays all EXIF metadata from an image,
including a privacy-risk score and GPS map link.
"""

from __future__ import annotations
import os
from typing import Any

from PIL import Image
import piexif

from .utils import decode_value, gps_to_deg, file_hash, file_size_fmt

# Tags that are considered privacy-sensitive
SENSITIVE_TAGS = {
    "GPSLatitude", "GPSLongitude", "GPSAltitude",
    "GPSDateStamp", "GPSTimeStamp", "GPSImgDirection",
    "GPSSpeed", "GPSDestLatitude", "GPSDestLongitude",
    "Make", "Model", "Software",
    "DateTime", "DateTimeOriginal", "DateTimeDigitized",
    "CameraOwnerName", "BodySerialNumber", "LensSerialNumber",
    "Artist", "Copyright",
    "HostComputer", "ImageDescription",
    "UserComment",
}


def _risk_score(found_sensitive: list[str]) -> tuple[int, str]:
    """Return (score 0-100, label)."""
    weights = {
        "GPSLatitude": 30, "GPSLongitude": 30,
        "GPSAltitude": 5, "GPSDateStamp": 5, "GPSTimeStamp": 5,
        "Make": 3, "Model": 3, "Software": 2,
        "DateTime": 4, "DateTimeOriginal": 5, "DateTimeDigitized": 3,
        "CameraOwnerName": 8, "BodySerialNumber": 6, "LensSerialNumber": 4,
        "Artist": 7, "Copyright": 5,
        "HostComputer": 5, "ImageDescription": 4, "UserComment": 4,
    }
    score = sum(weights.get(t, 2) for t in found_sensitive)
    score = min(score, 100)
    if score == 0:
        label = " Safe"
    elif score < 20:
        label = " Low"
    elif score < 50:
        label = " Medium"
    else:
        label = " High"
    return score, label


def scan(
    file: str,
    *,
    show_hash: bool = True,
    show_risk: bool = True,
    show_map: bool = True,
    raw: bool = False,
) -> dict[str, Any] | None:
    """
    Scan metadata from an image file and print a detailed report.

    Parameters
    ----------
    file       : Path to the image.
    show_hash  : Print SHA-256 hash of the file.
    show_risk  : Print a privacy-risk score.
    show_map   : Print a Google Maps link when GPS data is present.
    raw        : Return the raw results dict instead of printing.

    Returns
    -------
    dict with all findings, or None on error.
    """
    if not os.path.isfile(file):
        print(f"[pheser]  File not found: {file}")
        return None

    try:
        img = Image.open(file)
    except Exception as e:
        print(f"[pheser]  Cannot open image: {e}")
        return None

    results: dict[str, Any] = {
        "file": file,
        "format": img.format,
        "mode": img.mode,
        "size": img.size,
        "file_size": file_size_fmt(file),
        "sha256": file_hash(file) if show_hash else None,
        "exif": {},
        "gps": {},
        "sensitive_tags": [],
        "risk_score": 0,
        "risk_label": " Safe",
    }

    if "exif" not in img.info:
        if not raw:
            _print_no_exif(results)
        return results

    exif_dict = piexif.load(img.info["exif"])

    lat = lon = lat_ref = lon_ref = None

    for ifd in exif_dict:
        if ifd == "thumbnail":
            continue
        ifd_data = {}
        for tag in exif_dict[ifd]:
            try:
                tag_name = piexif.TAGS[ifd][tag]["name"]
            except KeyError:
                tag_name = f"Tag_{tag}"
            raw_val = exif_dict[ifd][tag]
            value = decode_value(raw_val)
            ifd_data[tag_name] = value

            if tag_name in SENSITIVE_TAGS:
                results["sensitive_tags"].append(tag_name)

            # Collect GPS specifics (need raw for rational math)
            if tag_name == "GPSLatitude":
                lat = raw_val
            elif tag_name == "GPSLatitudeRef":
                lat_ref = raw_val
            elif tag_name == "GPSLongitude":
                lon = raw_val
            elif tag_name == "GPSLongitudeRef":
                lon_ref = raw_val

        results["exif"][ifd] = ifd_data

    # Resolve GPS
    if lat and lon:
        lat_deg = gps_to_deg(lat, lat_ref or b"N")
        lon_deg = gps_to_deg(lon, lon_ref or b"E")
        results["gps"] = {
            "latitude": lat_deg,
            "longitude": lon_deg,
            "maps_url": f"https://maps.google.com/?q={lat_deg},{lon_deg}",
        }

    # Risk
    score, label = _risk_score(results["sensitive_tags"])
    results["risk_score"] = score
    results["risk_label"] = label

    if not raw:
        _print_report(results, show_hash=show_hash, show_risk=show_risk, show_map=show_map)

    return results


# ── Pretty printers ────────────────────────────────────────────────────────────

def _divider(char="─", width=52):
    print(char * width)


def _print_no_exif(r: dict):
    _divider("═")
    print("  Pheser — METADATA SCAN")
    _divider("═")
    print(f"  File   : {r['file']}")
    print(f"  Format : {r['format']}  |  Size: {r['size'][0]}×{r['size'][1]}  |  {r['file_size']}")
    if r["sha256"]:
        print(f"  SHA256 : {r['sha256']}")
    _divider()
    print("  ℹ  No EXIF data found in this image.")
    _divider("═")


def _print_report(r: dict, *, show_hash: bool, show_risk: bool, show_map: bool):
    _divider("═")
    print("  Pheser — METADATA SCAN")
    _divider("═")
    print(f"  File   : {r['file']}")
    print(f"  Format : {r['format']}  |  Mode: {r['mode']}  |  {r['file_size']}")
    print(f"  Pixels : {r['size'][0]} × {r['size'][1]}")
    if show_hash and r["sha256"]:
        print(f"  SHA256 : {r['sha256']}")

    if show_risk:
        _divider()
        print(f"  Privacy Risk : {r['risk_label']}  ({r['risk_score']}/100)")
        if r["sensitive_tags"]:
            print(f"  Sensitive    : {', '.join(sorted(set(r['sensitive_tags'])))}")

    for ifd, tags in r["exif"].items():
        if not tags:
            continue
        _divider()
        print(f"  [{ifd}]")
        for name, val in tags.items():
            print(f"    {name:<30} {val}")

    if r["gps"] and show_map:
        _divider()
        print("   GPS LOCATION")
        print(f"    Latitude  : {r['gps']['latitude']}")
        print(f"    Longitude : {r['gps']['longitude']}")
        print(f"    Maps      : {r['gps']['maps_url']}")

    _divider("═")