"""
helpers.py

Purpose:
  Miscellaneous utilities reused across routes:
    - Timestamp parsing from camera filenames
    - Human-readable formatting
    - Normalization of paths into the `static/User-photos` namespace

Exports:
  - parse_ts_from_any(path_or_name) -> (datetime|None, camera_id|None)
  - format_dt(datetime|None) -> str
  - normalize_to_static_user_photos(path: str) -> str
"""

import os
import re
from datetime import datetime
import base64
import hmac
import hashlib
from flask import current_app, url_for


def parse_ts_from_any(path_or_name: str):
    name = os.path.basename(path_or_name)
    m = re.match(r"PICT_(\d{4})(\d{2})(\d{2})_(\d{2})(\d{2})(\d{2})_([A-Za-z0-9]{12})", name, re.IGNORECASE)
    if not m:
        return None, None
    year, a, b, hh, mm, ss, cam = m.groups()
    # Primary format in data appears to be YYYYDDMM
    try:
        day = int(a); month = int(b)
        ts = datetime(int(year), month, day, int(hh), int(mm), int(ss))
        return ts, cam
    except ValueError:
        pass
    # Fallback try YYYYMMDD
    try:
        month = int(a); day = int(b)
        ts = datetime(int(year), month, day, int(hh), int(mm), int(ss))
        return ts, cam
    except ValueError:
        return None, cam


def format_dt(dt: datetime|None) -> str:
    if not dt:
        return 'Nema aktivnosti'
    return dt.strftime('%d.%m.%Y %H:%M')


def normalize_to_static_user_photos(path: str) -> str:
    if not path:
        return 'User-photos/'
    p = path.replace('\\', '/').lstrip('/')
    if p.startswith('static/'):
        p = p[len('static/') : ]
    if 'User-photos/' in p:
        p = p[p.find('User-photos/') : ]
    elif '/' not in p:
        p = f'User-photos/{p}'
    return p


# -------- Secure media token helpers --------

def _b64url_encode(data: bytes) -> str:
    return base64.urlsafe_b64encode(data).decode('utf-8').rstrip('=')


def _b64url_decode(data: str) -> bytes:
    padding = '=' * (-len(data) % 4)
    return base64.urlsafe_b64decode(data + padding)


def generate_media_token(rel_path: str) -> str:
    """Create a deterministic signed token for a relative path under User-photos.
    Token format: base64url(rel) + '.' + base64url(HMAC_SHA256(secret, rel))
    No expiry (stable), access still requires auth + ownership in the serving route.
    """
    rel = normalize_to_static_user_photos(rel_path)
    secret = (current_app.config.get('SECRET_KEY') or '').encode('utf-8')
    mac = hmac.new(secret, rel.encode('utf-8'), hashlib.sha256).digest()
    return f"{_b64url_encode(rel.encode('utf-8'))}.{_b64url_encode(mac)}"


def build_media_url(rel_path: str) -> str:
    token = generate_media_token(rel_path)
    return url_for('media_routes.media_get', token=token)


def resolve_media_token(token: str) -> str | None:
    """Verify a media token and return normalized relative path, or None if invalid."""
    try:
        b64_rel, b64_mac = token.split('.', 1)
        rel = _b64url_decode(b64_rel).decode('utf-8')
        rel = normalize_to_static_user_photos(rel)
        secret = (current_app.config.get('SECRET_KEY') or '').encode('utf-8')
        expected = hmac.new(secret, rel.encode('utf-8'), hashlib.sha256).digest()
        provided = _b64url_decode(b64_mac)
        if not hmac.compare_digest(expected, provided):
            return None
        return rel
    except Exception:
        return None


# -------- Expiring public share tokens --------

def generate_share_token(rel_path: str, expires_at_epoch: int) -> str:
    """Create an expiring token that can be used publicly without a session.
    Format: base64url(rel) + "." + str(exp) + "." + base64url(HMAC(secret, rel|exp|public))
    """
    rel = normalize_to_static_user_photos(rel_path)
    secret = (current_app.config.get('SECRET_KEY') or '').encode('utf-8')
    msg = f"{rel}|{int(expires_at_epoch)}|public".encode('utf-8')
    mac = hmac.new(secret, msg, hashlib.sha256).digest()
    return f"{_b64url_encode(rel.encode('utf-8'))}.{int(expires_at_epoch)}.{_b64url_encode(mac)}"


def build_share_url(rel_path: str, expires_at_epoch: int) -> str:
    token = generate_share_token(rel_path, expires_at_epoch)
    return url_for('media_routes.media_get', token=token, _external=True)


def resolve_share_token(token: str) -> tuple[str, int] | None:
    """Verify a share token and return (rel, exp) or None if invalid."""
    try:
        b64_rel, exp_s, b64_mac = token.split('.', 2)
        rel = _b64url_decode(b64_rel).decode('utf-8')
        rel = normalize_to_static_user_photos(rel)
        exp = int(exp_s)
        secret = (current_app.config.get('SECRET_KEY') or '').encode('utf-8')
        msg = f"{rel}|{exp}|public".encode('utf-8')
        expected = hmac.new(secret, msg, hashlib.sha256).digest()
        provided = _b64url_decode(b64_mac)
        if not hmac.compare_digest(expected, provided):
            return None
        return rel, exp
    except Exception:
        return None


