# SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Fullbleed-Commercial
"""Project scaffolding commands for fullbleed.

Provides commands for initializing new projects and creating templates.
"""
import json
import os
import shutil
import sys
from pathlib import Path
from datetime import datetime


# Builtin bootstrap defaults for `fullbleed init`.
DEFAULT_BOOTSTRAP_REF = "bootstrap"
DEFAULT_BOOTSTRAP_VERSION = "5.0.0"
DEFAULT_BOOTSTRAP_FILENAME = "bootstrap.min.css"
DEFAULT_BOOTSTRAP_REL_PATH = "vendor/css/bootstrap.min.css"
DEFAULT_BOOTSTRAP_LICENSE_FILE = "vendor/css/LICENSE.bootstrap.txt"
DEFAULT_BOOTSTRAP_LICENSE_URL = "https://raw.githubusercontent.com/twbs/bootstrap/v5.0.0/LICENSE"
DEFAULT_INIT_FONT_NAME = "inter"
DEFAULT_INIT_FONT_VERSION = "4.0"
DEFAULT_INIT_FONT_SOURCE_REL_PATH = "fonts/Inter-Variable.ttf"
DEFAULT_INIT_FONT_REL_PATH = "vendor/fonts/Inter-Variable.ttf"
DEFAULT_INIT_FONT_LICENSE = "OFL-1.1"
DEFAULT_INIT_FONT_LICENSE_FILE = "vendor/fonts/LICENSE.inter.txt"
DEFAULT_INIT_FONT_LICENSE_URL = "https://raw.githubusercontent.com/google/fonts/main/ofl/inter/OFL.txt"
DEFAULT_INIT_ICON_NAME = "bootstrap-icons"
DEFAULT_INIT_ICON_VERSION = "1.11.3"
DEFAULT_INIT_ICON_SOURCE_REL_PATH = "icons/bootstrap-icons.svg"
DEFAULT_INIT_ICON_REL_PATH = "vendor/icons/bootstrap-icons.svg"
DEFAULT_INIT_ICON_LICENSE = "MIT"
DEFAULT_INIT_ICON_LICENSE_FILE = "vendor/icons/LICENSE.bootstrap-icons.txt"
DEFAULT_INIT_ICON_LICENSE_URL = "https://raw.githubusercontent.com/twbs/icons/v1.11.3/LICENSE"


def _build_default_init_files(
    bootstrap_sha256: str,
    init_font_sha256: str,
    init_icon_sha256: str,
):
    """Return default init files with locked builtin asset metadata."""
    assets_lock = {
        "schema": 1,
        "packages": [
            {
                "name": "bootstrap",
                "version": DEFAULT_BOOTSTRAP_VERSION,
                "kind": "css",
                "files": [
                    {
                        "path": DEFAULT_BOOTSTRAP_REL_PATH,
                        "sha256": bootstrap_sha256,
                    }
                ],
            },
            {
                "name": DEFAULT_INIT_FONT_NAME,
                "version": DEFAULT_INIT_FONT_VERSION,
                "kind": "font",
                "files": [
                    {
                        "path": DEFAULT_INIT_FONT_REL_PATH,
                        "sha256": init_font_sha256,
                    }
                ],
            },
            {
                "name": DEFAULT_INIT_ICON_NAME,
                "version": DEFAULT_INIT_ICON_VERSION,
                "kind": "svg",
                "files": [
                    {
                        "path": DEFAULT_INIT_ICON_REL_PATH,
                        "sha256": init_icon_sha256,
                    }
                ],
            },
        ],
    }
    return {
        "report.py": '''import fullbleed
from pathlib import Path

def create_engine():
    # 1. Create Asset Bundle
    bundle = fullbleed.AssetBundle()
    
    # --- Vendored Assets ---
    bundle.add_file("vendor/css/bootstrap.min.css", "css", name="bootstrap")
    bundle.add_file("vendor/fonts/Inter-Variable.ttf", "font")
    bundle.add_file("vendor/icons/bootstrap-icons.svg", "svg", name="bootstrap-icons")
    
    # 2. Configure Engine
    engine = fullbleed.PdfEngine(
        page_width="8.5in",
        page_height="11in",
        margin="0.5in",
    )
    
    # 3. Register Bundle
    engine.register_bundle(bundle)
    return engine

TEST_HTML = """<!DOCTYPE html>
<html>
<body>
    <h1>Hello, Fullbleed!</h1>
    <p>This is a PDF generated from HTML.</p>
</body>
</html>
"""

TEST_CSS = """
@page { margin: 1in; }
body { font-family: "Inter", sans-serif; font-weight: 300; }
h1 { color: #333; }
"""

def main():
    engine = create_engine()
    output_path = "output/report.pdf"
    
    # Ensure output directory exists
    Path("output").mkdir(exist_ok=True)
    
    # Render PDF
    bytes_written = engine.render_pdf_to_file(TEST_HTML, TEST_CSS, output_path)
    print(f"Generated {output_path} ({bytes_written} bytes)")

if __name__ == "__main__":
    main()
''',
        "assets.lock.json": json.dumps(assets_lock, ensure_ascii=True, indent=2) + "\n",
        "COMPLIANCE.md": '''# Compliance Notes

Generated by `fullbleed init`.

## Project License Context

- This project is using the `fullbleed` engine.
- Upstream engine/package licensing: `AGPL-3.0-only OR LicenseRef-Fullbleed-Commercial`.
- Canonical engine licensing references:
  - `LICENSING.md`
  - `LICENSE`
  - `COPYRIGHT`
  - `THIRD_PARTY_LICENSES.md`

## Operational Guidance

- If using AGPL mode, review AGPL obligations with counsel.
- Keep third-party asset licenses and notices with deployed artifacts.
- Re-run `fullbleed compliance --json` during release review.

## Commercial/License Contact

- `info@fullbleed.dev`
- `fullbleed.dev`
''',
        ".gitignore": '''# Fullbleed output
output/
*.pdf

# Python
__pycache__/
*.pyc
.venv/

# IDE
.vscode/
.idea/
''',
    }


def _builtin_seed_info(asset_ref: str, source_rel_path: str, label: str):
    """Resolve source file + metadata for a builtin asset."""
    import fullbleed_assets
    from .assets import _compute_file_hash, _resolve_builtin_asset

    package = _resolve_builtin_asset(asset_ref)
    if not package:
        raise RuntimeError(f"builtin {label} package metadata is missing")

    source_path = Path(fullbleed_assets.asset_path(source_rel_path))
    if not source_path.exists():
        raise RuntimeError(f"builtin {label} asset not found: {source_path}")

    sha256 = _compute_file_hash(source_path)
    return package, source_path, sha256


def _bootstrap_seed_info():
    """Resolve bootstrap source file + metadata for scaffold init."""
    return _builtin_seed_info(
        asset_ref=DEFAULT_BOOTSTRAP_REF,
        source_rel_path=DEFAULT_BOOTSTRAP_FILENAME,
        label="bootstrap",
    )


def _init_font_seed_info():
    """Resolve the default init font source file and hash."""
    import fullbleed_assets
    from .assets import _compute_file_hash

    source_path = Path(fullbleed_assets.asset_path(DEFAULT_INIT_FONT_SOURCE_REL_PATH))
    if not source_path.exists():
        raise RuntimeError(f"init font asset not found: {source_path}")

    return source_path, _compute_file_hash(source_path)


def _init_icon_seed_info():
    """Resolve the default init icon bundle source file and hash."""
    import fullbleed_assets
    from .assets import _compute_file_hash

    source_path = Path(fullbleed_assets.asset_path(DEFAULT_INIT_ICON_SOURCE_REL_PATH))
    if not source_path.exists():
        raise RuntimeError(f"init icon asset not found: {source_path}")

    return source_path, _compute_file_hash(source_path)


def _emit_init_asset_error(exc, is_json):
    """Emit a consistent init error when builtin assets cannot be provisioned."""
    message = f"Failed to provision init vendored assets: {exc}"
    if is_json:
        result = {
            "schema": "fullbleed.error.v1",
            "ok": False,
            "code": "INIT_ASSET_UNAVAILABLE",
            "message": message,
        }
        sys.stdout.write(json.dumps(result, ensure_ascii=True) + "\n")
    else:
        sys.stderr.write(f"[error] {message}\n")


DEFAULT_INIT_DIRS = ["templates", "output", "vendor"]
DEFAULT_VENDOR_CSS_DIR = "vendor/css"
DEFAULT_VENDOR_FONT_DIR = "vendor/fonts"
DEFAULT_VENDOR_ICON_DIR = "vendor/icons"

DEFAULT_DIRS = DEFAULT_INIT_DIRS


# Sample templates
TEMPLATES = {
    "invoice": {
        "description": "Basic invoice template",
        "files": {
            "templates/invoice.html": '''<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="utf-8">
    <title>Invoice</title>
</head>
<body>
    <header class="invoice-header">
        <div class="company-info">
            <h1>{{ company_name }}</h1>
            <p>{{ company_address }}</p>
        </div>
        <div class="invoice-info">
            <h2>INVOICE</h2>
            <p>Invoice #: {{ invoice_number }}</p>
            <p>Date: {{ invoice_date }}</p>
            <p>Due: {{ due_date }}</p>
        </div>
    </header>
    
    <section class="billing">
        <div class="bill-to">
            <strong>Bill To:</strong>
            <p>{{ customer_name }}</p>
            <p>{{ customer_address }}</p>
        </div>
    </section>
    
    <table class="line-items">
        <thead>
            <tr>
                <th>Description</th>
                <th>Qty</th>
                <th>Unit Price</th>
                <th>Amount</th>
            </tr>
        </thead>
        <tbody>
            {% for item in items %}
            <tr>
                <td>{{ item.description }}</td>
                <td>{{ item.quantity }}</td>
                <td>${{ item.unit_price }}</td>
                <td>${{ item.amount }}</td>
            </tr>
            {% endfor %}
        </tbody>
        <tfoot>
            <tr>
                <td colspan="3" class="total-label">Total:</td>
                <td class="total-amount">${{ total }}</td>
            </tr>
        </tfoot>
    </table>
    
    <footer>
        <p>Thank you for your business!</p>
    </footer>
</body>
</html>
''',
            "templates/invoice.css": '''/* Invoice Template Styles */
@page {
    size: letter;
    margin: 0.75in;
}

body {
    font-family: system-ui, -apple-system, "Segoe UI", Roboto, sans-serif;
    font-size: 11pt;
    line-height: 1.4;
    color: #333;
}

.invoice-header {
    display: flex;
    justify-content: space-between;
    margin-bottom: 2rem;
    padding-bottom: 1rem;
    border-bottom: 2px solid #2563eb;
}

.company-info h1 {
    margin: 0;
    font-size: 1.5rem;
    color: #1e40af;
}

.invoice-info {
    text-align: right;
}

.invoice-info h2 {
    margin: 0 0 0.5rem;
    font-size: 1.25rem;
    color: #1e40af;
}

.billing {
    margin-bottom: 2rem;
}

.line-items {
    width: 100%;
    border-collapse: collapse;
    margin-bottom: 2rem;
}

.line-items th,
.line-items td {
    padding: 0.75rem;
    text-align: left;
    border-bottom: 1px solid #e5e7eb;
}

.line-items th {
    background: #f3f4f6;
    font-weight: 600;
}

.line-items th:last-child,
.line-items td:last-child {
    text-align: right;
}

.total-label {
    text-align: right;
    font-weight: 600;
}

.total-amount {
    font-weight: 600;
    font-size: 1.1em;
    color: #1e40af;
}

footer {
    margin-top: 3rem;
    padding-top: 1rem;
    border-top: 1px solid #e5e7eb;
    text-align: center;
    font-size: 0.9em;
    color: #6b7280;
}
''',
        },
    },
    "statement": {
        "description": "Bank/account statement template",
        "files": {
            "templates/statement.html": '''<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="utf-8">
    <title>Account Statement</title>
</head>
<body>
    <header>
        <h1>{{ institution_name }}</h1>
        <p>Account Statement</p>
    </header>
    
    <section class="account-info">
        <p><strong>Account Holder:</strong> {{ account_holder }}</p>
        <p><strong>Account Number:</strong> {{ account_number }}</p>
        <p><strong>Statement Period:</strong> {{ start_date }} - {{ end_date }}</p>
    </section>
    
    <section class="summary">
        <div class="balance-box">
            <span class="label">Opening Balance</span>
            <span class="amount">${{ opening_balance }}</span>
        </div>
        <div class="balance-box">
            <span class="label">Closing Balance</span>
            <span class="amount">${{ closing_balance }}</span>
        </div>
    </section>
    
    <table class="transactions">
        <thead>
            <tr>
                <th>Date</th>
                <th>Description</th>
                <th>Debit</th>
                <th>Credit</th>
                <th>Balance</th>
            </tr>
        </thead>
        <tbody>
            {% for tx in transactions %}
            <tr>
                <td>{{ tx.date }}</td>
                <td>{{ tx.description }}</td>
                <td>{% if tx.debit %}${{ tx.debit }}{% endif %}</td>
                <td>{% if tx.credit %}${{ tx.credit }}{% endif %}</td>
                <td>${{ tx.balance }}</td>
            </tr>
            {% endfor %}
        </tbody>
    </table>
</body>
</html>
''',
            "templates/statement.css": '''/* Statement Template Styles */
@page {
    size: letter;
    margin: 0.5in;
}

body {
    font-family: "Segoe UI", Roboto, sans-serif;
    font-size: 10pt;
    line-height: 1.3;
}

header {
    text-align: center;
    margin-bottom: 1.5rem;
    padding-bottom: 1rem;
    border-bottom: 2px solid #1f2937;
}

header h1 {
    margin: 0;
    font-size: 1.5rem;
}

.account-info {
    margin-bottom: 1.5rem;
    padding: 1rem;
    background: #f9fafb;
    border-radius: 4px;
}

.account-info p {
    margin: 0.25rem 0;
}

.summary {
    display: flex;
    gap: 2rem;
    margin-bottom: 1.5rem;
}

.balance-box {
    flex: 1;
    padding: 1rem;
    background: #eff6ff;
    border-radius: 4px;
    text-align: center;
}

.balance-box .label {
    display: block;
    font-size: 0.85em;
    color: #6b7280;
}

.balance-box .amount {
    display: block;
    font-size: 1.25em;
    font-weight: 600;
    color: #1e40af;
}

.transactions {
    width: 100%;
    border-collapse: collapse;
    font-size: 0.9em;
}

.transactions th,
.transactions td {
    padding: 0.5rem;
    border-bottom: 1px solid #e5e7eb;
}

.transactions th {
    background: #1f2937;
    color: white;
    text-align: left;
    font-weight: 500;
}

.transactions td:nth-child(3),
.transactions td:nth-child(4),
.transactions td:nth-child(5) {
    text-align: right;
    font-family: "Consolas", monospace;
}
''',
        },
    },
}


def cmd_init(args):
    """Initialize a new fullbleed project in the current directory."""
    target_dir = Path(args.path) if hasattr(args, "path") and args.path else Path.cwd()
    force = getattr(args, "force", False)
    is_json = getattr(args, "json", False)
    
    # Check if already initialized
    report_path = target_dir / "report.py"
    if report_path.exists() and not force:
        if is_json:
            result = {
                "schema": "fullbleed.error.v1",
                "ok": False,
                "code": "ALREADY_INITIALIZED",
                "message": f"Directory already contains report.py. Use --force to overwrite.",
            }
            sys.stdout.write(json.dumps(result, ensure_ascii=True) + "\n")
        else:
            sys.stderr.write(f"[error] Directory already contains report.py. Use --force to overwrite.\n")
        raise SystemExit(1)

    try:
        from .assets import _license_filename, _write_license_notice
        bootstrap, bootstrap_source_path, bootstrap_sha256 = _bootstrap_seed_info()
        init_font_source_path, init_font_sha256 = _init_font_seed_info()
        init_icon_source_path, init_icon_sha256 = _init_icon_seed_info()
    except Exception as exc:
        _emit_init_asset_error(exc, is_json=is_json)
        raise SystemExit(1)
    init_files = _build_default_init_files(
        bootstrap_sha256=bootstrap_sha256,
        init_font_sha256=init_font_sha256,
        init_icon_sha256=init_icon_sha256,
    )
    
    # Create directories
    created_dirs = []
    for dirname in DEFAULT_DIRS:
        dir_path = target_dir / dirname
        if not dir_path.exists():
            dir_path.mkdir(parents=True, exist_ok=True)
            created_dirs.append(dirname)

    vendor_css_dir = target_dir / DEFAULT_VENDOR_CSS_DIR
    if not vendor_css_dir.exists():
        vendor_css_dir.mkdir(parents=True, exist_ok=True)
        created_dirs.append(DEFAULT_VENDOR_CSS_DIR)
    vendor_font_dir = target_dir / DEFAULT_VENDOR_FONT_DIR
    if not vendor_font_dir.exists():
        vendor_font_dir.mkdir(parents=True, exist_ok=True)
        created_dirs.append(DEFAULT_VENDOR_FONT_DIR)
    vendor_icon_dir = target_dir / DEFAULT_VENDOR_ICON_DIR
    if not vendor_icon_dir.exists():
        vendor_icon_dir.mkdir(parents=True, exist_ok=True)
        created_dirs.append(DEFAULT_VENDOR_ICON_DIR)
    
    # Create files
    created_files = []
    for filename, content in init_files.items():
        file_path = target_dir / filename
        if not file_path.exists() or force:
            file_path.write_text(content, encoding="utf-8")
            created_files.append(filename)

    try:
        bootstrap_dest_path = target_dir / DEFAULT_BOOTSTRAP_REL_PATH
        if not bootstrap_dest_path.exists() or force:
            shutil.copy2(bootstrap_source_path, bootstrap_dest_path)
            created_files.append(DEFAULT_BOOTSTRAP_REL_PATH)

        bootstrap_license_name = _license_filename(bootstrap["name"])
        bootstrap_license_path = vendor_css_dir / bootstrap_license_name
        if not bootstrap_license_path.exists() or force:
            written_license = _write_license_notice(
                dest_dir=vendor_css_dir,
                package_name=bootstrap["name"],
                version=bootstrap["version"],
                license_name=bootstrap.get("license", "MIT"),
                license_url=bootstrap.get("license_url", DEFAULT_BOOTSTRAP_LICENSE_URL),
            )
            if written_license:
                created_files.append(DEFAULT_BOOTSTRAP_LICENSE_FILE)

        init_font_dest_path = target_dir / DEFAULT_INIT_FONT_REL_PATH
        if not init_font_dest_path.exists() or force:
            shutil.copy2(init_font_source_path, init_font_dest_path)
            created_files.append(DEFAULT_INIT_FONT_REL_PATH)

        init_font_license_name = _license_filename(DEFAULT_INIT_FONT_NAME)
        init_font_license_path = vendor_font_dir / init_font_license_name
        if not init_font_license_path.exists() or force:
            written_init_font_license = _write_license_notice(
                dest_dir=vendor_font_dir,
                package_name=DEFAULT_INIT_FONT_NAME,
                version=DEFAULT_INIT_FONT_VERSION,
                license_name=DEFAULT_INIT_FONT_LICENSE,
                license_url=DEFAULT_INIT_FONT_LICENSE_URL,
            )
            if written_init_font_license:
                created_files.append(DEFAULT_INIT_FONT_LICENSE_FILE)

        init_icon_dest_path = target_dir / DEFAULT_INIT_ICON_REL_PATH
        if not init_icon_dest_path.exists() or force:
            shutil.copy2(init_icon_source_path, init_icon_dest_path)
            created_files.append(DEFAULT_INIT_ICON_REL_PATH)

        init_icon_license_name = _license_filename(DEFAULT_INIT_ICON_NAME)
        init_icon_license_path = vendor_icon_dir / init_icon_license_name
        if not init_icon_license_path.exists() or force:
            written_init_icon_license = _write_license_notice(
                dest_dir=vendor_icon_dir,
                package_name=DEFAULT_INIT_ICON_NAME,
                version=DEFAULT_INIT_ICON_VERSION,
                license_name=DEFAULT_INIT_ICON_LICENSE,
                license_url=DEFAULT_INIT_ICON_LICENSE_URL,
            )
            if written_init_icon_license:
                created_files.append(DEFAULT_INIT_ICON_LICENSE_FILE)
    except Exception as exc:
        _emit_init_asset_error(exc, is_json=is_json)
        raise SystemExit(1)
    
    result = {
        "path": str(target_dir),
        "created_dirs": created_dirs,
        "created_files": created_files,
    }
    
    if is_json:
        output = {"schema": "fullbleed.init.v1", "ok": True, **result}
        sys.stdout.write(json.dumps(output, ensure_ascii=True) + "\n")
    else:
        sys.stdout.write(f"[ok] Initialized fullbleed project in {target_dir}\n")
        if created_dirs:
            sys.stdout.write(f"  Created directories: {', '.join(created_dirs)}\n")
        if created_files:
            sys.stdout.write(f"  Created files: {', '.join(created_files)}\n")
        sys.stdout.write("\n  Next steps:\n")
        sys.stdout.write("    1. Review COMPLIANCE.md\n")
        sys.stdout.write("    2. Edit report.py to customize your report\n")
        sys.stdout.write("    3. Run: python report.py\n")
        sys.stdout.write("    4. Or:  fullbleed run report:engine\n")


def cmd_new_template(args):
    """Create a new template from a starter template."""
    template_name = args.template
    target_dir = Path(args.path) if hasattr(args, "path") and args.path else Path.cwd()
    force = getattr(args, "force", False)
    
    if template_name not in TEMPLATES:
        available = ", ".join(TEMPLATES.keys())
        if getattr(args, "json", False):
            result = {
                "schema": "fullbleed.error.v1",
                "ok": False,
                "code": "UNKNOWN_TEMPLATE",
                "message": f"Unknown template: {template_name}. Available: {available}",
            }
            sys.stdout.write(json.dumps(result, ensure_ascii=True) + "\n")
        else:
            sys.stderr.write(f"[error] Unknown template: {template_name}\n")
            sys.stderr.write(f"  Available templates: {available}\n")
        raise SystemExit(1)
    
    template = TEMPLATES[template_name]
    created_files = []
    
    for filepath, content in template["files"].items():
        full_path = target_dir / filepath
        if full_path.exists() and not force:
            if getattr(args, "json", False):
                result = {
                    "schema": "fullbleed.error.v1",
                    "ok": False,
                    "code": "FILE_EXISTS",
                    "message": f"File already exists: {filepath}. Use --force to overwrite.",
                }
                sys.stdout.write(json.dumps(result, ensure_ascii=True) + "\n")
            else:
                sys.stderr.write(f"[error] File already exists: {filepath}. Use --force to overwrite.\n")
            raise SystemExit(1)
        
        full_path.parent.mkdir(parents=True, exist_ok=True)
        full_path.write_text(content, encoding="utf-8")
        created_files.append(filepath)
    
    result = {
        "template": template_name,
        "description": template["description"],
        "created_files": created_files,
    }
    
    if getattr(args, "json", False):
        output = {"schema": "fullbleed.new_template.v1", "ok": True, **result}
        sys.stdout.write(json.dumps(output, ensure_ascii=True) + "\n")
    else:
        sys.stdout.write(f"[ok] Created {template_name} template\n")
        for f in created_files:
            sys.stdout.write(f"  - {f}\n")
