"""Entryfile module for models."""

from __future__ import annotations

import logging
import re
from dataclasses import dataclass
from functools import cached_property
from pathlib import Path
from typing import Final

logger = logging.getLogger(__name__)

# GUI framework keywords for detection
_GUI_FRAMEWORKS: Final[frozenset[str]] = frozenset(
    (
        "pyside2",
        "pyqt5",
        "pyside6",
        "pyqt6",
        "tkinter",
        "wx",
        "kivy",
        "pygame",
    )
)

# CLI framework keywords for detection
_CLI_FRAMEWORKS: Final[frozenset[str]] = frozenset(
    (
        "argparse",
        "click",
        "typer",
        "fire",
        "docopt",
    )
)


@dataclass(frozen=True)
class EntryFile:
    """Represents a Python entry file with analysis capabilities.

    An entry file is a Python file that serves as an application entry point,
    typically containing a main() function or __main__ guard.

    Attributes
    ----------
        project_name: Name of the project this entry file belongs to
        source_file: Path to the Python source file
        project_root: Root directory of the project (optional)
    """

    project_name: str
    source_file: Path
    project_root: Path | None = None

    @cached_property
    def entry_name(self) -> str:
        """Get entry file name (stem without extension)."""
        return self.source_file.stem

    @cached_property
    def module_name(self) -> str:
        """Get module name from source file stem (hyphens replaced with underscores)."""
        return self.source_file.stem.replace("-", "_")

    @cached_property
    def is_gui(self) -> bool:
        """Determine if this entry file is a GUI application.

        Entry file is considered GUI if:
        1. File name contains 'gui' (e.g., docscan_gui.py, myapp-gui.py)
        2. File imports GUI frameworks (PySide2, PyQt5, PyQt6, tkinter, etc.)
        3. File contains Qt application patterns (QApplication, QMainWindow, etc.)

        Returns
        -------
            True if entry file is a GUI application, False otherwise
        """
        # Check file name for 'gui' keyword (highest priority)
        if "gui" in self.source_file.stem.lower():
            return True

        # Check file content for GUI framework imports and patterns
        try:
            content = self.source_file.read_text(encoding="utf-8")

            # Check for explicit GUI framework imports (ignore relative imports)
            gui_imports = any(
                (f"import {framework}" in content.lower() or f"from {framework}" in content.lower())
                and f"from .{framework}" not in content.lower()
                and f"from ..{framework}" not in content.lower()
                for framework in _GUI_FRAMEWORKS
            )

            # Check for Qt application patterns (exclude comments)
            qt_patterns = [
                "QApplication",
                "QMainWindow",
                "QWidget",
                "QDialog",
                "QFrame",
                "QLabel",
                "QPushButton",
                "QLineEdit",
                "QTextEdit",
                "QComboBox",
                "QListWidget",
                "QTreeWidget",
            ]
            has_qt_patterns = False
            for pattern in qt_patterns:
                matches = re.finditer(re.escape(pattern), content)
                for match in matches:
                    # Check if this occurrence is in a comment
                    line_start = content.rfind("\n", 0, match.start()) + 1
                    line_end = content.find("\n", match.end())
                    if line_end == -1:
                        line_end = len(content)
                    line = content[line_start:line_end]

                    # If not in comment, this is a real Qt pattern
                    if not line.lstrip().startswith("#"):
                        has_qt_patterns = True
                        break
                if has_qt_patterns:
                    break

            # Check for GUI widget creation patterns
            gui_patterns = ["app = QApplication", ".show()", "window =", "dialog ="]
            has_gui_patterns = any(pattern in content for pattern in gui_patterns)

            return gui_imports or has_qt_patterns or has_gui_patterns

        except Exception as e:
            logger.debug(f"Failed to analyze {self.source_file} for GUI indicators: {e}")
            return False

    @cached_property
    def is_cli(self) -> bool:
        """Determine if this entry file is a CLI application.

        Entry file is considered CLI if:
        1. Contains CLI framework imports (argparse, click, typer, etc.)
        2. Has argument parsing patterns
        3. Contains main function or __main__ guard
        4. Does NOT contain GUI framework imports or patterns

        Returns
        -------
            True if entry file is a CLI application, False otherwise
        """
        # First check if it's clearly a GUI application
        if self.is_gui:
            return False

        try:
            content = self.source_file.read_text(encoding="utf-8")

            # Check for CLI framework imports
            cli_imports = any(
                f"import {framework}" in content.lower() or f"from {framework}" in content.lower()
                for framework in _CLI_FRAMEWORKS
            )

            # Check for argument parsing
            has_arg_parsing = (
                "ArgumentParser" in content
                or ".command(" in content  # click/typer
                or "@app.command" in content  # typer
                or "sys.argv" in content
            )

            # Check for main patterns
            has_main_entry = (
                "def main(" in content
                or "main()" in content
                or 'if __name__ == "__main__"' in content
                or "if __name__ == '__main__':" in content
            )

            # Additional CLI indicators
            cli_patterns = ["parse_args", "add_argument", "click.command", "typer.run"]
            has_cli_patterns = any(pattern in content for pattern in cli_patterns)

            return (cli_imports and has_main_entry) or (has_arg_parsing and has_main_entry) or has_cli_patterns

        except Exception as e:
            logger.debug(f"Failed to analyze {self.source_file} for CLI indicators: {e}")
            return False

    @cached_property
    def loader_type(self) -> str:
        """Determine the appropriate loader type for this entry file.

        Returns
        -------
            'gui' if GUI application, 'console' if CLI application, 'console' as default
        """
        if self.is_gui:
            return "gui"
        if self.is_cli:
            return "console"
        # Default to console for unknown entry file types
        return "console"

    @cached_property
    def file_size(self) -> int:
        """Get the size of the entry file in bytes."""
        try:
            return self.source_file.stat().st_size
        except OSError:
            return 0

    @cached_property
    def dependencies(self) -> set[str]:
        """Extract imported dependencies from the entry file.

        Returns
        -------
            Set of imported package names
        """
        try:
            content = self.source_file.read_text(encoding="utf-8")

            # Match import statements
            import_patterns = [
                r"^import\s+([a-zA-Z_][a-zA-Z0-9_]*)",
                r"^from\s+([a-zA-Z_][a-zA-Z0-9_]*)\s+import",
                r"import\s+([a-zA-Z_][a-zA-Z0-9_]*)(?=\s*,|\s*$)",
            ]

            dependencies = set()
            for pattern in import_patterns:
                matches = re.finditer(pattern, content, re.MULTILINE)
                for match in matches:
                    dep = match.group(1)
                    if dep not in {
                        "os",
                        "sys",
                        "json",
                        "re",
                        "pathlib",
                        "logging",
                        "argparse",
                    }:
                        dependencies.add(dep.lower())

            return dependencies
        except Exception as e:
            logger.debug(f"Failed to extract dependencies from {self.source_file}: {e}")
            return set()

    @cached_property
    def complexity_score(self) -> int:
        """Calculate a rough complexity score based on code structure.

        Returns
        -------
            Integer score representing code complexity
        """
        try:
            content = self.source_file.read_text(encoding="utf-8")

            # Count various complexity indicators
            scores = {
                "functions": content.count("def "),
                "classes": content.count("class "),
                "loops": content.count("for ") + content.count("while "),
                "conditionals": content.count("if ") + content.count("elif ") + content.count("else:"),
                "imports": content.count("import "),
                "lines": len(content.splitlines()),
            }

            # Weighted complexity calculation
            return (
                scores["functions"] * 3
                + scores["classes"] * 5
                + scores["loops"] * 2
                + scores["conditionals"] * 1
                + scores["imports"] * 1
                + scores["lines"] // 10  # Every 10 lines adds 1 point
            )

        except Exception as e:
            logger.debug(f"Failed to calculate complexity for {self.source_file}: {e}")
            return 0

    @cached_property
    def has_main_guard(self) -> bool:
        """Check if the entry file has a __main__ guard.

        Returns
        -------
            True if file contains if __name__ == '__main__' pattern
        """
        try:
            content = self.source_file.read_text(encoding="utf-8")
            return 'if __name__ == "__main__"' in content or "if __name__ == '__main__':" in content
        except Exception:
            return False

    @cached_property
    def has_main_function(self) -> bool:
        """Check if the entry file has a main() function.

        Returns
        -------
            True if file contains def main( pattern
        """
        try:
            content = self.source_file.read_text(encoding="utf-8")
            return "def main(" in content
        except Exception:
            return False

    def __repr__(self) -> str:
        """Return a string representation of the EntryFile instance."""
        return (
            f"<EntryFile project_name={self.project_name} "
            f"entry_name={self.entry_name} "
            f"type={self.loader_type} "
            f"complexity={self.complexity_score} "
            f"deps={len(self.dependencies)} "
            f"path={self.source_file}>"
        )
