#!/usr/bin/env python3
"""
Promo Code Generator for Swing Shift Golf Club.

Generates unique, branded promo codes and tracks them in a local CSV ledger.
Codes follow the format: PREFIX + DISCOUNT (e.g., SWING20, SHIFT15, BAY10).

Usage:
    python3 promo_code_generator.py --discount 20
    python3 promo_code_generator.py --discount 15 --prefix SHIFT
    python3 promo_code_generator.py --list           # show all generated codes
    python3 promo_code_generator.py --active          # show unexpired active codes

Output: JSON to stdout with the generated code details.
"""

from __future__ import annotations

import argparse
import csv
import json
import random
import string
import sys
from datetime import datetime, timedelta
from pathlib import Path
from typing import Optional, List, Dict

WORKSPACE = Path(__file__).resolve().parent.parent
LEDGER_PATH = WORKSPACE / "12_Output" / "promo_code_ledger.csv"

BRAND_PREFIXES = [
    "SWING", "SHIFT", "BAY", "TEE", "DRIVE", "PUTT", "BIRDIE", "EAGLE",
    "IRON", "WEDGE", "CHIP", "FORE", "GREEN", "LINKS", "FAIRWAY",
]

LEDGER_HEADERS = [
    "code", "discount_pct", "created_at", "expires_at", "status",
    "campaign", "redeemed_count", "notes",
]


def load_ledger() -> List[Dict]:
    """Load existing promo codes from the ledger CSV."""
    if not LEDGER_PATH.exists():
        return []
    rows = []
    with open(LEDGER_PATH, newline="", encoding="utf-8") as f:
        reader = csv.DictReader(f)
        for row in reader:
            rows.append(row)
    return rows


def save_ledger(rows: List[Dict]) -> None:
    """Write the full ledger back to CSV."""
    LEDGER_PATH.parent.mkdir(parents=True, exist_ok=True)
    with open(LEDGER_PATH, "w", newline="", encoding="utf-8") as f:
        writer = csv.DictWriter(f, fieldnames=LEDGER_HEADERS)
        writer.writeheader()
        writer.writerows(rows)


def generate_code(discount: int, prefix: Optional[str] = None, suffix_len: int = 0) -> str:
    """
    Generate a branded promo code.

    Format: PREFIX + DISCOUNT [+ random suffix if needed for uniqueness]
    Examples: SWING20, SHIFT15, BAY10, SWING20X3K
    """
    if prefix is None:
        prefix = random.choice(BRAND_PREFIXES)

    code = f"{prefix}{discount}"

    if suffix_len > 0:
        suffix = "".join(random.choices(string.ascii_uppercase + string.digits, k=suffix_len))
        code = f"{code}{suffix}"

    return code


def create_promo(
    discount: int,
    prefix: Optional[str] = None,
    campaign: str = "slow-day",
    valid_hours: int = 24,
    notes: str = "",
) -> dict:
    """Create a new promo code and add it to the ledger."""
    ledger = load_ledger()
    existing_codes = {row["code"] for row in ledger}

    # Generate a unique code, adding suffix if needed
    code = generate_code(discount, prefix)
    suffix_len = 0
    while code in existing_codes:
        suffix_len += 1
        code = generate_code(discount, prefix, suffix_len)
        if suffix_len > 5:
            # Fallback: fully random
            code = f"SS{discount}{''.join(random.choices(string.ascii_uppercase + string.digits, k=4))}"
            break

    now = datetime.now()
    expires = now + timedelta(hours=valid_hours)

    entry = {
        "code": code,
        "discount_pct": str(discount),
        "created_at": now.strftime("%Y-%m-%d %H:%M"),
        "expires_at": expires.strftime("%Y-%m-%d %H:%M"),
        "status": "active",
        "campaign": campaign,
        "redeemed_count": "0",
        "notes": notes,
    }

    ledger.append(entry)
    save_ledger(ledger)

    return entry


def list_codes(active_only: bool = False) -> List[Dict]:
    """List promo codes, optionally filtering to active/unexpired only."""
    ledger = load_ledger()
    if not active_only:
        return ledger

    now = datetime.now()
    active = []
    for row in ledger:
        if row.get("status") != "active":
            continue
        try:
            expires = datetime.strptime(row["expires_at"], "%Y-%m-%d %H:%M")
            if expires > now:
                active.append(row)
        except ValueError:
            active.append(row)
    return active


def main():
    parser = argparse.ArgumentParser(description="Generate branded promo codes for Swing Shift")
    parser.add_argument("--discount", type=int, help="Discount percentage (e.g., 20 for 20%% off)")
    parser.add_argument("--prefix", help="Code prefix (default: random brand word)")
    parser.add_argument("--campaign", default="slow-day", help="Campaign name (default: slow-day)")
    parser.add_argument("--valid-hours", type=int, default=24, help="Hours until expiry (default: 24)")
    parser.add_argument("--notes", default="", help="Optional notes")
    parser.add_argument("--list", action="store_true", help="List all promo codes")
    parser.add_argument("--active", action="store_true", help="List active (unexpired) codes only")
    parser.add_argument("--json", action="store_true", dest="json_output", help="JSON output")
    args = parser.parse_args()

    if args.list or args.active:
        codes = list_codes(active_only=args.active)
        print(json.dumps(codes, indent=2))
        return

    if not args.discount:
        parser.error("--discount is required when generating a code")

    entry = create_promo(
        discount=args.discount,
        prefix=args.prefix,
        campaign=args.campaign,
        valid_hours=args.valid_hours,
        notes=args.notes,
    )
    print(json.dumps(entry, indent=2))


if __name__ == "__main__":
    main()
