"""MFC (Microsoft Foundation Classes) specialized parser.

This module extends the C++ parser with MFC-specific constructs like
message maps, dialog classes, and MFC macros.
"""

from __future__ import annotations

import re
from pathlib import Path
from typing import Optional

from ieum.ast.base import (
    Symbol,
    SymbolType,
    Reference,
    CodeRange,
    ParseResult,
    Visibility,
)
from ieum.ast.cpp_parser import CppASTParser


class MFCParser(CppASTParser):
    """MFC-specialized C++ parser."""

    LANGUAGE = "mfc"

    # MFC base class patterns
    MFC_BASE_CLASSES = {
        "CWinApp": "application",
        "CFrameWnd": "frame_window",
        "CMDIFrameWnd": "mdi_frame",
        "CMDIChildWnd": "mdi_child",
        "CDialog": "dialog",
        "CDialogEx": "dialog",
        "CPropertyPage": "property_page",
        "CPropertySheet": "property_sheet",
        "CView": "view",
        "CScrollView": "scroll_view",
        "CFormView": "form_view",
        "CListView": "list_view",
        "CTreeView": "tree_view",
        "CDocument": "document",
        "CWnd": "window",
        "CButton": "control",
        "CEdit": "control",
        "CListCtrl": "control",
        "CTreeCtrl": "control",
        "CComboBox": "control",
        "CStatic": "control",
    }

    # MFC message handlers
    MFC_MESSAGE_HANDLERS = {
        "ON_WM_CREATE": "WM_CREATE",
        "ON_WM_DESTROY": "WM_DESTROY",
        "ON_WM_PAINT": "WM_PAINT",
        "ON_WM_SIZE": "WM_SIZE",
        "ON_WM_CLOSE": "WM_CLOSE",
        "ON_WM_TIMER": "WM_TIMER",
        "ON_WM_LBUTTONDOWN": "WM_LBUTTONDOWN",
        "ON_WM_LBUTTONUP": "WM_LBUTTONUP",
        "ON_WM_RBUTTONDOWN": "WM_RBUTTONDOWN",
        "ON_WM_MOUSEMOVE": "WM_MOUSEMOVE",
        "ON_WM_KEYDOWN": "WM_KEYDOWN",
        "ON_WM_KEYUP": "WM_KEYUP",
        "ON_WM_CHAR": "WM_CHAR",
        "ON_WM_HSCROLL": "WM_HSCROLL",
        "ON_WM_VSCROLL": "WM_VSCROLL",
        "ON_WM_ERASEBKGND": "WM_ERASEBKGND",
        "ON_WM_CTLCOLOR": "WM_CTLCOLOR",
        "ON_WM_SETFOCUS": "WM_SETFOCUS",
        "ON_WM_KILLFOCUS": "WM_KILLFOCUS",
    }

    def parse_source(self, source: str, file_path: str = "<string>") -> ParseResult:
        """Parse MFC source code.

        Args:
            source: MFC/C++ source code.
            file_path: Virtual file path.

        Returns:
            ParseResult with MFC-specific symbols.
        """
        # First, do standard C++ parsing
        result = super().parse_source(source, file_path)
        result.language = self.LANGUAGE

        # Add MFC-specific parsing
        self._parse_message_maps(source, result)
        self._parse_mfc_macros(source, result)
        self._classify_mfc_classes(result)
        self._extract_control_ids(source, result)

        return result

    def _parse_message_maps(self, source: str, result: ParseResult) -> None:
        """Parse MFC message maps.

        Args:
            source: Source code.
            result: ParseResult to populate.
        """
        # Find BEGIN_MESSAGE_MAP / END_MESSAGE_MAP blocks
        message_map_pattern = re.compile(
            r"BEGIN_MESSAGE_MAP\s*\(\s*([\w]+)\s*,\s*([\w]+)\s*\)"
            r"(.*?)"
            r"END_MESSAGE_MAP\s*\(\s*\)",
            re.DOTALL
        )

        for match in message_map_pattern.finditer(source):
            class_name = match.group(1)
            base_class = match.group(2)
            map_content = match.group(3)
            start_line = source[:match.start()].count("\n") + 1
            end_line = source[:match.end()].count("\n") + 1

            # Create message map symbol
            result.symbols.append(Symbol(
                name=f"{class_name}_MESSAGE_MAP",
                type=SymbolType.MESSAGE_MAP,
                file_path=result.file_path,
                range=CodeRange(
                    start_line=start_line,
                    start_column=0,
                    end_line=end_line,
                    end_column=0,
                ),
                parent=class_name,
                metadata={
                    "base_class": base_class,
                    "handlers": [],
                },
            ))

            # Parse individual message handlers
            self._parse_message_handlers(map_content, class_name, result, start_line)

    def _parse_message_handlers(
        self,
        content: str,
        class_name: str,
        result: ParseResult,
        base_line: int,
    ) -> None:
        """Parse individual message handlers in a message map.

        Args:
            content: Message map content.
            class_name: Parent class name.
            result: ParseResult to populate.
            base_line: Starting line number.
        """
        # ON_COMMAND pattern
        command_pattern = re.compile(
            r"ON_COMMAND\s*\(\s*([\w]+)\s*,\s*&?([\w:]+)\s*\)"
        )

        # ON_BN_CLICKED and similar
        notify_pattern = re.compile(
            r"ON_(\w+)\s*\(\s*([\w]+)\s*,\s*&?([\w:]+)\s*\)"
        )

        # Standard message handlers (ON_WM_*)
        wm_pattern = re.compile(r"(ON_WM_\w+)\s*\(\s*\)")

        # ON_MESSAGE pattern
        message_pattern = re.compile(
            r"ON_MESSAGE\s*\(\s*([\w]+)\s*,\s*&?([\w:]+)\s*\)"
        )

        lines = content.split("\n")

        for i, line in enumerate(lines):
            line_num = base_line + i

            # Check ON_COMMAND
            for match in command_pattern.finditer(line):
                cmd_id = match.group(1)
                handler = match.group(2)
                result.symbols.append(Symbol(
                    name=handler.split("::")[-1],
                    type=SymbolType.MESSAGE_HANDLER,
                    file_path=result.file_path,
                    range=CodeRange(
                        start_line=line_num,
                        start_column=match.start(),
                        end_line=line_num,
                        end_column=match.end(),
                    ),
                    parent=class_name,
                    metadata={
                        "message_type": "ON_COMMAND",
                        "command_id": cmd_id,
                    },
                ))

            # Check notification handlers
            for match in notify_pattern.finditer(line):
                notify_type = match.group(1)
                control_id = match.group(2)
                handler = match.group(3)

                if notify_type.startswith("WM_"):
                    continue  # Skip, handled by wm_pattern

                result.symbols.append(Symbol(
                    name=handler.split("::")[-1],
                    type=SymbolType.MESSAGE_HANDLER,
                    file_path=result.file_path,
                    range=CodeRange(
                        start_line=line_num,
                        start_column=match.start(),
                        end_line=line_num,
                        end_column=match.end(),
                    ),
                    parent=class_name,
                    metadata={
                        "message_type": f"ON_{notify_type}",
                        "control_id": control_id,
                    },
                ))

            # Check WM_* handlers
            for match in wm_pattern.finditer(line):
                handler_macro = match.group(1)
                message = self.MFC_MESSAGE_HANDLERS.get(handler_macro, handler_macro)

                # Derive handler name from message
                handler_name = "On" + "".join(
                    word.capitalize()
                    for word in message.replace("WM_", "").split("_")
                )

                result.symbols.append(Symbol(
                    name=handler_name,
                    type=SymbolType.MESSAGE_HANDLER,
                    file_path=result.file_path,
                    range=CodeRange(
                        start_line=line_num,
                        start_column=match.start(),
                        end_line=line_num,
                        end_column=match.end(),
                    ),
                    parent=class_name,
                    metadata={
                        "message_type": handler_macro,
                        "windows_message": message,
                    },
                ))

            # Check ON_MESSAGE
            for match in message_pattern.finditer(line):
                msg_id = match.group(1)
                handler = match.group(2)
                result.symbols.append(Symbol(
                    name=handler.split("::")[-1],
                    type=SymbolType.MESSAGE_HANDLER,
                    file_path=result.file_path,
                    range=CodeRange(
                        start_line=line_num,
                        start_column=match.start(),
                        end_line=line_num,
                        end_column=match.end(),
                    ),
                    parent=class_name,
                    metadata={
                        "message_type": "ON_MESSAGE",
                        "message_id": msg_id,
                    },
                ))

    def _parse_mfc_macros(self, source: str, result: ParseResult) -> None:
        """Parse MFC declaration macros.

        Args:
            source: Source code.
            result: ParseResult to populate.
        """
        # DECLARE_DYNAMIC / IMPLEMENT_DYNAMIC
        dynamic_pattern = re.compile(
            r"(DECLARE|IMPLEMENT)_(DYNAMIC|DYNCREATE|SERIAL)\s*\(\s*([\w]+)"
        )

        for match in dynamic_pattern.finditer(source):
            macro_type = match.group(1)
            macro_variant = match.group(2)
            class_name = match.group(3)
            line = source[:match.start()].count("\n") + 1

            # Find corresponding class and update metadata
            for symbol in result.symbols:
                if symbol.name == class_name and symbol.type == SymbolType.CLASS:
                    if "mfc_macros" not in symbol.metadata:
                        symbol.metadata["mfc_macros"] = []
                    symbol.metadata["mfc_macros"].append(f"{macro_type}_{macro_variant}")

        # DDX/DDV data exchange
        ddx_pattern = re.compile(
            r"(DDX|DDV)_(\w+)\s*\(\s*pDX\s*,\s*([\w]+)\s*,\s*([\w.]+)"
        )

        for match in ddx_pattern.finditer(source):
            exchange_type = match.group(1)
            data_type = match.group(2)
            control_id = match.group(3)
            member_var = match.group(4)
            line = source[:match.start()].count("\n") + 1

            result.symbols.append(Symbol(
                name=member_var,
                type=SymbolType.VARIABLE,
                file_path=result.file_path,
                range=CodeRange(
                    start_line=line,
                    start_column=match.start(),
                    end_line=line,
                    end_column=match.end(),
                ),
                metadata={
                    "ddx_type": f"{exchange_type}_{data_type}",
                    "control_id": control_id,
                },
            ))

    def _classify_mfc_classes(self, result: ParseResult) -> None:
        """Classify classes based on MFC base classes.

        Args:
            result: ParseResult to update.
        """
        for symbol in result.symbols:
            if symbol.type in (SymbolType.CLASS, SymbolType.STRUCT):
                for base in symbol.base_classes:
                    base_name = base.split("::")[-1].strip()
                    if base_name in self.MFC_BASE_CLASSES:
                        mfc_type = self.MFC_BASE_CLASSES[base_name]
                        symbol.metadata["mfc_class_type"] = mfc_type

                        # Update symbol type for specific MFC classes
                        if mfc_type == "dialog":
                            symbol.type = SymbolType.DIALOG_CLASS
                        elif mfc_type == "view":
                            symbol.type = SymbolType.VIEW_CLASS
                        elif mfc_type == "document":
                            symbol.type = SymbolType.DOCUMENT_CLASS
                        break

    def _extract_control_ids(self, source: str, result: ParseResult) -> None:
        """Extract control IDs from resource definitions.

        Args:
            source: Source code.
            result: ParseResult to populate.
        """
        # #define ID patterns
        id_pattern = re.compile(
            r"#define\s+(ID[A-Z]?_[\w]+)\s+(\d+)"
        )

        for match in id_pattern.finditer(source):
            id_name = match.group(1)
            id_value = match.group(2)
            line = source[:match.start()].count("\n") + 1

            result.symbols.append(Symbol(
                name=id_name,
                type=SymbolType.CONSTANT,
                file_path=result.file_path,
                range=CodeRange(
                    start_line=line,
                    start_column=0,
                    end_line=line,
                    end_column=len(match.group(0)),
                ),
                metadata={
                    "id_value": int(id_value),
                    "id_category": self._categorize_id(id_name),
                },
            ))

    def _categorize_id(self, id_name: str) -> str:
        """Categorize an ID based on its prefix.

        Args:
            id_name: The ID name.

        Returns:
            Category string.
        """
        prefixes = {
            "IDC_": "control",
            "IDM_": "menu",
            "IDD_": "dialog",
            "IDR_": "resource",
            "IDS_": "string",
            "IDB_": "bitmap",
            "IDI_": "icon",
            "ID_": "command",
        }

        for prefix, category in prefixes.items():
            if id_name.startswith(prefix):
                return category

        return "unknown"

    def get_message_handlers(self, class_name: str, source: str) -> list[Symbol]:
        """Get all message handlers for a class.

        Args:
            class_name: Name of the class.
            source: Source code.

        Returns:
            List of message handler symbols.
        """
        result = self.parse_source(source)
        return [
            s for s in result.symbols
            if s.type == SymbolType.MESSAGE_HANDLER and s.parent == class_name
        ]

    def get_mfc_classes(self, source: str) -> list[Symbol]:
        """Get all MFC-derived classes.

        Args:
            source: Source code.

        Returns:
            List of MFC class symbols.
        """
        result = self.parse_source(source)
        return [
            s for s in result.symbols
            if s.type in (SymbolType.CLASS, SymbolType.DIALOG_CLASS,
                         SymbolType.VIEW_CLASS, SymbolType.DOCUMENT_CLASS)
            and "mfc_class_type" in s.metadata
        ]

    def get_class_hierarchy(self, source: str) -> dict:
        """Build MFC class hierarchy.

        Args:
            source: Source code.

        Returns:
            Dictionary mapping class names to their info.
        """
        result = self.parse_source(source)
        hierarchy = {}

        for symbol in result.symbols:
            if symbol.type in (SymbolType.CLASS, SymbolType.STRUCT,
                              SymbolType.DIALOG_CLASS, SymbolType.VIEW_CLASS,
                              SymbolType.DOCUMENT_CLASS):
                hierarchy[symbol.name] = {
                    "base_classes": symbol.base_classes,
                    "type": symbol.type.value,
                    "mfc_type": symbol.metadata.get("mfc_class_type"),
                    "handlers": [
                        s.name for s in result.symbols
                        if s.type == SymbolType.MESSAGE_HANDLER
                        and s.parent == symbol.name
                    ],
                }

        return hierarchy
