#!/usr/bin/env python3
"""PDF Report Generator — Manage report generation and scheduling.

Generates structured markdown reports, manages recurring report schedules,
and maintains a brand-level report library. Reports are saved as markdown
ready for PDF conversion via external tools or MCP servers.

Dependencies: none (stdlib only)

Storage: ~/.claude-marketing/brands/{slug}/reports/
         ~/.claude-marketing/brands/{slug}/reports/generated/
         ~/.claude-marketing/brands/{slug}/reports/schedules/

Usage:
    python pdf-generator.py --brand acme --action generate-report --report-type executive-summary --title "Q1 Executive Summary" --data '{"overview": "...", "highlights": ["..."]}'
    python pdf-generator.py --brand acme --action schedule-report --report-type monthly-review --frequency monthly --title "Monthly Review"
    python pdf-generator.py --brand acme --action list-schedules
    python pdf-generator.py --brand acme --action cancel-schedule --schedule-id sched-monthly-review-20260213
    python pdf-generator.py --brand acme --action list-reports --report-type campaign-report --limit 10
    python pdf-generator.py --brand acme --action get-report --report-id rpt-executive-summary-20260213-143022
    python pdf-generator.py --brand acme --action brand-theme --set '{"primary_color": "#1a73e8", "font": "Inter"}'
    python pdf-generator.py --brand acme --action export-markdown --report-id rpt-executive-summary-20260213-143022
"""

import argparse
import json
import sys
from datetime import datetime
from pathlib import Path

BRANDS_DIR = Path.home() / ".claude-marketing" / "brands"

VALID_REPORT_TYPES = [
    "executive-summary", "campaign-report", "channel-report",
    "competitor-report", "monthly-review",
]
VALID_FREQUENCIES = ["daily", "weekly", "monthly", "quarterly"]
VALID_CHANNELS = ["email", "slack", "drive"]

DEFAULT_THEME = {
    "primary_color": "#1a73e8",
    "secondary_color": "#34a853",
    "font": "Inter",
    "logo_path": "",
    "header_text": "",
    "footer_text": "Generated by Digital Marketing Pro",
}


def get_brand_dir(slug):
    """Get and validate brand directory."""
    brand_dir = BRANDS_DIR / slug
    if not brand_dir.exists():
        return None, f"Brand '{slug}' not found. Run /dm:brand-setup first."
    return brand_dir, None


def _load_json(path, default=None):
    if default is None:
        default = []
    if not path.exists():
        return default
    try:
        return json.loads(path.read_text(encoding="utf-8"))
    except (json.JSONDecodeError, OSError):
        return default


def _save_json(path, data):
    path.parent.mkdir(parents=True, exist_ok=True)
    path.write_text(json.dumps(data, indent=2), encoding="utf-8")


def _build_markdown(report_type, title, data, theme):
    """Build a structured markdown report from sections data."""
    lines = []
    if theme.get("header_text"):
        lines.append(f"*{theme['header_text']}*\n")
    lines.append(f"# {title}\n")
    lines.append(f"**Report Type:** {report_type}  ")
    lines.append(f"**Generated:** {datetime.now().strftime('%B %d, %Y at %H:%M')}\n")
    lines.append("---\n")

    # Standard sections by report type
    section_map = {
        "executive-summary": ["overview", "highlights", "kpis", "recommendations", "next_steps"],
        "campaign-report": ["summary", "performance", "channels", "creative", "budget", "learnings"],
        "channel-report": ["channel_overview", "metrics", "trends", "benchmarks", "optimizations"],
        "competitor-report": ["landscape", "competitors", "swot", "opportunities", "threats"],
        "monthly-review": ["summary", "performance", "wins", "challenges", "goals", "action_items"],
    }
    sections = section_map.get(report_type, list(data.keys()))
    included = []

    for section in sections:
        if section not in data:
            continue
        included.append(section)
        heading = section.replace("_", " ").title()
        lines.append(f"## {heading}\n")
        value = data[section]
        if isinstance(value, list):
            for item in value:
                if isinstance(item, dict):
                    for k, v in item.items():
                        lines.append(f"- **{k}:** {v}")
                else:
                    lines.append(f"- {item}")
            lines.append("")
        elif isinstance(value, dict):
            for k, v in value.items():
                lines.append(f"- **{k}:** {v}")
            lines.append("")
        else:
            lines.append(f"{value}\n")

    # Include any extra keys not in the standard sections
    for key in data:
        if key not in sections:
            included.append(key)
            heading = key.replace("_", " ").title()
            lines.append(f"## {heading}\n")
            value = data[key]
            if isinstance(value, list):
                for item in value:
                    lines.append(f"- {item}")
                lines.append("")
            elif isinstance(value, dict):
                for k, v in value.items():
                    lines.append(f"- **{k}:** {v}")
                lines.append("")
            else:
                lines.append(f"{value}\n")

    lines.append("---\n")
    if theme.get("footer_text"):
        lines.append(f"*{theme['footer_text']}*\n")

    return "\n".join(lines), included


# ---------------------------------------------------------------------------
# Actions
# ---------------------------------------------------------------------------

def generate_report(slug, report_type, title, data):
    brand_dir, err = get_brand_dir(slug)
    if err:
        return {"error": err}

    gen_dir = brand_dir / "reports" / "generated"
    gen_dir.mkdir(parents=True, exist_ok=True)

    theme = _load_json(brand_dir / "reports" / "theme.json", DEFAULT_THEME.copy())
    if not isinstance(theme, dict):
        theme = DEFAULT_THEME.copy()

    timestamp = datetime.now().strftime("%Y%m%d-%H%M%S")
    report_id = f"rpt-{report_type}-{timestamp}"
    markdown, included = _build_markdown(report_type, title, data, theme)

    # Save markdown
    md_path = gen_dir / f"{report_id}.md"
    md_path.write_text(markdown, encoding="utf-8")

    # Save metadata
    meta = {
        "report_id": report_id,
        "report_type": report_type,
        "title": title,
        "sections_included": included,
        "generated_at": datetime.now().isoformat(),
        "path": str(md_path),
    }
    _save_json(gen_dir / f"{report_id}.json", meta)

    return meta


def schedule_report(slug, report_type, frequency, title, recipients, channel):
    brand_dir, err = get_brand_dir(slug)
    if err:
        return {"error": err}

    sched_dir = brand_dir / "reports" / "schedules"
    sched_dir.mkdir(parents=True, exist_ok=True)

    timestamp = datetime.now().strftime("%Y%m%d-%H%M%S")
    schedule_id = f"sched-{report_type}-{timestamp}"

    schedule = {
        "schedule_id": schedule_id,
        "report_type": report_type,
        "frequency": frequency,
        "title": title,
        "recipients": [r.strip() for r in recipients.split(",")] if recipients else [],
        "channel": channel,
        "active": True,
        "created_at": datetime.now().isoformat(),
    }
    _save_json(sched_dir / f"{schedule_id}.json", schedule)

    return schedule


def list_schedules(slug):
    brand_dir, err = get_brand_dir(slug)
    if err:
        return {"error": err}

    sched_dir = brand_dir / "reports" / "schedules"
    if not sched_dir.exists():
        return {"schedules": [], "total": 0}

    schedules = []
    for fp in sorted(sched_dir.glob("sched-*.json")):
        entry = _load_json(fp, None)
        if isinstance(entry, dict) and entry.get("active", False):
            schedules.append(entry)

    return {"schedules": schedules, "total": len(schedules)}


def cancel_schedule(slug, schedule_id):
    brand_dir, err = get_brand_dir(slug)
    if err:
        return {"error": err}

    sched_dir = brand_dir / "reports" / "schedules"
    fp = sched_dir / f"{schedule_id}.json"
    if not fp.exists():
        return {"error": f"Schedule '{schedule_id}' not found."}

    schedule = _load_json(fp, {})
    schedule["active"] = False
    schedule["cancelled_at"] = datetime.now().isoformat()
    _save_json(fp, schedule)

    return {"status": "cancelled", "schedule_id": schedule_id}


def list_reports(slug, report_type=None, limit=20):
    brand_dir, err = get_brand_dir(slug)
    if err:
        return {"error": err}

    gen_dir = brand_dir / "reports" / "generated"
    if not gen_dir.exists():
        return {"reports": [], "total": 0}

    reports = []
    for fp in sorted(gen_dir.glob("rpt-*.json"), reverse=True):
        meta = _load_json(fp, None)
        if not isinstance(meta, dict):
            continue
        if report_type and meta.get("report_type") != report_type:
            continue
        reports.append(meta)

    total = len(reports)
    reports = reports[:limit]
    return {"reports": reports, "returned": len(reports), "total": total}


def get_report(slug, report_id):
    brand_dir, err = get_brand_dir(slug)
    if err:
        return {"error": err}

    gen_dir = brand_dir / "reports" / "generated"
    meta_path = gen_dir / f"{report_id}.json"
    md_path = gen_dir / f"{report_id}.md"

    if not meta_path.exists():
        return {"error": f"Report '{report_id}' not found."}

    meta = _load_json(meta_path, {})
    content = md_path.read_text(encoding="utf-8") if md_path.exists() else ""
    meta["content"] = content
    return meta


def brand_theme(slug, set_data=None):
    brand_dir, err = get_brand_dir(slug)
    if err:
        return {"error": err}

    reports_dir = brand_dir / "reports"
    reports_dir.mkdir(parents=True, exist_ok=True)
    theme_path = reports_dir / "theme.json"

    current = _load_json(theme_path, DEFAULT_THEME.copy())
    if not isinstance(current, dict):
        current = DEFAULT_THEME.copy()

    if set_data:
        allowed_keys = set(DEFAULT_THEME.keys())
        for k, v in set_data.items():
            if k in allowed_keys:
                current[k] = v
        _save_json(theme_path, current)

    return {"theme": current}


def export_markdown(slug, report_id):
    """Returns raw markdown to stdout (not JSON)."""
    brand_dir, err = get_brand_dir(slug)
    if err:
        print(json.dumps({"error": err}))
        return False

    md_path = brand_dir / "reports" / "generated" / f"{report_id}.md"
    if not md_path.exists():
        print(json.dumps({"error": f"Report '{report_id}' not found."}))
        return False

    sys.stdout.write(md_path.read_text(encoding="utf-8"))
    return True


# ---------------------------------------------------------------------------
# CLI
# ---------------------------------------------------------------------------

def main():
    parser = argparse.ArgumentParser(
        description="PDF Report Generator — manage report generation and scheduling"
    )
    parser.add_argument("--brand", required=True, help="Brand slug")
    parser.add_argument("--action", required=True,
                        choices=["generate-report", "schedule-report", "list-schedules",
                                 "cancel-schedule", "list-reports", "get-report",
                                 "brand-theme", "export-markdown"],
                        help="Action to perform")
    parser.add_argument("--report-type", choices=VALID_REPORT_TYPES,
                        help="Type of report")
    parser.add_argument("--title", help="Report title")
    parser.add_argument("--data", help="JSON data with report sections")
    parser.add_argument("--frequency", choices=VALID_FREQUENCIES,
                        help="Schedule frequency")
    parser.add_argument("--recipients", help="Comma-separated recipient list")
    parser.add_argument("--channel", choices=VALID_CHANNELS, default="email",
                        help="Delivery channel (default: email)")
    parser.add_argument("--schedule-id", help="Schedule ID (for cancel)")
    parser.add_argument("--report-id", help="Report ID (for get/export)")
    parser.add_argument("--limit", type=int, default=20,
                        help="Max items to return (default: 20)")
    parser.add_argument("--set", dest="set_theme",
                        help="JSON theme data to set")
    args = parser.parse_args()

    result = None

    if args.action == "generate-report":
        if not args.report_type:
            print(json.dumps({"error": "Provide --report-type"}))
            sys.exit(1)
        if not args.title:
            print(json.dumps({"error": "Provide --title"}))
            sys.exit(1)
        if not args.data:
            print(json.dumps({"error": "Provide --data with report sections JSON"}))
            sys.exit(1)
        try:
            data = json.loads(args.data)
        except json.JSONDecodeError:
            print(json.dumps({"error": "Invalid JSON in --data"}))
            sys.exit(1)
        result = generate_report(args.brand, args.report_type, args.title, data)

    elif args.action == "schedule-report":
        if not args.report_type:
            print(json.dumps({"error": "Provide --report-type"}))
            sys.exit(1)
        if not args.frequency:
            print(json.dumps({"error": "Provide --frequency"}))
            sys.exit(1)
        if not args.title:
            print(json.dumps({"error": "Provide --title"}))
            sys.exit(1)
        result = schedule_report(args.brand, args.report_type, args.frequency,
                                 args.title, args.recipients, args.channel)

    elif args.action == "list-schedules":
        result = list_schedules(args.brand)

    elif args.action == "cancel-schedule":
        if not args.schedule_id:
            print(json.dumps({"error": "Provide --schedule-id"}))
            sys.exit(1)
        result = cancel_schedule(args.brand, args.schedule_id)

    elif args.action == "list-reports":
        result = list_reports(args.brand, args.report_type, args.limit)

    elif args.action == "get-report":
        if not args.report_id:
            print(json.dumps({"error": "Provide --report-id"}))
            sys.exit(1)
        result = get_report(args.brand, args.report_id)

    elif args.action == "brand-theme":
        set_data = None
        if args.set_theme:
            try:
                set_data = json.loads(args.set_theme)
            except json.JSONDecodeError:
                print(json.dumps({"error": "Invalid JSON in --set"}))
                sys.exit(1)
        result = brand_theme(args.brand, set_data)

    elif args.action == "export-markdown":
        if not args.report_id:
            print(json.dumps({"error": "Provide --report-id"}))
            sys.exit(1)
        export_markdown(args.brand, args.report_id)
        sys.exit(0)

    if result is not None:
        json.dump(result, sys.stdout, indent=2)
        print()


if __name__ == "__main__":
    main()
