#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
============================================
QR Platba Generator – Fully Documented Version
============================================

This script generates a valid Czech payment QR code (QR Platba)
according to the official ČBA (Czech Banking Association) specification v1.2.

The script creates a correct SPD (Short Payment Descriptor) string,
turns it into a QR code (PNG image), and saves it to disk.

✅ Works with all Czech banks (Fio, ČSOB, KB, Air Bank…)
✅ Supports both IBAN or domestic account format (account/bankcode)
✅ Automatically URL-encodes the message (so spaces and diacritics work)
✅ Can handle optional VS, SS, KS, due date, and instant-payment flag

Usage example (in your terminal):
---------------------------------
python generate_qrplatba.py \
    --acc CZ7520100000002800046620 \
    --amount 1543.50 \
    --vs 7309208104 \
    --ss 123456 \
    --ks 0308 \
    --date 09.12.2025 \
    --msg "ahoj zlatíčko" \
    --out qr_ok.png
"""

import argparse           # For parsing command-line arguments
import urllib.parse        # For safe URL-encoding of message text
from pathlib import Path   # For easy file handling (cross-platform)
import qrcode              # For generating the QR code image
from datetime import datetime  # For date parsing and formatting


# --------------------------------------------------------------------
# 🧩 Helper function: Normalize date into the required YYYYMMDD format
# --------------------------------------------------------------------
def normalize_date(date_str: str) -> str:
    """
    Converts a user-supplied date string (various formats) into the
    standard YYYYMMDD format used by the QR Platba specification.

    Acceptable input examples:
        "20251209"
        "09.12.2025"
        "2025-12-09"
        "09122025"

    Returns:
        str: normalized date string in "YYYYMMDD" format
        or empty string "" if input is None or empty
    """
    if not date_str:
        return ""
    s = date_str.strip()

    # Already in correct 8-digit form?
    if len(s) == 8 and s.isdigit():
        return s

    # Try several common formats
    for fmt in ("%d.%m.%Y", "%Y-%m-%d", "%d%m%Y"):
        try:
            dt = datetime.strptime(s, fmt)
            return dt.strftime("%Y%m%d")
        except ValueError:
            continue

    # If all parsing attempts fail → raise an informative error
    raise ValueError(
        "Date must be in one of formats: YYYYMMDD, DD.MM.YYYY, YYYY-MM-DD, DDMMYYYY"
    )


# --------------------------------------------------------------------
# 🔧 Helper function: Build the SPD string (core of QR Platba)
# --------------------------------------------------------------------
def build_spayd(params: dict) -> str:
    """
    Assembles a valid SPD (Short Payment Descriptor) string
    according to ČBA v1.2 specification.

    Example output:
    SPD*1.0*ACC:CZ7520100000002800046620*AM:1543.50*CC:CZK*DT:20251209*
    X-VS:7309208104*X-SS:123456*X-KS:0308*MSG:ahoj%20zlaticko
    """

    # Start with fixed header
    parts = ["SPD*1.0*"]

    # --- Required field: account number / IBAN ---
    acc = params.get("acc", "").strip()
    if not acc:
        raise ValueError("Missing required parameter: --acc")
    parts.append(f"ACC:{acc}*")

    # --- Optional: amount ---
    amt = params.get("amount")
    if amt:
        parts.append(f"AM:{float(amt):.2f}*")  # always two decimals

    # --- Optional: currency (defaults to CZK) ---
    cc = params.get("cc") or "CZK"
    parts.append(f"CC:{cc}*")

    # --- Optional: due date ---
    dt = params.get("dt")
    if dt:
        parts.append(f"DT:{dt}*")

    # --- Optional: symbols (VS, SS, KS) ---
    for key in ("X-VS", "X-SS", "X-KS"):
        val = params.get(key.replace("-", "_").lower())
        if val:
            parts.append(f"{key}:{val}*")

    # --- Optional: payment type (PT:IP = Instant Payment) ---
    pt = params.get("pt")
    if pt:
        parts.append(f"PT:{pt}*")

    # --- Optional: message for recipient ---
    msg = params.get("msg") or ""
    if msg:
        # Encode to keep spaces and Czech letters valid
        safe_chars = "$%*+-.:/"
        encoded_msg = urllib.parse.quote(msg, safe=safe_chars)
        parts.append(f"MSG:{encoded_msg}")

    # Combine everything into one string
    return "".join(parts)


# --------------------------------------------------------------------
# 🚀 Main program entry point
# --------------------------------------------------------------------
def main():
    # -------------------------------
    # Define all command-line options
    # -------------------------------
    parser = argparse.ArgumentParser(
        description="Generate a Czech QR Platba (payment QR code)."
    )
    parser.add_argument("--acc", required=True,
                        help='Account: either "2800046620/2010" or IBAN "CZ..."')
    parser.add_argument("--amount", help="Payment amount (e.g. 1543.50)")
    parser.add_argument("--vs", help="Variable symbol (X-VS)")
    parser.add_argument("--ss", help="Specific symbol (X-SS)")
    parser.add_argument("--ks", help="Constant symbol (X-KS)")
    parser.add_argument("--date", help="Due date (YYYYMMDD or DD.MM.YYYY etc.)")
    parser.add_argument("--msg", help="Message for recipient (will be URL-encoded)")
    parser.add_argument("--cc", default="CZK", help="Currency (default CZK)")
    parser.add_argument("--pt", help="Payment type (e.g. IP for instant payment)")
    parser.add_argument("--out", default="qrplatba.png", help="Output PNG file name")

    args = parser.parse_args()

    # -------------------------------
    # Normalize and prepare arguments
    # -------------------------------
    dt_norm = normalize_date(args.date) if args.date else ""

    params = {
        "acc": args.acc,
        "amount": args.amount,
        "cc": args.cc,
        "dt": dt_norm,
        "x_vs": args.vs,
        "x_ss": args.ss,
        "x_ks": args.ks,
        "msg": args.msg,
        "pt": args.pt,
    }

    # -------------------------------
    # Build the SPD string
    # -------------------------------
    spayd = build_spayd(params)

    # -------------------------------
    # Generate QR code image
    # -------------------------------
    img = qrcode.make(spayd)
    output_path = Path(args.out)
    img.save(output_path)

    # -------------------------------
    # Print results for verification
    # -------------------------------
    print("✅ QR Platba successfully generated")
    print("SPD string (you can verify at https://qr-platba.cz/test/):")
    print(spayd)
    print()
    print(f"📂 QR code image saved to: {output_path.resolve()}")


# --------------------------------------------------------------------
# 🔘 Run when executed directly
# --------------------------------------------------------------------
if __name__ == "__main__":
    main()
