"""Shared analysis logic for all language analyzers."""

from __future__ import annotations

from dataclasses import dataclass
from pathlib import Path
from typing import TYPE_CHECKING, Callable

if TYPE_CHECKING:
    from tree_sitter import Node
    from vibelexity.metrics.duplication.config import LanguageConfig

from vibelexity.models import (
    FileComplexity,
    FunctionComplexity,
    HalsteadMetrics,
    PatternViolation,
)

if TYPE_CHECKING:
    # Callable type aliases (defined inside TYPE_CHECKING to see imported types)
    ParseFn = Callable[[str], Node]
    CollectFunctionsFn = Callable[[Node], list[Node]]
    FuncNameFn = Callable[[Node], str]
    ParamCountFn = Callable[[Node], int]
    ComputeCognitiveFn = Callable[[Node, str], int]
    ComputeHalsteadFn = Callable[[Node], HalsteadMetrics]
    ComputeNPathFn = Callable[[Node], int]
    ComputeDTDFn = Callable[[Node], int]
    ComputeCyclomaticFn = Callable[[Node], int]
    CountLlocFn = Callable[[Node], int]


@dataclass
class AnalyzerConfig:
    parse_fn: Callable[[str], Node]
    collect_functions_fn: Callable[[Node], list[Node]]
    func_name_fn: Callable[[Node], str]
    func_param_count_fn: Callable[[Node], int]
    compute_cognitive_fn: Callable[[Node, str], int]
    compute_halstead_fn: Callable[[Node], HalsteadMetrics]
    compute_npath_fn: Callable[[Node], int]
    compute_dtd_fn: Callable[[Node], int]
    compute_cyclomatic_fn: Callable[[Node], int]
    count_lloc_fn: Callable[[Node], int]
    dup_config: LanguageConfig
    check_patterns_fn: (
        Callable[[Node, list[str] | None, str | None], list[PatternViolation]] | None
    ) = None


from vibelexity.common import (
    _compute_loc,
    _compute_sloc,
    compute_maintainability_index,
    compute_ldi,
)
from vibelexity.metrics.duplication.core import (
    compute_type1_hash,
    compute_type2_hash,
    count_statements,
)


def analyze_source_generic(
    code: str,
    config: AnalyzerConfig,
) -> FileComplexity:
    """Shared analysis loop: parse, collect functions, compute all metrics."""
    root = config.parse_fn(code)
    functions = config.collect_functions_fn(root)
    result = FileComplexity(path="<string>")
    result.loc = _compute_loc(code)
    result.lloc = config.count_lloc_fn(root)
    result.sloc = _compute_sloc(code, root)
    if config.check_patterns_fn is not None:
        result.pattern_violations = config.check_patterns_fn(root, None, code)

    for func_node in functions:
        name = config.func_name_fn(func_node)
        line = func_node.start_point[0] + 1
        func_loc = func_node.end_point[0] - func_node.start_point[0] + 1

        complexity = config.compute_cognitive_fn(func_node, name)
        halstead = config.compute_halstead_fn(func_node)
        npath = config.compute_npath_fn(func_node)
        dtd = config.compute_dtd_fn(func_node)
        cyclomatic = config.compute_cyclomatic_fn(func_node)
        mi = compute_maintainability_index(halstead.volume, cyclomatic, func_loc)
        ldi = compute_ldi(
            halstead.volume, halstead.vocabulary, halstead.n2, halstead.difficulty
        )
        t1 = compute_type1_hash(func_node, config.dup_config)
        t2 = compute_type2_hash(func_node, config.dup_config)
        stmts = count_statements(func_node)

        result.functions.append(
            FunctionComplexity(
                name=name,
                line=line,
                complexity=complexity,
                halstead=halstead,
                npath=npath,
                dtd=dtd,
                mi=mi,
                ldi=ldi,
                cyclomatic=cyclomatic,
                type1_hash=t1,
                type2_hash=t2,
                func_loc=func_loc,
                stmt_count=stmts,
                param_count=config.func_param_count_fn(func_node),
            )
        )
    return result
