"""
Command-line interface for Cogniflow pipeline certification.

Provides commands for:
- Validating step packages
- Creating certification requests
- Signing certificates (certification authority)
- Verifying certificates
- Computing hashes
- Generating keypairs
"""

import argparse
import json
import os
import sys
from pathlib import Path

try:
    from cf_cli.registry import register_command
except ImportError:  # pragma: no cover - optional dependency
    register_command = None

from . import __version__, OFFICIAL_PUBLIC_KEY_B64
from .crypto import generate_keypair, compute_distribution_hash
from .validation import (
    load_steps_jsonld,
    select_plugin_artifact,
    sign_certificate_request,
    validate_step_package,
    verify_certificate,
)


def _set_plugin_certificate(plugin_node, cert):
    for key in (
        "cf:pluginCertificate",
        "https://cogniflow.org/ns#pluginCertificate",
        "http://cogniflow.org/ns#pluginCertificate",
    ):
        if key in plugin_node:
            plugin_node[key] = cert
            return
    plugin_node["cf:pluginCertificate"] = cert


def cmd_validate(args):
    """Validate a step package against the contract."""
    plugin_path = Path(args.path).resolve()
    
    if not plugin_path.exists():
        print(f"Error: Path does not exist: {plugin_path}", file=sys.stderr)
        return 1
    
    result = validate_step_package(plugin_path)
    
    print(f"Plugin: {result.plugin_name} v{result.plugin_version}")
    print(f"Valid: {result.valid}")
    
    if result.valid:
        print(f"Distribution Hash: {result.distribution_hash}")
        print(f"Operations: {len(result.operations)}")
        for op in result.operations:
            print(f"  - {op.name}: {len(op.inputs)} inputs, {len(op.outputs)} outputs")
    
    if result.errors:
        print("\nErrors:")
        for err in result.errors:
            print(f"  - {err}")
    
    if result.warnings:
        print("\nWarnings:")
        for warn in result.warnings:
            print(f"  - {warn}")
    
    return 0 if result.valid else 1


def cmd_request(args):
    """Create a certification request for signing."""
    plugin_path = Path(args.path).resolve()
    
    result = validate_step_package(plugin_path)
    if not result.valid:
        print("Error: Plugin validation failed", file=sys.stderr)
        for err in result.errors:
            print(f"  - {err}", file=sys.stderr)
        return 1
    
    # Create unsigned certificate (request)
    cert = result.to_certificate(sign_key_b64=None)
    
    # Write request file
    output_path = Path(args.output) if args.output else Path(f"{result.plugin_name}.cert-request.json")
    with open(output_path, "w", encoding="utf-8") as f:
        json.dump(cert, f, indent=2)
    
    print(f"Certification request created: {output_path}")
    print(f"   Plugin: {result.plugin_name} v{result.plugin_version}")
    print(f"   Hash: {result.distribution_hash}")
    print(f"\nSubmit this file to the certification authority for signing.")
    return 0


def cmd_sign(args):
    """Sign a certification request (certification authority only)."""
    request_path = Path(args.request).resolve()
    
    if not request_path.exists():
        print(f"Error: Request file not found: {request_path}", file=sys.stderr)
        return 1
    
    # Get signing key from environment
    signing_key = args.key or os.environ.get("CF_CERT_SIGNING_KEY_B64")
    if not signing_key:
        print("Error: No signing key provided.", file=sys.stderr)
        print("Use --key or set CF_CERT_SIGNING_KEY_B64 environment variable.", file=sys.stderr)
        return 1
    
    output_path = Path(args.output) if args.output else None
    
    try:
        cert = sign_certificate_request(request_path, signing_key, output_path)
        actual_output = output_path or request_path.with_suffix("").with_suffix(".cert.json")
        print(f"Certificate signed: {actual_output}")
        print(f"   Plugin: {cert['plugin_name']} v{cert['plugin_version']}")
        return 0
    except Exception as e:
        print(f"Error signing certificate: {e}", file=sys.stderr)
        return 1


def cmd_import(args):
    """Import a signed certificate into a step package."""
    cert_path = Path(args.certificate).resolve()
    plugin_path = Path(args.path).resolve()
    
    if not cert_path.exists():
        print(f"Error: Certificate file not found: {cert_path}", file=sys.stderr)
        return 1
    
    # Read certificate
    with open(cert_path, "r", encoding="utf-8") as f:
        cert_data = json.load(f)

    try:
        steps_path, steps_doc = load_steps_jsonld(plugin_path)
        plugin_node, _ = select_plugin_artifact(steps_doc)
    except Exception as exc:
        print(f"Error: {exc}", file=sys.stderr)
        return 1

    _set_plugin_certificate(plugin_node, cert_data)
    with open(steps_path, "w", encoding="utf-8") as f:
        json.dump(steps_doc, f, indent=2)

    print(f"Certificate imported into {steps_path}")
    return 0


def cmd_verify(args):
    """Verify a step package certificate."""
    plugin_path = Path(args.path).resolve()
    
    if not plugin_path.exists():
        print(f"Error: Path does not exist: {plugin_path}", file=sys.stderr)
        return 1
    
    # Get verification key (not needed for self-signed)
    public_key = args.key or os.environ.get("CF_CERT_VERIFICATION_KEY_B64") or OFFICIAL_PUBLIC_KEY_B64
    
    result = verify_certificate(plugin_path, public_key)
    
    if result["certificate"]:
        cert = result["certificate"]
        print(f"Plugin: {cert.plugin_name} v{cert.plugin_version}")
        print(f"Certified at: {cert.certified_at}")
        print(f"Format: {cert.format_version}")
        print(f"Trust Level: {cert.trust_level}")
        print(f"Signed: {'Yes' if cert.is_signed else 'No'}")
    
    print(f"\nVerification Results:")
    print(f"  Hash matches: {'Yes' if result['hash_matches'] else 'No'}")
    if result["signature_valid"] is not None:
        print(f"  Signature valid: {'Yes' if result['signature_valid'] else 'No'}")
    
    # Show trust level warning
    trust_level = result.get("trust_level", "unknown")
    if trust_level == "self-signed":
        print("  Trust: Self-signed (hash verified, but not CA certified)")
    elif trust_level == "ca-signed":
        print("  Trust: CA-signed (officially certified)")
    elif trust_level == "unsigned":
        print("  Trust: Unsigned (no guarantees)")
    
    print(f"  Overall: {'VALID' if result['valid'] else 'INVALID'}")
    
    if result["errors"]:
        print(f"\nErrors:")
        for err in result["errors"]:
            print(f"  - {err}")
    
    return 0 if result["valid"] else 1


def cmd_hash(args):
    """Compute distribution hash for a step package."""
    plugin_path = Path(args.path).resolve()
    
    if not plugin_path.exists():
        print(f"Error: Path does not exist: {plugin_path}", file=sys.stderr)
        return 1

    try:
        steps_path, _ = load_steps_jsonld(plugin_path)
        package_root = steps_path.parent
    except Exception as exc:
        print(f"Error: {exc}", file=sys.stderr)
        return 1

    hash_value = compute_distribution_hash(package_root)
    
    if args.json:
        print(json.dumps({"path": str(plugin_path), "hash": hash_value}))
    else:
        print(f"Distribution Hash: {hash_value}")
    
    return 0


def cmd_keygen(args):
    """Generate a new Ed25519 keypair."""
    private_key, public_key = generate_keypair()
    
    print("=" * 60)
    print("GENERATED ED25519 KEYPAIR")
    print("=" * 60)
    print()
    print("PRIVATE KEY (keep this secret!):")
    print(f"   {private_key}")
    print()
    print("PUBLIC KEY (can be shared):")
    print(f"   {public_key}")
    print()
    print("=" * 60)
    print()
    print("For CI/CD usage, set these environment variables:")
    print(f"  CF_CERT_SIGNING_KEY_B64={private_key}")
    print(f"  CF_CERT_VERIFICATION_KEY_B64={public_key}")
    print()
    print("For GitHub Actions, add these as repository secrets.")
    
    return 0


def cmd_certify(args):
    """One-step certification: validate, sign, and embed certificate."""
    plugin_path = Path(args.path).resolve()
    
    # Validate first
    result = validate_step_package(plugin_path)
    if not result.valid:
        print("Error: Plugin validation failed", file=sys.stderr)
        for err in result.errors:
            print(f"  - {err}", file=sys.stderr)
        return 1
    
    # Determine certification mode
    if args.self_signed:
        # Self-signed mode: generate keypair, sign, embed public key
        cert = result.to_self_signed_certificate()
        trust_msg = "self-signed"
    else:
        # CA-signed mode: requires signing key
        signing_key = args.key or os.environ.get("CF_CERT_SIGNING_KEY_B64")
        if not signing_key:
            print("Error: No signing key provided.", file=sys.stderr)
            print("Use --key or set CF_CERT_SIGNING_KEY_B64 environment variable.", file=sys.stderr)
            print("Or use --self-signed for local/development use.", file=sys.stderr)
            return 1
        cert = result.to_certificate(sign_key_b64=signing_key)
        trust_msg = "CA-signed"
    
    # Embed in plugin.json
    try:
        steps_path, steps_doc = load_steps_jsonld(plugin_path)
        plugin_node, _ = select_plugin_artifact(steps_doc)
    except Exception as exc:
        print(f"Error: {exc}", file=sys.stderr)
        return 1

    _set_plugin_certificate(plugin_node, cert)
    with open(steps_path, "w", encoding="utf-8") as f:
        json.dump(steps_doc, f, indent=2)

    print(f"Plugin certified ({trust_msg}): {result.plugin_name} v{result.plugin_version}")
    print(f"   Hash: {result.distribution_hash}")
    print(f"   Certificate embedded in: {steps_path}")
    
    if args.self_signed:
        print()
        print("Self-signed certificates:")
        print("  - Protect against tampering after creation")
        print("  - Anyone can verify hash integrity")
        print("  - Not CA-certified (lower trust level)")
    
    return 0


def _add_subcommands(parser: argparse.ArgumentParser) -> None:
    subparsers = parser.add_subparsers(dest="command", help="Available commands")

    # validate command
    p_validate = subparsers.add_parser("validate", help="Validate a step package")
    p_validate.add_argument("path", help="Path to the step package")
    p_validate.set_defaults(func=cmd_validate, _cf_handler=cmd_validate)

    # request command
    p_request = subparsers.add_parser("request", help="Create a certification request")
    p_request.add_argument("path", help="Path to the step package")
    p_request.add_argument("-o", "--output", help="Output file path")
    p_request.set_defaults(func=cmd_request, _cf_handler=cmd_request)

    # sign command
    p_sign = subparsers.add_parser("sign", help="Sign a certification request")
    p_sign.add_argument("request", help="Path to the .cert-request.json file")
    p_sign.add_argument("-k", "--key", help="Signing key (base64)")
    p_sign.add_argument("-o", "--output", help="Output file path")
    p_sign.set_defaults(func=cmd_sign, _cf_handler=cmd_sign)

    # import command
    p_import = subparsers.add_parser("import", help="Import a signed certificate")
    p_import.add_argument("certificate", help="Path to the .cert.json file")
    p_import.add_argument("path", help="Path to the step package")
    p_import.set_defaults(func=cmd_import, _cf_handler=cmd_import)

    # verify command
    p_verify = subparsers.add_parser("verify", help="Verify a step package certificate")
    p_verify.add_argument("path", help="Path to the step package")
    p_verify.add_argument("-k", "--key", help="Verification key (base64)")
    p_verify.set_defaults(func=cmd_verify, _cf_handler=cmd_verify)

    # hash command
    p_hash = subparsers.add_parser("hash", help="Compute distribution hash")
    p_hash.add_argument("path", help="Path to the step package")
    p_hash.add_argument("--json", action="store_true", help="Output as JSON")
    p_hash.set_defaults(func=cmd_hash, _cf_handler=cmd_hash)

    # keygen command
    p_keygen = subparsers.add_parser("keygen", help="Generate a new keypair")
    p_keygen.set_defaults(func=cmd_keygen, _cf_handler=cmd_keygen)

    # certify command (one-step)
    p_certify = subparsers.add_parser("certify", help="One-step certification (validate + sign + embed)")
    p_certify.add_argument("path", help="Path to the step package")
    p_certify.add_argument("-k", "--key", help="Signing key (base64) for CA-signed certificates")
    p_certify.add_argument(
        "--self-signed", 
        action="store_true",
        dest="self_signed",
        help="Create self-signed certificate (no CA required, lower trust level)"
    )
    p_certify.set_defaults(func=cmd_certify, _cf_handler=cmd_certify)


def _build_parser(*, prog: str = "cf pipeline cert") -> argparse.ArgumentParser:
    """Build argument parser for certification CLI."""
    parser = argparse.ArgumentParser(
        prog=prog,
        description="CogniFlow Step Package Certification Tool",
    )
    parser.add_argument(
        "--version", action="version", version=f"cf pipeline cert {__version__}"
    )
    _add_subcommands(parser)
    return parser


def run(
    argv: list[str] | None = None,
    *,
    prog: str = "cf pipeline cert",
) -> int:
    parser = _build_parser(prog=prog)
    args = parser.parse_args(argv)
    if not args.command:
        parser.print_help()
        return 1

    return args.func(args)


def main() -> int:
    """Main entry point for CLI."""
    return run()


def _add_cf_parser(subparsers: argparse._SubParsersAction) -> argparse.ArgumentParser:
    parser = subparsers.add_parser("cert", help="Step package certification tools.")
    _add_subcommands(parser)
    return parser


def _register_cf_cli() -> None:
    if register_command is None:
        return
    register_command(
        "pipeline",
        "cert",
        _add_cf_parser,
        group_help="Pipeline tools",
        command_help="Step package certification tools",
    )


if __name__ == "__main__":
    sys.exit(main())

_register_cf_cli()
