"""Vibelexity: Cognitive complexity analyzer for Python, TypeScript, and Go source files."""

from __future__ import annotations

import argparse
import sys
from dataclasses import dataclass
from pathlib import Path

from vibelexity import __version__
from vibelexity.analysis import (
    _all_functions,
    _analyze_files,
    _build_candidate_lists,
    _compute_cycle_stats,
    _compute_duplication_stats,
)
from vibelexity.call_graph import build_call_graph
from vibelexity.call_graph.formatters import format_dot
from vibelexity.circular_deps import find_cycles, find_cycles_tarjan
from vibelexity.dead_code.core import find_dead_code
from vibelexity.dead_code.formatters import format_text as format_dead_code_text
from vibelexity.file_utils import _SUPPORTED_EXTENSIONS, _collect_source_files
from vibelexity.metrics.duplication.core import find_duplicates
from vibelexity.output.json import JsonOutputConfig
from vibelexity.output.rich_output import (
    print_cycle_stats_table,
    print_dead_code_table,
    print_duplication_table,
    print_fatness_table,
    print_pattern_violations_table,
    print_refactor_candidates_table,
    print_worst_metrics_table,
)

# CLI argument definitions
_CLI_ARGUMENTS = [
    (
        "--version",
        {
            "action": "version",
            "version": f"vibelexity {__version__}",
        },
    ),
    (
        "path",
        {
            "nargs": "?",
            "default": ".",
            "help": "Directory or file to analyze (default: current directory)",
        },
    ),
    (
        "--json",
        {
            "dest": "json_output",
            "action": "store_true",
            "help": "Output results as JSON",
        },
    ),
    (
        "--show-dupes",
        {
            "dest": "show_dupes",
            "action": "store_true",
            "help": "Show detailed code duplication sites",
        },
    ),
    (
        "--show-dead",
        {
            "dest": "show_dead",
            "action": "store_true",
            "help": "Show all dead code violations found",
        },
    ),
    (
        "--show-bad-patterns",
        {
            "dest": "show_bad_patterns",
            "action": "store_true",
            "help": "Show detailed pattern violations list",
        },
    ),
    (
        "--show-circulars",
        {
            "dest": "show_circulars",
            "action": "store_true",
            "help": "Show circular import cycle details",
        },
    ),
    (
        "--ts-module-resolution",
        {
            "dest": "ts_module_resolution",
            "choices": ["node", "node16", "nodenext", "bundler"],
            "default": None,
            "help": "TypeScript module resolution mode (default: auto-discover from tsconfig.json)",
        },
    ),
    (
        "--algorithm",
        {
            "dest": "algorithm",
            "choices": ["dfs", "tarjan"],
            "default": "dfs",
            "help": "Cycle detection algorithm (default: dfs)",
        },
    ),
    (
        "--call-graph",
        {
            "dest": "call_graph",
            "action": "store_true",
            "help": "Generate call graph analysis (DOT format)",
        },
    ),
    (
        "--show-fatties",
        {
            "dest": "show_fatties",
            "action": "store_true",
            "help": "Show detailed fat files and functions list",
        },
    ),
    (
        "--worst-cog",
        {
            "dest": "worst_cog",
            "action": "store_true",
            "help": "Show worst 10 Cognitive Complexity scores",
        },
    ),
    (
        "--worst-npath",
        {
            "dest": "worst_npath",
            "action": "store_true",
            "help": "Show worst 10 NPath Complexity scores",
        },
    ),
    (
        "--worst-dtd",
        {
            "dest": "worst_dtd",
            "action": "store_true",
            "help": "Show worst 10 Data Transformation Density scores",
        },
    ),
    (
        "--worst-cc",
        {
            "dest": "worst_cc",
            "action": "store_true",
            "help": "Show worst 10 Cyclomatic Complexity scores",
        },
    ),
    (
        "--worst-mi",
        {
            "dest": "worst_mi",
            "action": "store_true",
            "help": "Show worst 10 Maintainability Index scores (lower is worse)",
        },
    ),
    (
        "--worst-ldi",
        {
            "dest": "worst_ldi",
            "action": "store_true",
            "help": "Show worst 10 Logic Density Index scores",
        },
    ),
]


@dataclass
class OutputContext:
    """Context data for text output functions."""

    results: list
    duplicates: list
    dead_code_violations: list
    all_funcs: list
    cycle_stats: dict
    dup_stats: dict
    total_loc: int
    source_files: list


def _setup_arg_parser():
    """Create and configure the argument parser for vibelexity CLI."""
    parser = argparse.ArgumentParser(
        prog="vibelexity",
        description="Analyze cognitive complexity of Python, TypeScript, and Go source files.",
    )
    for arg in _CLI_ARGUMENTS:
        parser.add_argument(arg[0], **arg[1])
    return parser


def _validate_and_collect_files(args):
    """Validate the input path and collect source files."""
    path = Path(args.path)
    if path.is_file():
        if path.suffix.lower() not in _SUPPORTED_EXTENSIONS:
            print(f"Error: unsupported file extension: {args.path}", file=sys.stderr)
            sys.exit(2)
        source_files = [path]
    elif path.is_dir():
        source_files = _collect_source_files(path)
    else:
        print(f"Error: not a file or directory: {args.path}", file=sys.stderr)
        sys.exit(2)

    if not source_files:
        print(f"Error: no supported source files found in {args.path}", file=sys.stderr)
        sys.exit(2)

    return source_files


def _perform_analysis(source_files, args):
    """Perform analysis cycles and dead code detection based on flags."""
    config = {"ts_module_resolution": args.ts_module_resolution}

    analysis_data = {}

    run_all = not any(
        [
            args.show_dead,
            args.show_bad_patterns,
            args.show_dupes,
            args.show_circulars,
            args.show_fatties,
            args.worst_cog,
            args.worst_npath,
            args.worst_dtd,
            args.worst_cc,
            args.worst_mi,
            args.worst_ldi,
        ]
    )

    if args.show_circulars or run_all:
        if args.algorithm == "tarjan":
            analysis_data["cycles"] = find_cycles_tarjan(source_files, config=config)
        else:
            analysis_data["cycles"] = find_cycles(source_files, config=config)
    else:
        analysis_data["cycles"] = None

    if args.show_dead or run_all:
        analysis_data["dead_code"] = find_dead_code(source_files)
    else:
        analysis_data["dead_code"] = None

    if (
        args.show_bad_patterns
        or args.show_dupes
        or args.show_fatties
        or args.worst_cog
        or args.worst_npath
        or args.worst_dtd
        or args.worst_cc
        or args.worst_mi
        or args.worst_ldi
        or run_all
    ):
        analysis_data["results"] = _analyze_files(source_files)
    else:
        analysis_data["results"] = None

    if args.show_dupes or run_all:
        results = analysis_data.get("results")
        analysis_data["duplicates"] = find_duplicates(results) if results else []
    else:
        analysis_data["duplicates"] = None

    return analysis_data


def _any_show_flag(args):
    """Check if any --show-* flag is set."""
    show_flags = [
        args.show_dead,
        args.show_bad_patterns,
        args.show_dupes,
        args.show_circulars,
        args.show_fatties,
    ]
    return any(show_flags)


def _run_call_graph_cli(source_files, args):
    """Run call graph generation."""
    path = Path(args.path)
    base_path = path.parent if path.is_file() else path
    graph = build_call_graph(source_files)
    print(format_dot(graph, base_path=base_path))


def _build_output_context(analysis_data, all_funcs, source_files):
    """Build the output context for text printing."""
    results = analysis_data.get("results")
    duplicates = analysis_data.get("duplicates")
    cycles = analysis_data.get("cycles")
    dead_code_violations = analysis_data.get("dead_code")

    cycle_stats = _compute_cycle_stats(cycles) if cycles else {}
    total_loc = sum(r.loc for r in results) if results else 0
    dup_stats = (
        _compute_duplication_stats(duplicates, all_funcs, total_loc)
        if duplicates
        else {}
    )

    return OutputContext(
        results=results or [],
        duplicates=duplicates or [],
        dead_code_violations=dead_code_violations or [],
        all_funcs=all_funcs or [],
        cycle_stats=cycle_stats,
        dup_stats=dup_stats,
        total_loc=total_loc,
        source_files=source_files,
    )


def _print_basic_stats(args, ctx):
    """Print basic statistics outputs."""
    from vibelexity.output.rich_output import (
        print_summary_table,
        print_statistics_table,
        print_cycle_stats_table,
    )

    print_summary_table(ctx.results, ctx.all_funcs, ctx.total_loc)
    print_statistics_table(ctx.results, ctx.all_funcs)
    print_cycle_stats_table(ctx.cycle_stats, show_circulars=args.show_circulars)


def _print_analysis_details(args, ctx):
    """Print detailed analysis outputs."""
    from vibelexity.metrics.fatness import compute_fatness as _compute_fatness

    print_pattern_violations_table(
        ctx.results, show_bad_patterns=args.show_bad_patterns
    )
    print_duplication_table(
        ctx.duplicates,
        show_dupes=args.show_dupes,
        dup_stats=ctx.dup_stats,
        all_funcs=ctx.all_funcs,
        total_loc=ctx.total_loc,
    )
    print_dead_code_table(ctx.dead_code_violations, show_dead=args.show_dead)

    call_graph = build_call_graph(ctx.source_files) if ctx.source_files else None
    fatness_report = _compute_fatness(ctx.results, call_graph=call_graph)
    print_fatness_table(fatness_report, show_fatties=args.show_fatties)

    if not False:
        candidate_lists = _build_candidate_lists(ctx.all_funcs)
        print_refactor_candidates_table(candidate_lists)


def _print_full_text_output(args, ctx):
    """Print the full text output for the CLI."""
    _print_basic_stats(args, ctx)
    _print_analysis_details(args, ctx)


def _print_text_output(args, analysis_data, all_funcs, source_files):
    """Print text output for the CLI."""
    ctx = _build_output_context(analysis_data, all_funcs, source_files)

    if _check_single_output_flags(args, ctx):
        return True

    _print_full_text_output(args, ctx)
    return False


def _check_single_output_flags(args, ctx):
    """Check if any single output flag is set and handle it."""

    if args.show_dead:
        print(format_dead_code_text(ctx.dead_code_violations or []))
        print_dead_code_table(ctx.dead_code_violations, show_dead=True)
        return True

    if args.show_bad_patterns:
        print_pattern_violations_table(ctx.results, show_bad_patterns=True)
        return True

    if args.show_dupes:
        print_duplication_table(
            ctx.duplicates,
            show_dupes=True,
            dup_stats=ctx.dup_stats,
            all_funcs=ctx.all_funcs,
            total_loc=ctx.total_loc,
        )
        return True

    if args.show_circulars:
        print_cycle_stats_table(ctx.cycle_stats, show_circulars=True)
        return True

    if args.show_fatties:
        call_graph = build_call_graph(ctx.source_files) if ctx.source_files else None
        from vibelexity.metrics.fatness import compute_fatness

        fatness_report = compute_fatness(ctx.results, call_graph=call_graph)
        print_fatness_table(fatness_report, show_fatties=True)
        return True

    if args.worst_cog:
        print_worst_metrics_table("complexity", ctx.all_funcs)
        return True

    if args.worst_npath:
        print_worst_metrics_table("npath", ctx.all_funcs)
        return True

    if args.worst_dtd:
        print_worst_metrics_table("dtd", ctx.all_funcs)
        return True

    if args.worst_cc:
        print_worst_metrics_table("cyclomatic", ctx.all_funcs)
        return True

    if args.worst_mi:
        print_worst_metrics_table("mi", ctx.all_funcs)
        return True

    if args.worst_ldi:
        print_worst_metrics_table("ldi", ctx.all_funcs)
        return True

    return False


def _print_json_output(args, analysis_data, source_files):
    """Print JSON output for the CLI."""
    from vibelexity.output.json import _print_json

    config = JsonOutputConfig(
        results=analysis_data["results"],
        duplicates=analysis_data["duplicates"],
        cycles=analysis_data["cycles"],
        show_all=False,
        show_bottom=False,
        dead_code_violations=analysis_data["dead_code"],
        source_files=source_files,
    )
    _print_json(config)


def _run_analysis_and_print(source_files, args):
    """Run analysis and print output based on args."""
    analysis_data = _perform_analysis(source_files, args)
    all_funcs = (
        _all_functions(analysis_data.get("results") or [])
        if analysis_data.get("results")
        else []
    )

    if args.json_output:
        _print_json_output(args, analysis_data, source_files)
    else:
        _print_text_output(args, analysis_data, all_funcs, source_files)


def cli():
    """Main CLI entry point for vibelexity."""
    parser = _setup_arg_parser()
    args = parser.parse_args()

    source_files = _validate_and_collect_files(args)

    if args.call_graph:
        _run_call_graph_cli(source_files, args)
        return

    _run_analysis_and_print(source_files, args)


if __name__ == "__main__":
    cli()
