#!/usr/bin/env python3
"""
gpathex - Main CLI Interface

Genbinesia Pathway Extractor
"""
import sys
import argparse
import logging
from pathlib import Path
from importlib.metadata import version

from gpathex import __version__, __author__, __email__
from gpathex.config import config


def show_banner():
    """Display gpathex banner."""
    banner = r"""
    gpathex v{version}
    Genbinesia Pathway Extractor 
    
    Authors: {authors}
    Contact: {email}
    """
    print(banner.format(
        version=__version__,
        authors=__author__,
        email=__email__
    ))


def create_parser():
    """Create main argument parser."""
    parser = argparse.ArgumentParser(
        prog="gpathex",
        description="gpathex - Extract the juice from KEGG databases!",
        formatter_class=argparse.RawDescriptionHelpFormatter,
        epilog="""
Examples:
  Get KEGG organisms:   gpathex download-org --out organisms.tsv
  Download KO files:    gpathex download-ko --org organisms.tsv --out ko_files
  Get protein sequences: gpathex download-proteins --org organisms.tsv --out proteins
  Create database:      gpathex make-db --org organisms.tsv --keg ko_files --pep proteins --out my_db
  Create .keg file:     gpathex make-keg --keg ko00001.keg --in annotations.tsv --out results
  Plot KO hierarchy:    gpathex plot-keg-kos --keg ko00001.keg --out kos_plot
  Plot gene annotations: gpathex plot-keg-genes --keg results.keg --out genes_plot
  Get organism ranks:   gpathex get-ranks --keg br08610.keg --taxon ncbi_taxonomy.tsv --out ranks.tsv
  
Short alias: gpe
        """
    )
    
    parser.add_argument(
        "-V", "--version",
        action="version",
        version=f"gpathex v{__version__}"
    )
    
    parser.add_argument(
        "-v", "--verbose",
        action="store_true",
        help="Enable verbose output"
    )
    
    parser.add_argument(
        "-q", "--quiet",
        action="store_true",
        help="Suppress non-essential output"
    )
    
    # Create subparsers
    subparsers = parser.add_subparsers(
        title="Available Commands",
        dest="command",
        metavar="<command>",
        help="Command to execute"
    )
    
    # Add all command parsers
    _add_download_org_parser(subparsers)
    _add_download_ko_parser(subparsers)
    _add_download_proteins_parser(subparsers)
    _add_process_proteins_parser(subparsers)
    _add_makedb_parser(subparsers)
    _add_make_keg_parser(subparsers)
    _add_plot_keg_kos_parser(subparsers)      # New: KO hierarchy plotting
    _add_plot_keg_genes_parser(subparsers)    # New: Gene annotation plotting
    _add_plot_keg_parser(subparsers)          # Legacy: Auto-detect plotting
    _add_get_ranks_parser(subparsers)
    _add_config_parser(subparsers)
    _add_info_parser(subparsers)
    _add_download_taxonomy_parser(subparsers)
    
    return parser


def _add_download_org_parser(subparsers):
    """Add download-org command."""
    parser = subparsers.add_parser(
        "download-org",
        help="Download KEGG organism information"
    )
    parser.add_argument(
        "--url",
        default="https://www.kegg.jp/kegg/tables/br08606.html",
        help="KEGG organism list URL (default: https://www.kegg.jp/kegg/tables/br08606.html)"
    )
    parser.add_argument(
        "--out", "-o",
        default=config.get("DEFAULT_ORG_FILE"),
        help=f"Output file (default: {config.get('DEFAULT_ORG_FILE')})"
    )
    parser.add_argument(
        "--verbose", "-v",
        action="store_true",
        help="Verbose output"
    )


def _add_download_ko_parser(subparsers):
    """Add download-ko command."""
    parser = subparsers.add_parser(
        "download-ko",
        help="Download KEGG Orthology (KO) files"
    )
    parser.add_argument(
        "--org", "-i",
        required=True,
        help="Input file with organism codes (one per line or TSV)"
    )
    parser.add_argument(
        "--out", "-o",
        default="ko_files",
        help="Output directory (default: ko_files)"
    )
    parser.add_argument(
        "--concurrent", "-c",
        type=int,
        default=config.get("DEFAULT_CONCURRENT"),
        help=f"Number of concurrent downloads (default: {config.get('DEFAULT_CONCURRENT')})"
    )
    parser.add_argument(
        "--retries", "-r",
        type=int,
        default=config.get("MAX_RETRIES"),
        help=f"Maximum retry attempts (default: {config.get('MAX_RETRIES')})"
    )
    parser.add_argument(
        "--skip-existing",
        action="store_true",
        default=True,
        help="Skip files that already exist (default: True)"
    )
    parser.add_argument(
        "--no-skip-existing",
        action="store_false",
        dest="skip_existing",
        help="Download even if file exists"
    )
    parser.add_argument(
        "--verbose", "-v",
        action="store_true",
        help="Verbose output"
    )


def _add_download_proteins_parser(subparsers):
    """Add download-proteins command."""
    parser = subparsers.add_parser(
        "download-proteins",
        help="Download protein sequences from NCBI"
    )
    parser.add_argument(
        "--org", "-i",
        required=True,
        help="Organism file from download-org command"
    )
    parser.add_argument(
        "--out", "-o",
        default="proteins",
        help="Output directory (default: proteins)"
    )
    parser.add_argument(
        "--concurrent", "-c",
        type=int,
        default=config.get("DEFAULT_CONCURRENT"),
        help=f"Number of concurrent downloads (default: {config.get('DEFAULT_CONCURRENT')})"
    )
    parser.add_argument(
        "--format",
        choices=["fasta", "fasta.gz"],
        default="fasta.gz",
        help="Download format (default: fasta.gz)"
    )
    parser.add_argument(
        "--skip-ncbi-check",
        action="store_true",
        help="Skip NCBI URL validation"
    )
    parser.add_argument(
        "--skip-existing",
        action="store_true",
        default=True,
        help="Skip files that already exist (default: True)"
    )
    parser.add_argument(
        "--no-skip-existing",
        action="store_false",
        dest="skip_existing",
        help="Download even if file exists"
    )
    parser.add_argument(
        "--verbose", "-v",
        action="store_true",
        help="Verbose output"
    )


def _add_process_proteins_parser(subparsers):
    """Add process-proteins command."""
    parser = subparsers.add_parser(
        "process-proteins",
        help="Process proteins with KO annotations"
    )
    parser.add_argument(
        "--org", "-i",
        required=True,
        help="Organism file with codes"
    )
    parser.add_argument(
        "--keg", "-k",
        required=True,
        help="Directory with KO files"
    )
    parser.add_argument(
        "--pep", "-p",
        required=True,
        help="Directory with protein files"
    )
    parser.add_argument(
        "--out", "-o",
        default="processed",
        help="Output directory (default: processed)"
    )
    parser.add_argument(
        "--min-ko",
        type=int,
        default=1,
        help="Minimum number of KOs required (default: 1)"
    )
    parser.add_argument(
        "--verbose", "-v",
        action="store_true",
        help="Verbose output"
    )


def _add_makedb_parser(subparsers):
    """Add make-db command."""
    parser = subparsers.add_parser(
        "make-db",
        help="Create custom KEGG database"
    )
    parser.add_argument(
        "--org", "-i",
        required=True,
        help="Organism file with codes"
    )
    parser.add_argument(
        "--keg", "-k",
        required=True,
        help="Directory with KO files"
    )
    parser.add_argument(
        "--pep", "-p",
        required=True,
        help="Directory with protein files"
    )
    parser.add_argument(
        "--out", "-o",
        default="kegg_db",
        help="Output prefix (default: kegg_db)"
    )
    parser.add_argument(
        "--format",
        choices=["fasta", "parquet", "both"],
        default="both",
        help="Output format (default: both)"
    )
    parser.add_argument(
        "--compress",
        action="store_true",
        help="Compress output files"
    )
    parser.add_argument(
        "--verbose", "-v",
        action="store_true",
        help="Verbose output"
    )


def _add_make_keg_parser(subparsers):
    """Add make-keg command."""
    parser = subparsers.add_parser(
        "make-keg",
        help="Create .keg file from annotations"
    )
    parser.add_argument(
        "--keg", "-k",
        required=True,
        help="Template KEGG file (e.g., ko00001.keg)"
    )
    parser.add_argument(
        "--in", "-i",
        dest="input_file",
        required=True,
        help="Input annotations file"
    )
    parser.add_argument(
        "--out", "-o",
        default="output",
        help="Output prefix (default: output)"
    )
    parser.add_argument(
        "--verbose", "-v",
        action="store_true",
        help="Verbose output"
    )


def _add_plot_keg_kos_parser(subparsers):
    """Add plot-keg-kos command."""
    parser = subparsers.add_parser(
        "plot-keg-kos",
        help="Plot KEGG KO hierarchy (ko00001.keg format)"
    )
    parser.add_argument(
        "--keg", "-k",
        required=True,
        help="Input .keg file with KO hierarchy (e.g., ko00001.keg)"
    )
    parser.add_argument(
        "--out", "-o",
        default="kegg_kos_plot",
        help="Output file or prefix (default: kegg_kos_plot)"
    )
    parser.add_argument(
        "--format",
        choices=["png", "pdf", "svg", "jpg", "jpeg"],
        default="png",
        help="Output format (default: png)"
    )
    parser.add_argument(
        "--width",
        type=float,
        default=10,
        help="Figure width in inches (default: 10)"
    )
    parser.add_argument(
        "--height",
        type=float,
        default=8,
        help="Figure height in inches (default: 8)"
    )
    parser.add_argument(
        "--dpi",
        type=int,
        default=300,
        help="Figure DPI (default: 300)"
    )
    parser.add_argument(
        "--verbose", "-v",
        action="store_true",
        help="Verbose output"
    )


def _add_plot_keg_genes_parser(subparsers):
    """Add plot-keg-genes command."""
    parser = subparsers.add_parser(
        "plot-keg-genes",
        help="Plot KEGG gene annotations (from make-keg command)"
    )
    parser.add_argument(
        "--keg", "-k",
        required=True,
        help="Input .keg file with gene annotations (from make-keg)"
    )
    parser.add_argument(
        "--out", "-o",
        default="kegg_genes_plot",
        help="Output file or prefix (default: kegg_genes_plot)"
    )
    parser.add_argument(
        "--format",
        choices=["png", "pdf", "svg", "jpg", "jpeg"],
        default="png",
        help="Output format (default: png)"
    )
    parser.add_argument(
        "--width",
        type=float,
        default=10,
        help="Figure width in inches (default: 10)"
    )
    parser.add_argument(
        "--height",
        type=float,
        default=8,
        help="Figure height in inches (default: 8)"
    )
    parser.add_argument(
        "--dpi",
        type=int,
        default=300,
        help="Figure DPI (default: 300)"
    )
    parser.add_argument(
        "--verbose", "-v",
        action="store_true",
        help="Verbose output"
    )


def _add_plot_keg_parser(subparsers):
    """Add legacy plot-keg command for backward compatibility."""
    parser = subparsers.add_parser(
        "plot-keg",
        help="Legacy: Plot KEGG annotation results (use plot-keg-kos or plot-keg-genes)"
    )
    parser.add_argument(
        "--keg", "-k",
        required=True,
        help="Input .keg file"
    )
    parser.add_argument(
        "--out", "-o",
        default="kegg_plot",
        help="Output file or prefix (default: kegg_plot)"
    )
    parser.add_argument(
        "--format",
        choices=["png", "pdf", "svg", "jpg", "jpeg"],
        default="png",
        help="Output format (default: png)"
    )
    parser.add_argument(
        "--width",
        type=float,
        default=10,
        help="Figure width in inches (default: 10)"
    )
    parser.add_argument(
        "--height",
        type=float,
        default=8,
        help="Figure height in inches (default: 8)"
    )
    parser.add_argument(
        "--dpi",
        type=int,
        default=300,
        help="Figure DPI (default: 300)"
    )
    parser.add_argument(
        "--verbose", "-v",
        action="store_true",
        help="Verbose output"
    )


def _add_download_taxonomy_parser(subparsers):
    """Add download-taxonomy command."""
    parser = subparsers.add_parser(
        "download-taxonomy",
        help="Download NCBI taxonomy database"
    )
    parser.add_argument(
        "--out", "-o",
        default="ncbi_taxonomy.tsv",
        help="Output file (default: ncbi_taxonomy.tsv)"
    )
    parser.add_argument(
        "--force", "-f",
        action="store_true",
        help="Force download even if file exists"
    )
    parser.add_argument(
        "--verbose", "-v",
        action="store_true",
        help="Verbose output"
    )


def _add_get_ranks_parser(subparsers):
    """Add get-ranks command."""
    parser = subparsers.add_parser(
        "get-ranks",
        help="Get KEGG organism classification ranks"
    )
    parser.add_argument(
        "--keg", "-k",
        required=True,
        help="KEGG taxonomy (br08610.keg) file (br08610.keg file, download it from https://www.kegg.jp/kegg-bin/download_htext?htext=br08610&format=htext&filedir=)"
    )
    parser.add_argument(
        "--taxon", "-t",
        required=True,
        help="NCBI taxonomy file (ncbi_taxonomy.tsv). Create by gpathex download-taxonomy --out /path/to/your/ncbi_taxonomy.tsv "
    )
    parser.add_argument(
        "--out", "-o",
        default=config.get("DEFAULT_RANKS_FILE"),
        help=f"Output file (default: {config.get('DEFAULT_RANKS_FILE')})"
    )
    parser.add_argument(
        "--verbose", "-v",
        action="store_true",
        help="Verbose output"
    )


def _add_config_parser(subparsers):
    """Add config command."""
    parser = subparsers.add_parser(
        "config",
        help="Show or modify configuration"
    )
    parser.add_argument(
        "key",
        nargs="?",
        help="Configuration key to view or modify"
    )
    parser.add_argument(
        "value",
        nargs="?",
        help="Value to set (if modifying)"
    )
    parser.add_argument(
        "--list", "-l",
        action="store_true",
        help="List all configuration values"
    )
    parser.add_argument(
        "--reset",
        action="store_true",
        help="Reset to default values"
    )
    parser.add_argument(
        "--verbose", "-v",
        action="store_true",
        help="Verbose output"
    )


def _add_info_parser(subparsers):
    """Add info command."""
    subparsers.add_parser(
        "info",
        help="Show system information and dependencies"
    )


def main() -> int:
    """Main entry point."""
    parser = create_parser()
    
    # Show banner if no arguments or help requested
    if len(sys.argv) == 1:
        show_banner()
        parser.print_help()
        return 0
    
    # Parse arguments
    args = parser.parse_args()
    
    # Set up logging based on verbosity
    if args.quiet:
        log_level = logging.ERROR
    elif args.verbose:
        log_level = logging.DEBUG
    else:
        log_level = getattr(logging, config.get("LOG_LEVEL").upper())
    
    logging.basicConfig(
        level=log_level,
        format=config.get("LOG_FORMAT")
    )
    
    LOG = logging.getLogger(__name__)
    
    # Import commands dynamically to avoid circular imports
    try:
        if args.command == "download-org":
            from gpathex.commands.download_org import main as download_org_main
            import sys as sys_module
            sys_argv_backup = sys_module.argv.copy()
            
            cmd_args = [
                "download-org",
                "--url", args.url,
                "--out", args.out,
            ]
            if hasattr(args, 'api') and args.api:
                cmd_args.append("--api")
            if args.verbose:
                cmd_args.append("--verbose")
            
            sys_module.argv = cmd_args
            result = download_org_main()
            sys_module.argv = sys_argv_backup
            return result
        
        elif args.command == "download-ko":
            from gpathex.commands.download_ko import main as download_ko_main
            import sys as sys_module
            sys_argv_backup = sys_module.argv.copy()
            
            cmd_args = [
                "download-ko",
                "--org", args.org,
                "--out", args.out,
                "--concurrent", str(args.concurrent),
                "--retries", str(args.retries),
            ]
            if hasattr(args, 'skip_existing') and args.skip_existing:
                cmd_args.append("--skip-existing")
            else:
                cmd_args.append("--no-skip-existing")
            if args.verbose:
                cmd_args.append("--verbose")
            
            sys_module.argv = cmd_args
            result = download_ko_main()
            sys_module.argv = sys_argv_backup
            return result
        
        elif args.command == "download-proteins":
            from gpathex.commands.download_proteins import main as download_proteins_main
            import sys as sys_module
            sys_argv_backup = sys_module.argv.copy()
            
            cmd_args = [
                "download-proteins",
                "--org", args.org,
                "--out", args.out,
                "--concurrent", str(args.concurrent),
                "--format", args.format,
            ]
            if hasattr(args, 'skip_ncbi_check') and args.skip_ncbi_check:
                cmd_args.append("--skip-ncbi-check")
            if hasattr(args, 'skip_existing') and args.skip_existing:
                cmd_args.append("--skip-existing")
            else:
                cmd_args.append("--no-skip-existing")
            if args.verbose:
                cmd_args.append("--verbose")
            
            sys_module.argv = cmd_args
            result = download_proteins_main()
            sys_module.argv = sys_argv_backup
            return result
        
        elif args.command == "process-proteins":
            from gpathex.commands.process_proteins import main as process_proteins_main
            import sys as sys_module
            sys_argv_backup = sys_module.argv.copy()
            
            cmd_args = [
                "process-proteins",
                "--org", args.org,
                "--keg", args.keg,
                "--pep", args.pep,
                "--out", args.out,
                "--min-ko", str(args.min_ko),
            ]
            if args.verbose:
                cmd_args.append("--verbose")
            
            sys_module.argv = cmd_args
            result = process_proteins_main()
            sys_module.argv = sys_argv_backup
            return result
        
        elif args.command == "make-db":
            from gpathex.commands.makedb import main as makedb_main
            import sys as sys_module
            sys_argv_backup = sys_module.argv.copy()
            
            cmd_args = [
                "make-db",
                "--org", args.org,
                "--keg", args.keg,
                "--pep", args.pep,
                "--out", args.out,
                "--format", args.format,
            ]
            if hasattr(args, 'compress') and args.compress:
                cmd_args.append("--compress")
            if args.verbose:
                cmd_args.append("--verbose")
            
            sys_module.argv = cmd_args
            result = makedb_main()
            sys_module.argv = sys_argv_backup
            return result
        
        elif args.command == "make-keg":
            from gpathex.commands.make_keg import main as make_keg_main
            import sys as sys_module
            sys_argv_backup = sys_module.argv.copy()
            
            cmd_args = [
                "make-keg",
                "--keg", args.keg,
                "--in", args.input_file,
                "--out", args.out,
            ]
            if args.verbose:
                cmd_args.append("--verbose")
            
            sys_module.argv = cmd_args
            result = make_keg_main()
            sys_module.argv = sys_argv_backup
            return result
        
        elif args.command == "plot-keg-kos":
            from gpathex.commands.plot_keg import main_plot_kos
            import sys as sys_module
            sys_argv_backup = sys_module.argv.copy()
            
            cmd_args = [
                "plot-keg-kos",
                "--keg", args.keg,
                "--out", args.out,
                "--format", args.format,
                "--width", str(args.width),
                "--height", str(args.height),
                "--dpi", str(args.dpi),
            ]
            if args.verbose:
                cmd_args.append("--verbose")
            
            sys_module.argv = cmd_args
            result = main_plot_kos()
            sys_module.argv = sys_argv_backup
            return result
        
        elif args.command == "plot-keg-genes":
            from gpathex.commands.plot_keg import main_plot_genes
            import sys as sys_module
            sys_argv_backup = sys_module.argv.copy()
            
            cmd_args = [
                "plot-keg-genes",
                "--keg", args.keg,
                "--out", args.out,
                "--format", args.format,
                "--width", str(args.width),
                "--height", str(args.height),
                "--dpi", str(args.dpi),
            ]
            if args.verbose:
                cmd_args.append("--verbose")
            
            sys_module.argv = cmd_args
            result = main_plot_genes()
            sys_module.argv = sys_argv_backup
            return result
        
        elif args.command == "plot-keg":
            from gpathex.commands.plot_keg import main as plot_keg_main
            import sys as sys_module
            sys_argv_backup = sys_module.argv.copy()
            
            cmd_args = [
                "plot-keg",
                "--keg", args.keg,
                "--out", args.out,
                "--format", args.format,
                "--width", str(args.width),
                "--height", str(args.height),
                "--dpi", str(args.dpi),
            ]
            if args.verbose:
                cmd_args.append("--verbose")
            
            sys_module.argv = cmd_args
            result = plot_keg_main()
            sys_module.argv = sys_argv_backup
            return result
        
        elif args.command == "download-taxonomy":
            from gpathex.utils.taxonomy import download_and_create_taxonomy
            import sys as sys_module
            sys_argv_backup = sys_module.argv.copy()
            
            try:
                count = download_and_create_taxonomy(
                    args.out,
                    selected_taxa=None  # Download all
                )
                print(f"✅ Successfully created taxonomy file with {count} taxa")
                print(f"📁 Saved to: {args.out}")
                return 0
            except Exception as e:
                LOG.error(f"❌ Failed to download taxonomy: {e}")
                return 1
        
        elif args.command == "get-ranks":
            from gpathex.commands.get_ranks import main as get_ranks_main
            import sys as sys_module
            sys_argv_backup = sys_module.argv.copy()
            
            cmd_args = [
                "get-ranks",
                "--keg", args.keg,
                "--taxon", args.taxon,
                "--out", args.out,
            ]
            if hasattr(args, 'download_tax') and args.download_tax:
                cmd_args.append("--download-tax")
            if hasattr(args, 'force_download') and args.force_download:
                cmd_args.append("--force-download")
            if args.verbose:
                cmd_args.append("--verbose")
            
            sys_module.argv = cmd_args
            result = get_ranks_main()
            sys_module.argv = sys_argv_backup
            return result    
                
        elif args.command == "config":
            if hasattr(args, 'reset') and args.reset:
                # Reset to defaults
                from gpathex.config import Config
                config._config = Config._config.copy()  # Reset to original defaults
                print("Configuration reset to defaults")
                return 0
            
            elif hasattr(args, 'list') and args.list:
                print("Current Configuration:")
                for key, value in sorted(config.all.items()):
                    print(f"  {key}: {value}")
                return 0
            
            elif hasattr(args, 'key') and args.key:
                if hasattr(args, 'value') and args.value is not None:
                    # Set value
                    config.set(args.key.upper(), args.value)
                    print(f"Set {args.key} = {args.value}")
                else:
                    # Get value
                    value = config.get(args.key.upper())
                    if value is not None:
                        print(f"{args.key}: {value}")
                    else:
                        print(f"Configuration key not found: {args.key}")
                        return 1
                return 0
            
            else:
                print("Current Configuration:")
                for key, value in sorted(config.all.items()):
                    print(f"  {key}: {value}")
                return 0
        
        elif args.command == "info":
            show_banner()
            print("\nSystem Information:")
            print(f"  Python: {sys.version}")
            print(f"  Platform: {sys.platform}")
            
            # Check dependencies
            from gpathex.utils.polars_utils import (
                check_polars_available, 
                check_biopython_available,
                check_polars_bio_available
            )
            
            print("\n🔧 Core Dependencies:")
            print(f"  Polars: {'Available' if check_polars_available() else 'Not installed'}")
            print(f"  BioPython: {'Available' if check_biopython_available() else 'Not installed'}")
            print(f"  polars-bio: {'Available' if check_polars_bio_available() else 'Not installed (optional)'}")
            
            # Try to import matplotlib
            try:
                import matplotlib
                print(f"  matplotlib: Available (v{matplotlib.__version__})")
            except ImportError:
                print(f"  matplotlib: Not installed (optional for plotting)")
            
            # Try to import rich
            try:
                import rich
                print(f"  rich: Available (v{version('rich')})")
            except ImportError:
                print(f"  rich: Not installed (optional for pretty output)")
            
            return 0
        
        elif args.command is None:
            # No command specified
            show_banner()
            parser.print_help()
            return 0
        
        else:
            LOG.error(f"Unknown command: {args.command}")
            parser.print_help()
            return 1
    
    except KeyboardInterrupt:
        print("\n\n  Operation cancelled by user")
        return 130
    
    except ImportError as e:
        LOG.error(f"Failed to import command module: {e}")
        LOG.error("Make sure all dependencies are installed:")
        LOG.error("  pip install 'gpathex[all]'")
        return 1
    
    except Exception as e:
        LOG.error(f"Error executing command: {e}")
        if hasattr(args, 'verbose') and args.verbose:
            import traceback
            traceback.print_exc()
        return 1


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