"""
CPPY Converter - Python <-> C++ bidirectional code conversion.
Full implementation with struct, class, function, template support.
"""

import re
import ast
from pathlib import Path
from typing import Dict, List, Tuple, Optional, Set
from dataclasses import dataclass, field


# Python type to C++ type mapping
PY_TO_CPP_TYPES = {
    'int': 'int',
    'float': 'double',
    'str': 'std::string',
    'bool': 'bool',
    'bytes': 'std::vector<uint8_t>',
    'None': 'void',
    'any': 'auto',
    'Any': 'auto',
    'object': 'py::object',
    'list': 'std::vector',
    'List': 'std::vector',
    'dict': 'std::unordered_map',
    'Dict': 'std::unordered_map',
    'set': 'std::unordered_set',
    'Set': 'std::unordered_set',
    'tuple': 'std::tuple',
    'Tuple': 'std::tuple',
    'Optional': 'std::optional',
    'Union': 'std::variant',
    'Callable': 'std::function',
}

# C++ type to Python type mapping
CPP_TO_PY_TYPES = {
    'int': 'int',
    'long': 'int',
    'long long': 'int',
    'short': 'int',
    'unsigned int': 'int',
    'unsigned long': 'int',
    'size_t': 'int',
    'float': 'float',
    'double': 'float',
    'bool': 'bool',
    'char': 'str',
    'std::string': 'str',
    'string': 'str',
    'void': 'None',
    'auto': 'Any',
    'std::vector': 'list',
    'vector': 'list',
    'std::map': 'dict',
    'std::unordered_map': 'dict',
    'map': 'dict',
    'std::set': 'set',
    'std::unordered_set': 'set',
    'set': 'set',
    'std::tuple': 'tuple',
    'tuple': 'tuple',
    'std::optional': 'Optional',
    'optional': 'Optional',
    'std::variant': 'Union',
    'std::function': 'Callable',
}


@dataclass
class FunctionInfo:
    name: str
    return_type: str
    params: List[Tuple[str, str]]  # (name, type)
    body: str
    is_method: bool = False
    is_static: bool = False
    is_const: bool = False
    is_virtual: bool = False
    is_property: bool = False
    decorators: List[str] = field(default_factory=list)


@dataclass
class ClassInfo:
    name: str
    bases: List[str]
    methods: List[FunctionInfo]
    fields: List[Tuple[str, str, Optional[str]]]  # (name, type, default)
    is_struct: bool = False
    constructors: List[FunctionInfo] = field(default_factory=list)


@dataclass
class StructInfo:
    name: str
    fields: List[Tuple[str, str]]  # (name, type)


class PythonToCppConverter:
    def __init__(self):
        self.imports: Set[str] = set()
        self.forward_decls: Set[str] = set()

    def convert(self, source: str, module_name: str) -> Tuple[str, str]:
        """Convert Python source to C++ (.cpp and .h content)."""
        self.imports = set()
        self.forward_decls = set()

        try:
            tree = ast.parse(source)
        except SyntaxError as e:
            raise ValueError(f"Python syntax error: {e}")

        classes = []
        functions = []
        global_vars = []

        for node in ast.iter_child_nodes(tree):
            if isinstance(node, ast.ClassDef):
                classes.append(self._convert_class(node))
            elif isinstance(node, ast.FunctionDef) or isinstance(node, ast.AsyncFunctionDef):
                functions.append(self._convert_function(node))
            elif isinstance(node, ast.Assign):
                for target in node.targets:
                    if isinstance(target, ast.Name):
                        var_type = self._infer_type(node.value)
                        var_value = self._convert_expr(node.value)
                        global_vars.append((target.id, var_type, var_value))
            elif isinstance(node, ast.AnnAssign):
                if isinstance(node.target, ast.Name):
                    var_type = self._convert_type_annotation(node.annotation)
                    var_value = self._convert_expr(node.value) if node.value else None
                    global_vars.append((node.target.id, var_type, var_value))

        header = self._generate_header(module_name, classes, functions, global_vars)
        source_cpp = self._generate_source(module_name, classes, functions, global_vars)

        return source_cpp, header

    def _convert_class(self, node: ast.ClassDef) -> ClassInfo:
        bases = []
        for base in node.bases:
            if isinstance(base, ast.Name):
                bases.append(base.id)
            elif isinstance(base, ast.Attribute):
                bases.append(self._get_attr_name(base))

        methods = []
        constructors = []
        fields = []

        for item in node.body:
            if isinstance(item, ast.FunctionDef) or isinstance(item, ast.AsyncFunctionDef):
                func = self._convert_function(item, is_method=True)
                if func.name == '__init__':
                    func.name = node.name
                    constructors.append(func)
                    for stmt in item.body:
                        if isinstance(stmt, ast.Assign):
                            for target in stmt.targets:
                                if isinstance(target, ast.Attribute) and isinstance(target.value, ast.Name):
                                    if target.value.id == 'self':
                                        field_type = self._infer_type(stmt.value)
                                        field_val = self._convert_expr(stmt.value)
                                        fields.append((target.attr, field_type, field_val))
                        elif isinstance(stmt, ast.AnnAssign):
                            if isinstance(stmt.target, ast.Attribute) and isinstance(stmt.target.value, ast.Name):
                                if stmt.target.value.id == 'self':
                                    field_type = self._convert_type_annotation(stmt.annotation)
                                    field_val = self._convert_expr(stmt.value) if stmt.value else None
                                    fields.append((stmt.target.attr, field_type, field_val))
                elif func.name.startswith('__') and func.name.endswith('__'):
                    continue
                else:
                    methods.append(func)
            elif isinstance(item, ast.AnnAssign):
                if isinstance(item.target, ast.Name):
                    field_type = self._convert_type_annotation(item.annotation)
                    field_val = self._convert_expr(item.value) if item.value else None
                    fields.append((item.target.id, field_type, field_val))

        return ClassInfo(
            name=node.name,
            bases=bases,
            methods=methods,
            fields=fields,
            constructors=constructors
        )

    def _convert_function(self, node, is_method: bool = False) -> FunctionInfo:
        decorators = []
        is_static = False
        is_property = False

        for dec in node.decorator_list:
            if isinstance(dec, ast.Name):
                if dec.id == 'staticmethod':
                    is_static = True
                elif dec.id == 'classmethod':
                    is_static = True
                elif dec.id == 'property':
                    is_property = True
                decorators.append(dec.id)

        params = []
        for arg in node.args.args:
            if arg.arg == 'self' or arg.arg == 'cls':
                continue
            if arg.annotation:
                param_type = self._convert_type_annotation(arg.annotation)
            else:
                param_type = 'auto'
            params.append((arg.arg, param_type))

        if node.returns:
            return_type = self._convert_type_annotation(node.returns)
        else:
            return_type = self._infer_return_type(node)

        body = self._convert_body(node.body)

        return FunctionInfo(
            name=node.name,
            return_type=return_type,
            params=params,
            body=body,
            is_method=is_method,
            is_static=is_static,
            is_property=is_property,
            decorators=decorators
        )

    def _convert_type_annotation(self, node) -> str:
        if node is None:
            return 'auto'

        if isinstance(node, ast.Name):
            py_type = node.id
            return PY_TO_CPP_TYPES.get(py_type, py_type)

        elif isinstance(node, ast.Subscript):
            if isinstance(node.value, ast.Name):
                container = node.value.id
                cpp_container = PY_TO_CPP_TYPES.get(container, container)

                if isinstance(node.slice, ast.Tuple):
                    inner_types = [self._convert_type_annotation(elt) for elt in node.slice.elts]
                    return f"{cpp_container}<{', '.join(inner_types)}>"
                else:
                    inner_type = self._convert_type_annotation(node.slice)
                    return f"{cpp_container}<{inner_type}>"

        elif isinstance(node, ast.Constant):
            if node.value is None:
                return 'void'
            return str(type(node.value).__name__)

        elif isinstance(node, ast.Attribute):
            return self._get_attr_name(node)

        elif isinstance(node, ast.BinOp) and isinstance(node.op, ast.BitOr):
            left = self._convert_type_annotation(node.left)
            right = self._convert_type_annotation(node.right)
            return f"std::variant<{left}, {right}>"

        return 'auto'

    def _infer_type(self, node) -> str:
        if node is None:
            return 'auto'

        if isinstance(node, ast.Constant):
            val = node.value
            if isinstance(val, bool):
                return 'bool'
            elif isinstance(val, int):
                return 'int'
            elif isinstance(val, float):
                return 'double'
            elif isinstance(val, str):
                return 'std::string'
            elif val is None:
                return 'void'

        elif isinstance(node, ast.List):
            if node.elts:
                inner = self._infer_type(node.elts[0])
                return f'std::vector<{inner}>'
            return 'std::vector<auto>'

        elif isinstance(node, ast.Dict):
            if node.keys and node.values:
                key_type = self._infer_type(node.keys[0])
                val_type = self._infer_type(node.values[0])
                return f'std::unordered_map<{key_type}, {val_type}>'
            return 'std::unordered_map<auto, auto>'

        elif isinstance(node, ast.Set):
            if node.elts:
                inner = self._infer_type(node.elts[0])
                return f'std::unordered_set<{inner}>'
            return 'std::unordered_set<auto>'

        elif isinstance(node, ast.Tuple):
            if node.elts:
                types = [self._infer_type(elt) for elt in node.elts]
                return f'std::tuple<{", ".join(types)}>'
            return 'std::tuple<>'

        elif isinstance(node, ast.Call):
            if isinstance(node.func, ast.Name):
                func_name = node.func.id
                return PY_TO_CPP_TYPES.get(func_name, func_name)

        elif isinstance(node, ast.BinOp):
            left_type = self._infer_type(node.left)
            right_type = self._infer_type(node.right)
            if 'double' in (left_type, right_type):
                return 'double'
            if 'std::string' in (left_type, right_type):
                return 'std::string'
            return left_type

        elif isinstance(node, ast.Compare):
            return 'bool'

        elif isinstance(node, ast.BoolOp):
            return 'bool'

        elif isinstance(node, ast.UnaryOp):
            if isinstance(node.op, ast.Not):
                return 'bool'
            return self._infer_type(node.operand)

        return 'auto'

    def _infer_return_type(self, node) -> str:
        for stmt in ast.walk(node):
            if isinstance(stmt, ast.Return) and stmt.value:
                return self._infer_type(stmt.value)
        return 'void'

    def _convert_body(self, stmts: List, indent: int = 1) -> str:
        lines = []
        ind = '    ' * indent

        for stmt in stmts:
            lines.extend(self._convert_stmt(stmt, indent))

        return '\n'.join(lines)

    def _convert_stmt(self, stmt, indent: int) -> List[str]:
        ind = '    ' * indent
        lines = []

        if isinstance(stmt, ast.Return):
            if stmt.value:
                val = self._convert_expr(stmt.value)
                lines.append(f'{ind}return {val};')
            else:
                lines.append(f'{ind}return;')

        elif isinstance(stmt, ast.Assign):
            for target in stmt.targets:
                target_str = self._convert_expr(target)
                value_str = self._convert_expr(stmt.value)
                var_type = self._infer_type(stmt.value)
                if isinstance(target, ast.Name):
                    lines.append(f'{ind}{var_type} {target_str} = {value_str};')
                else:
                    lines.append(f'{ind}{target_str} = {value_str};')

        elif isinstance(stmt, ast.AnnAssign):
            target_str = self._convert_expr(stmt.target)
            var_type = self._convert_type_annotation(stmt.annotation)
            if stmt.value:
                value_str = self._convert_expr(stmt.value)
                lines.append(f'{ind}{var_type} {target_str} = {value_str};')
            else:
                lines.append(f'{ind}{var_type} {target_str};')

        elif isinstance(stmt, ast.AugAssign):
            target_str = self._convert_expr(stmt.target)
            value_str = self._convert_expr(stmt.value)
            op = self._convert_binop(stmt.op)
            lines.append(f'{ind}{target_str} {op}= {value_str};')

        elif isinstance(stmt, ast.Expr):
            expr_str = self._convert_expr(stmt.value)
            lines.append(f'{ind}{expr_str};')

        elif isinstance(stmt, ast.If):
            test = self._convert_expr(stmt.test)
            lines.append(f'{ind}if ({test}) {{')
            lines.extend(self._convert_stmt_list(stmt.body, indent + 1))
            if stmt.orelse:
                if len(stmt.orelse) == 1 and isinstance(stmt.orelse[0], ast.If):
                    lines.append(f'{ind}}} else')
                    lines.extend(self._convert_stmt(stmt.orelse[0], indent))
                else:
                    lines.append(f'{ind}}} else {{')
                    lines.extend(self._convert_stmt_list(stmt.orelse, indent + 1))
                    lines.append(f'{ind}}}')
            else:
                lines.append(f'{ind}}}')

        elif isinstance(stmt, ast.For):
            target = self._convert_expr(stmt.target)
            iter_expr = stmt.iter

            if isinstance(iter_expr, ast.Call) and isinstance(iter_expr.func, ast.Name):
                if iter_expr.func.id == 'range':
                    args = iter_expr.args
                    if len(args) == 1:
                        end = self._convert_expr(args[0])
                        lines.append(f'{ind}for (int {target} = 0; {target} < {end}; ++{target}) {{')
                    elif len(args) == 2:
                        start = self._convert_expr(args[0])
                        end = self._convert_expr(args[1])
                        lines.append(f'{ind}for (int {target} = {start}; {target} < {end}; ++{target}) {{')
                    elif len(args) == 3:
                        start = self._convert_expr(args[0])
                        end = self._convert_expr(args[1])
                        step = self._convert_expr(args[2])
                        lines.append(f'{ind}for (int {target} = {start}; {target} < {end}; {target} += {step}) {{')
                else:
                    iter_str = self._convert_expr(iter_expr)
                    lines.append(f'{ind}for (auto& {target} : {iter_str}) {{')
            else:
                iter_str = self._convert_expr(iter_expr)
                lines.append(f'{ind}for (auto& {target} : {iter_str}) {{')

            lines.extend(self._convert_stmt_list(stmt.body, indent + 1))
            lines.append(f'{ind}}}')

        elif isinstance(stmt, ast.While):
            test = self._convert_expr(stmt.test)
            lines.append(f'{ind}while ({test}) {{')
            lines.extend(self._convert_stmt_list(stmt.body, indent + 1))
            lines.append(f'{ind}}}')

        elif isinstance(stmt, ast.Break):
            lines.append(f'{ind}break;')

        elif isinstance(stmt, ast.Continue):
            lines.append(f'{ind}continue;')

        elif isinstance(stmt, ast.Pass):
            pass

        elif isinstance(stmt, ast.Try):
            lines.append(f'{ind}try {{')
            lines.extend(self._convert_stmt_list(stmt.body, indent + 1))
            lines.append(f'{ind}}}')
            for handler in stmt.handlers:
                exc_type = 'std::exception'
                exc_name = 'e'
                if handler.type:
                    exc_type = self._convert_expr(handler.type)
                if handler.name:
                    exc_name = handler.name
                lines.append(f'{ind}catch (const {exc_type}& {exc_name}) {{')
                lines.extend(self._convert_stmt_list(handler.body, indent + 1))
                lines.append(f'{ind}}}')
            if stmt.finalbody:
                lines.append(f'{ind}// finally block (manual cleanup needed)')
                lines.extend(self._convert_stmt_list(stmt.finalbody, indent))

        elif isinstance(stmt, ast.Raise):
            if stmt.exc:
                exc = self._convert_expr(stmt.exc)
                lines.append(f'{ind}throw std::runtime_error({exc});')
            else:
                lines.append(f'{ind}throw;')

        elif isinstance(stmt, ast.With):
            lines.append(f'{ind}// with statement - manual resource management')
            for item in stmt.items:
                ctx = self._convert_expr(item.context_expr)
                if item.optional_vars:
                    var = self._convert_expr(item.optional_vars)
                    lines.append(f'{ind}auto {var} = {ctx};')
                else:
                    lines.append(f'{ind}{ctx};')
            lines.extend(self._convert_stmt_list(stmt.body, indent))

        elif isinstance(stmt, ast.FunctionDef):
            func = self._convert_function(stmt)
            lines.append(f'{ind}auto {func.name} = []({self._format_params(func.params)}) -> {func.return_type} {{')
            lines.append(func.body)
            lines.append(f'{ind}}};')

        return lines

    def _convert_stmt_list(self, stmts: List, indent: int) -> List[str]:
        lines = []
        for stmt in stmts:
            lines.extend(self._convert_stmt(stmt, indent))
        return lines

    def _convert_expr(self, node) -> str:
        if node is None:
            return ''

        if isinstance(node, ast.Constant):
            val = node.value
            if isinstance(val, str):
                escaped = val.replace('\\', '\\\\').replace('"', '\\"').replace('\n', '\\n').replace('\r', '\\r').replace('\t', '\\t')
                return f'"{escaped}"'
            elif isinstance(val, bool):
                return 'true' if val else 'false'
            elif val is None:
                return 'nullptr'
            else:
                return str(val)

        elif isinstance(node, ast.Name):
            name = node.id
            if name == 'True':
                return 'true'
            elif name == 'False':
                return 'false'
            elif name == 'None':
                return 'nullptr'
            return name

        elif isinstance(node, ast.Attribute):
            value = self._convert_expr(node.value)
            if value == 'self':
                return f'this->{node.attr}'
            return f'{value}.{node.attr}'

        elif isinstance(node, ast.Subscript):
            value = self._convert_expr(node.value)
            slice_val = self._convert_expr(node.slice)
            return f'{value}[{slice_val}]'

        elif isinstance(node, ast.Call):
            func = self._convert_expr(node.func)
            args = [self._convert_expr(arg) for arg in node.args]

            if func == 'len':
                return f'{args[0]}.size()'
            elif func == 'str':
                return f'std::to_string({args[0]})'
            elif func == 'int':
                return f'static_cast<int>({args[0]})'
            elif func == 'float':
                return f'static_cast<double>({args[0]})'
            elif func == 'print':
                return f'std::cout << {" << \" \" << ".join(args)} << std::endl'
            elif func == 'range':
                return f'/* range({", ".join(args)}) */'
            elif func == 'list':
                if args:
                    return f'std::vector({args[0]})'
                return 'std::vector<auto>{}'
            elif func == 'dict':
                return 'std::unordered_map<auto, auto>{}'
            elif func == 'set':
                if args:
                    return f'std::unordered_set({args[0]})'
                return 'std::unordered_set<auto>{}'
            elif func == 'abs':
                return f'std::abs({args[0]})'
            elif func == 'min':
                return f'std::min({{{", ".join(args)}}})'
            elif func == 'max':
                return f'std::max({{{", ".join(args)}}})'
            elif func == 'sum':
                self.imports.add('<numeric>')
                return f'std::accumulate({args[0]}.begin(), {args[0]}.end(), 0)'
            elif func == 'sorted':
                self.imports.add('<algorithm>')
                return f'[&](){{ auto tmp = {args[0]}; std::sort(tmp.begin(), tmp.end()); return tmp; }}()'
            elif func == 'reversed':
                self.imports.add('<algorithm>')
                return f'[&](){{ auto tmp = {args[0]}; std::reverse(tmp.begin(), tmp.end()); return tmp; }}()'
            elif func.endswith('.append'):
                obj = func.rsplit('.', 1)[0]
                return f'{obj}.push_back({args[0]})'
            elif func.endswith('.pop'):
                obj = func.rsplit('.', 1)[0]
                if args:
                    return f'{obj}.erase({obj}.begin() + {args[0]})'
                return f'[&](){{ auto tmp = {obj}.back(); {obj}.pop_back(); return tmp; }}()'
            elif func.endswith('.insert'):
                obj = func.rsplit('.', 1)[0]
                return f'{obj}.insert({obj}.begin() + {args[0]}, {args[1]})'
            elif func.endswith('.remove'):
                obj = func.rsplit('.', 1)[0]
                self.imports.add('<algorithm>')
                return f'{obj}.erase(std::remove({obj}.begin(), {obj}.end(), {args[0]}), {obj}.end())'
            elif func.endswith('.clear'):
                obj = func.rsplit('.', 1)[0]
                return f'{obj}.clear()'
            elif func.endswith('.keys'):
                obj = func.rsplit('.', 1)[0]
                return f'[&](){{ std::vector<decltype({obj})::key_type> keys; for(auto& p : {obj}) keys.push_back(p.first); return keys; }}()'
            elif func.endswith('.values'):
                obj = func.rsplit('.', 1)[0]
                return f'[&](){{ std::vector<decltype({obj})::mapped_type> vals; for(auto& p : {obj}) vals.push_back(p.second); return vals; }}()'
            elif func.endswith('.items'):
                obj = func.rsplit('.', 1)[0]
                return f'{obj}'
            elif func.endswith('.get'):
                obj = func.rsplit('.', 1)[0]
                if len(args) >= 2:
                    return f'({obj}.count({args[0]}) ? {obj}[{args[0]}] : {args[1]})'
                return f'{obj}[{args[0]}]'
            elif func.endswith('.find'):
                obj = func.rsplit('.', 1)[0]
                return f'{obj}.find({args[0]})'
            elif func.endswith('.replace'):
                obj = func.rsplit('.', 1)[0]
                self.imports.add('<regex>')
                return f'std::regex_replace({obj}, std::regex({args[0]}), {args[1]})'
            elif func.endswith('.split'):
                obj = func.rsplit('.', 1)[0]
                self.imports.add('<sstream>')
                if args:
                    return f'/* split by {args[0]} - implement manually */'
                return f'[&](){{ std::vector<std::string> tokens; std::istringstream iss({obj}); std::string tok; while(iss >> tok) tokens.push_back(tok); return tokens; }}()'
            elif func.endswith('.join'):
                obj = func.rsplit('.', 1)[0]
                return f'[&](){{ std::string result; for(size_t i=0; i<{args[0]}.size(); ++i) {{ if(i>0) result += {obj}; result += {args[0]}[i]; }} return result; }}()'
            elif func.endswith('.strip') or func.endswith('.lstrip') or func.endswith('.rstrip'):
                obj = func.rsplit('.', 1)[0]
                return f'/* {func}() - implement trim manually */'
            elif func.endswith('.lower'):
                obj = func.rsplit('.', 1)[0]
                self.imports.add('<algorithm>')
                return f'[&](){{ std::string s = {obj}; std::transform(s.begin(), s.end(), s.begin(), ::tolower); return s; }}()'
            elif func.endswith('.upper'):
                obj = func.rsplit('.', 1)[0]
                self.imports.add('<algorithm>')
                return f'[&](){{ std::string s = {obj}; std::transform(s.begin(), s.end(), s.begin(), ::toupper); return s; }}()'
            elif func.endswith('.startswith'):
                obj = func.rsplit('.', 1)[0]
                return f'{obj}.substr(0, {args[0]}.size()) == {args[0]}'
            elif func.endswith('.endswith'):
                obj = func.rsplit('.', 1)[0]
                return f'({obj}.size() >= {args[0]}.size() && {obj}.substr({obj}.size() - {args[0]}.size()) == {args[0]})'

            kwargs = []
            for kw in node.keywords:
                kwargs.append(f'/* {kw.arg}= */{self._convert_expr(kw.value)}')

            all_args = args + kwargs
            return f'{func}({", ".join(all_args)})'

        elif isinstance(node, ast.BinOp):
            left = self._convert_expr(node.left)
            right = self._convert_expr(node.right)
            op = self._convert_binop(node.op)

            if isinstance(node.op, ast.Pow):
                self.imports.add('<cmath>')
                return f'std::pow({left}, {right})'
            elif isinstance(node.op, ast.FloorDiv):
                return f'static_cast<int>({left} / {right})'
            elif isinstance(node.op, ast.Mod):
                left_type = self._infer_type(node.left)
                if 'double' in left_type or 'float' in left_type:
                    self.imports.add('<cmath>')
                    return f'std::fmod({left}, {right})'

            return f'({left} {op} {right})'

        elif isinstance(node, ast.UnaryOp):
            operand = self._convert_expr(node.operand)
            if isinstance(node.op, ast.Not):
                return f'!{operand}'
            elif isinstance(node.op, ast.USub):
                return f'-{operand}'
            elif isinstance(node.op, ast.UAdd):
                return f'+{operand}'
            elif isinstance(node.op, ast.Invert):
                return f'~{operand}'
            return operand

        elif isinstance(node, ast.Compare):
            left = self._convert_expr(node.left)
            parts = [left]
            for i, (op, comp) in enumerate(zip(node.ops, node.comparators)):
                op_str = self._convert_cmpop(op)
                comp_str = self._convert_expr(comp)
                if isinstance(op, ast.In):
                    self.imports.add('<algorithm>')
                    prev = parts[-1] if parts else left
                    return f'std::find({comp_str}.begin(), {comp_str}.end(), {prev}) != {comp_str}.end()'
                elif isinstance(op, ast.NotIn):
                    self.imports.add('<algorithm>')
                    prev = parts[-1] if parts else left
                    return f'std::find({comp_str}.begin(), {comp_str}.end(), {prev}) == {comp_str}.end()'
                parts.append(f'{op_str} {comp_str}')
            return ' '.join(parts)

        elif isinstance(node, ast.BoolOp):
            op = ' && ' if isinstance(node.op, ast.And) else ' || '
            values = [self._convert_expr(v) for v in node.values]
            return f'({op.join(values)})'

        elif isinstance(node, ast.IfExp):
            test = self._convert_expr(node.test)
            body = self._convert_expr(node.body)
            orelse = self._convert_expr(node.orelse)
            return f'({test} ? {body} : {orelse})'

        elif isinstance(node, ast.List):
            elts = [self._convert_expr(e) for e in node.elts]
            if elts:
                inner_type = self._infer_type(node.elts[0])
                return f'std::vector<{inner_type}>{{{", ".join(elts)}}}'
            return 'std::vector<auto>{}'

        elif isinstance(node, ast.Dict):
            pairs = []
            for k, v in zip(node.keys, node.values):
                key = self._convert_expr(k)
                val = self._convert_expr(v)
                pairs.append(f'{{{key}, {val}}}')
            if pairs:
                key_type = self._infer_type(node.keys[0]) if node.keys else 'auto'
                val_type = self._infer_type(node.values[0]) if node.values else 'auto'
                return f'std::unordered_map<{key_type}, {val_type}>{{{", ".join(pairs)}}}'
            return 'std::unordered_map<auto, auto>{}'

        elif isinstance(node, ast.Set):
            elts = [self._convert_expr(e) for e in node.elts]
            if elts:
                inner_type = self._infer_type(node.elts[0])
                return f'std::unordered_set<{inner_type}>{{{", ".join(elts)}}}'
            return 'std::unordered_set<auto>{}'

        elif isinstance(node, ast.Tuple):
            elts = [self._convert_expr(e) for e in node.elts]
            return f'std::make_tuple({", ".join(elts)})'

        elif isinstance(node, ast.Lambda):
            params = []
            for arg in node.args.args:
                params.append(f'auto {arg.arg}')
            body = self._convert_expr(node.body)
            return f'[&]({", ".join(params)}) {{ return {body}; }}'

        elif isinstance(node, ast.ListComp):
            self.imports.add('<algorithm>')
            elt = self._convert_expr(node.elt)
            gen = node.generators[0]
            iter_var = self._convert_expr(gen.target)
            iter_src = self._convert_expr(gen.iter)
            if gen.ifs:
                cond = self._convert_expr(gen.ifs[0])
                return f'[&](){{ std::vector<decltype({elt})> result; for(auto& {iter_var} : {iter_src}) if({cond}) result.push_back({elt}); return result; }}()'
            return f'[&](){{ std::vector<decltype({elt})> result; for(auto& {iter_var} : {iter_src}) result.push_back({elt}); return result; }}()'

        elif isinstance(node, ast.DictComp):
            key = self._convert_expr(node.key)
            value = self._convert_expr(node.value)
            gen = node.generators[0]
            iter_var = self._convert_expr(gen.target)
            iter_src = self._convert_expr(gen.iter)
            if gen.ifs:
                cond = self._convert_expr(gen.ifs[0])
                return f'[&](){{ std::unordered_map<decltype({key}), decltype({value})> result; for(auto& {iter_var} : {iter_src}) if({cond}) result[{key}] = {value}; return result; }}()'
            return f'[&](){{ std::unordered_map<decltype({key}), decltype({value})> result; for(auto& {iter_var} : {iter_src}) result[{key}] = {value}; return result; }}()'

        elif isinstance(node, ast.SetComp):
            elt = self._convert_expr(node.elt)
            gen = node.generators[0]
            iter_var = self._convert_expr(gen.target)
            iter_src = self._convert_expr(gen.iter)
            if gen.ifs:
                cond = self._convert_expr(gen.ifs[0])
                return f'[&](){{ std::unordered_set<decltype({elt})> result; for(auto& {iter_var} : {iter_src}) if({cond}) result.insert({elt}); return result; }}()'
            return f'[&](){{ std::unordered_set<decltype({elt})> result; for(auto& {iter_var} : {iter_src}) result.insert({elt}); return result; }}()'

        elif isinstance(node, ast.Slice):
            lower = self._convert_expr(node.lower) if node.lower else '0'
            upper = self._convert_expr(node.upper) if node.upper else ''
            return f'{lower}:{upper}'

        elif isinstance(node, ast.Index):
            return self._convert_expr(node.value)

        return f'/* unknown: {type(node).__name__} */'

    def _convert_binop(self, op) -> str:
        ops = {
            ast.Add: '+',
            ast.Sub: '-',
            ast.Mult: '*',
            ast.Div: '/',
            ast.FloorDiv: '/',
            ast.Mod: '%',
            ast.Pow: '**',
            ast.LShift: '<<',
            ast.RShift: '>>',
            ast.BitOr: '|',
            ast.BitXor: '^',
            ast.BitAnd: '&',
            ast.MatMult: '*',
        }
        return ops.get(type(op), '+')

    def _convert_cmpop(self, op) -> str:
        ops = {
            ast.Eq: '==',
            ast.NotEq: '!=',
            ast.Lt: '<',
            ast.LtE: '<=',
            ast.Gt: '>',
            ast.GtE: '>=',
            ast.Is: '==',
            ast.IsNot: '!=',
            ast.In: 'in',
            ast.NotIn: 'not in',
        }
        return ops.get(type(op), '==')

    def _get_attr_name(self, node) -> str:
        if isinstance(node, ast.Attribute):
            value = self._get_attr_name(node.value)
            return f'{value}::{node.attr}'
        elif isinstance(node, ast.Name):
            return node.id
        return ''

    def _format_params(self, params: List[Tuple[str, str]]) -> str:
        return ', '.join(f'{ptype} {pname}' for pname, ptype in params)

    def _format_params_with_const_ref(self, params: List[Tuple[str, str]]) -> str:
        result = []
        for pname, ptype in params:
            if ptype in ('std::string', 'std::vector', 'std::unordered_map', 'std::unordered_set') or ptype.startswith('std::'):
                result.append(f'const {ptype}& {pname}')
            else:
                result.append(f'{ptype} {pname}')
        return ', '.join(result)

    def _generate_header(self, module_name: str, classes: List[ClassInfo],
                         functions: List[FunctionInfo], global_vars: List) -> str:
        lines = []
        guard = f'{module_name.upper()}_H'

        lines.append(f'#ifndef {guard}')
        lines.append(f'#define {guard}')
        lines.append('')

        std_includes = {'<string>', '<vector>', '<unordered_map>', '<unordered_set>',
                        '<tuple>', '<optional>', '<functional>', '<memory>'}
        std_includes.update(self.imports)
        for inc in sorted(std_includes):
            lines.append(f'#include {inc}')
        lines.append('')

        lines.append('namespace includecpp {')
        lines.append('')

        for name, var_type, _ in global_vars:
            lines.append(f'extern {var_type} {name};')

        if global_vars:
            lines.append('')

        for cls in classes:
            if cls.bases:
                bases_str = ', '.join(f'public {b}' for b in cls.bases)
                lines.append(f'class {cls.name} : {bases_str} {{')
            else:
                lines.append(f'class {cls.name} {{')
            lines.append('public:')

            for fname, ftype, _ in cls.fields:
                lines.append(f'    {ftype} {fname};')

            if cls.fields:
                lines.append('')

            for ctor in cls.constructors:
                params = self._format_params_with_const_ref(ctor.params)
                lines.append(f'    {cls.name}({params});')

            for method in cls.methods:
                params = self._format_params_with_const_ref(method.params)
                static = 'static ' if method.is_static else ''
                const = ' const' if method.is_const else ''
                lines.append(f'    {static}{method.return_type} {method.name}({params}){const};')

            lines.append('};')
            lines.append('')

        for func in functions:
            params = self._format_params_with_const_ref(func.params)
            lines.append(f'{func.return_type} {func.name}({params});')

        lines.append('')
        lines.append('} // namespace includecpp')
        lines.append('')
        lines.append(f'#endif // {guard}')

        return '\n'.join(lines)

    def _generate_source(self, module_name: str, classes: List[ClassInfo],
                         functions: List[FunctionInfo], global_vars: List) -> str:
        lines = []

        lines.append(f'#include "{module_name}.h"')
        lines.append('#include <iostream>')
        lines.append('#include <cmath>')
        lines.append('#include <stdexcept>')
        lines.append('')
        lines.append('namespace includecpp {')
        lines.append('')

        for name, var_type, value in global_vars:
            if value:
                lines.append(f'{var_type} {name} = {value};')
            else:
                lines.append(f'{var_type} {name};')

        if global_vars:
            lines.append('')

        for cls in classes:
            for ctor in cls.constructors:
                params = self._format_params_with_const_ref(ctor.params)
                lines.append(f'{cls.name}::{cls.name}({params}) {{')

                for fname, ftype, fdefault in cls.fields:
                    init_val = None
                    for pname, _ in ctor.params:
                        if pname == fname:
                            init_val = pname
                            break
                    if init_val:
                        lines.append(f'    this->{fname} = {init_val};')
                    elif fdefault:
                        lines.append(f'    this->{fname} = {fdefault};')

                ctor_body_lines = ctor.body.split('\n')
                for line in ctor_body_lines:
                    if 'this->' in line and '=' in line:
                        pass
                    elif line.strip():
                        lines.append(line)

                lines.append('}')
                lines.append('')

            for method in cls.methods:
                params = self._format_params_with_const_ref(method.params)
                const = ' const' if method.is_const else ''
                lines.append(f'{method.return_type} {cls.name}::{method.name}({params}){const} {{')
                lines.append(method.body)
                lines.append('}')
                lines.append('')

        for func in functions:
            params = self._format_params_with_const_ref(func.params)
            lines.append(f'{func.return_type} {func.name}({params}) {{')
            lines.append(func.body)
            lines.append('}')
            lines.append('')

        lines.append('} // namespace includecpp')

        return '\n'.join(lines)


class CppToPythonConverter:
    def __init__(self):
        self.indent = '    '

    def convert(self, source: str, module_name: str) -> str:
        """Convert C++ source to Python."""
        lines = []

        source = self._remove_comments(source)

        functions = self._parse_functions(source)
        classes = self._parse_classes(source)
        structs = self._parse_structs(source)
        global_vars = self._parse_global_vars(source)

        if any(c.name for c in classes) or any(s.name for s in structs):
            lines.append('from dataclasses import dataclass, field')
            lines.append('from typing import List, Dict, Set, Tuple, Optional, Any, Callable')
            lines.append('')

        for struct in structs:
            lines.extend(self._generate_struct(struct))
            lines.append('')

        for cls in classes:
            lines.extend(self._generate_class(cls))
            lines.append('')

        for func in functions:
            lines.extend(self._generate_function(func))
            lines.append('')

        for name, value in global_vars:
            lines.append(f'{name} = {value}')

        return '\n'.join(lines)

    def _remove_comments(self, source: str) -> str:
        source = re.sub(r'//.*$', '', source, flags=re.MULTILINE)
        source = re.sub(r'/\*.*?\*/', '', source, flags=re.DOTALL)
        return source

    def _parse_functions(self, source: str) -> List[FunctionInfo]:
        functions = []

        namespace_match = re.search(r'namespace\s+includecpp\s*\{(.*)\}', source, re.DOTALL)
        if namespace_match:
            source = namespace_match.group(1)

        pattern = r'(?:(?:static|inline|virtual|explicit|constexpr)\s+)*(\w+(?:<[^>]+>)?(?:\s*[*&])?)\s+(\w+)\s*\(([^)]*)\)\s*(?:const)?\s*\{([^{}]*(?:\{[^{}]*\}[^{}]*)*)\}'

        for match in re.finditer(pattern, source):
            return_type = match.group(1).strip()
            name = match.group(2)
            params_str = match.group(3).strip()
            body = match.group(4)

            if name in ('if', 'while', 'for', 'switch', 'catch'):
                continue

            params = self._parse_params(params_str)
            py_body = self._convert_cpp_body(body)

            functions.append(FunctionInfo(
                name=name,
                return_type=self._convert_cpp_type(return_type),
                params=[(n, self._convert_cpp_type(t)) for n, t in params],
                body=py_body
            ))

        return functions

    def _parse_classes(self, source: str) -> List[ClassInfo]:
        classes = []

        pattern = r'class\s+(\w+)(?:\s*:\s*(?:public|private|protected)\s+(\w+))?\s*\{([^{}]*(?:\{[^{}]*\}[^{}]*)*)\}'

        for match in re.finditer(pattern, source):
            name = match.group(1)
            base = match.group(2)
            body = match.group(3)

            fields = self._parse_class_fields(body)
            methods = self._parse_class_methods(body, name)
            constructors = self._parse_constructors(body, name)

            classes.append(ClassInfo(
                name=name,
                bases=[base] if base else [],
                methods=methods,
                fields=fields,
                constructors=constructors
            ))

        return classes

    def _parse_structs(self, source: str) -> List[StructInfo]:
        structs = []

        pattern = r'struct\s+(\w+)\s*\{([^{}]*)\}'

        for match in re.finditer(pattern, source):
            name = match.group(1)
            body = match.group(2)

            fields = []
            field_pattern = r'(\w+(?:<[^>]+>)?(?:\s*[*&])?)\s+(\w+)\s*;'
            for fm in re.finditer(field_pattern, body):
                field_type = fm.group(1).strip()
                field_name = fm.group(2)
                fields.append((field_name, self._convert_cpp_type(field_type)))

            structs.append(StructInfo(name=name, fields=fields))

        return structs

    def _parse_global_vars(self, source: str) -> List[Tuple[str, str]]:
        vars = []

        namespace_match = re.search(r'namespace\s+includecpp\s*\{(.*)\}', source, re.DOTALL)
        if namespace_match:
            content = namespace_match.group(1)

            pattern = r'(?:const\s+)?(\w+(?:<[^>]+>)?)\s+(\w+)\s*=\s*([^;]+);'
            for match in re.finditer(pattern, content):
                var_name = match.group(2)
                var_value = self._convert_cpp_expr(match.group(3).strip())
                vars.append((var_name, var_value))

        return vars

    def _parse_params(self, params_str: str) -> List[Tuple[str, str]]:
        if not params_str.strip():
            return []

        params = []
        depth = 0
        current = ''

        for char in params_str:
            if char == '<':
                depth += 1
            elif char == '>':
                depth -= 1
            elif char == ',' and depth == 0:
                params.append(current.strip())
                current = ''
                continue
            current += char

        if current.strip():
            params.append(current.strip())

        result = []
        for param in params:
            param = param.strip()
            if not param:
                continue

            param = re.sub(r'^const\s+', '', param)
            param = re.sub(r'\s*[&*]+\s*', ' ', param)

            parts = param.rsplit(None, 1)
            if len(parts) == 2:
                ptype, pname = parts
                pname = re.sub(r'=.*$', '', pname).strip()
                result.append((pname, ptype))
            elif len(parts) == 1:
                result.append((parts[0], 'auto'))

        return result

    def _parse_class_fields(self, body: str) -> List[Tuple[str, str, Optional[str]]]:
        fields = []

        sections = re.split(r'(?:public|private|protected)\s*:', body)
        public_section = sections[1] if len(sections) > 1 else body

        field_pattern = r'(\w+(?:<[^>]+>)?)\s+(\w+)\s*(?:=\s*([^;]+))?\s*;'
        for match in re.finditer(field_pattern, public_section):
            field_type = match.group(1)
            field_name = match.group(2)
            default = match.group(3)

            if '(' in field_type or field_type in ('return', 'if', 'for', 'while'):
                continue

            py_default = self._convert_cpp_expr(default) if default else None
            fields.append((field_name, self._convert_cpp_type(field_type), py_default))

        return fields

    def _parse_class_methods(self, body: str, class_name: str) -> List[FunctionInfo]:
        methods = []

        pattern = r'(?:(static|virtual)\s+)?(\w+(?:<[^>]+>)?(?:\s*[*&])?)\s+(\w+)\s*\(([^)]*)\)\s*(const)?\s*(?:\{([^{}]*(?:\{[^{}]*\}[^{}]*)*)\}|;)'

        for match in re.finditer(pattern, body):
            modifier = match.group(1)
            return_type = match.group(2).strip()
            name = match.group(3)
            params_str = match.group(4)
            is_const = match.group(5) is not None
            method_body = match.group(6) or ''

            if name == class_name or name == f'~{class_name}':
                continue

            params = self._parse_params(params_str)
            py_body = self._convert_cpp_body(method_body)

            methods.append(FunctionInfo(
                name=name,
                return_type=self._convert_cpp_type(return_type),
                params=[(n, self._convert_cpp_type(t)) for n, t in params],
                body=py_body,
                is_method=True,
                is_static=(modifier == 'static'),
                is_const=is_const
            ))

        return methods

    def _parse_constructors(self, body: str, class_name: str) -> List[FunctionInfo]:
        constructors = []

        pattern = rf'{class_name}\s*\(([^)]*)\)\s*(?::\s*[^{{]+)?\s*\{{([^{{}}]*(?:\{{[^{{}}]*\}}[^{{}}]*)*)\}}'

        for match in re.finditer(pattern, body):
            params_str = match.group(1)
            ctor_body = match.group(2)

            params = self._parse_params(params_str)
            py_body = self._convert_cpp_body(ctor_body)

            constructors.append(FunctionInfo(
                name='__init__',
                return_type='None',
                params=[(n, self._convert_cpp_type(t)) for n, t in params],
                body=py_body,
                is_method=True
            ))

        return constructors

    def _convert_cpp_type(self, cpp_type: str) -> str:
        cpp_type = cpp_type.strip()
        cpp_type = re.sub(r'^const\s+', '', cpp_type)
        cpp_type = re.sub(r'\s*[&*]+\s*$', '', cpp_type)
        cpp_type = cpp_type.strip()

        template_match = re.match(r'(\w+(?:::\w+)?)<(.+)>$', cpp_type)
        if template_match:
            container = template_match.group(1)
            inner = template_match.group(2)

            py_container = CPP_TO_PY_TYPES.get(container, container)

            inner_types = self._split_template_args(inner)
            py_inner = [self._convert_cpp_type(t) for t in inner_types]

            if py_container == 'list':
                return f'List[{py_inner[0]}]' if py_inner else 'List'
            elif py_container == 'dict':
                if len(py_inner) >= 2:
                    return f'Dict[{py_inner[0]}, {py_inner[1]}]'
                return 'Dict'
            elif py_container == 'set':
                return f'Set[{py_inner[0]}]' if py_inner else 'Set'
            elif py_container == 'tuple':
                return f'Tuple[{", ".join(py_inner)}]'
            elif py_container == 'Optional':
                return f'Optional[{py_inner[0]}]' if py_inner else 'Optional'

        return CPP_TO_PY_TYPES.get(cpp_type, cpp_type)

    def _split_template_args(self, args: str) -> List[str]:
        result = []
        depth = 0
        current = ''

        for char in args:
            if char == '<':
                depth += 1
                current += char
            elif char == '>':
                depth -= 1
                current += char
            elif char == ',' and depth == 0:
                result.append(current.strip())
                current = ''
            else:
                current += char

        if current.strip():
            result.append(current.strip())

        return result

    def _convert_cpp_body(self, body: str) -> str:
        if not body.strip():
            return 'pass'

        lines = []
        for line in body.split(';'):
            line = line.strip()
            if not line:
                continue

            py_line = self._convert_cpp_statement(line)
            if py_line:
                lines.append(py_line)

        return '\n'.join(lines) if lines else 'pass'

    def _convert_cpp_statement(self, stmt: str) -> str:
        stmt = stmt.strip()
        if not stmt or stmt == '{' or stmt == '}':
            return ''

        if stmt.startswith('return'):
            expr = stmt[6:].strip()
            if not expr:
                return 'return'
            return f'return {self._convert_cpp_expr(expr)}'

        var_match = re.match(r'(\w+(?:<[^>]+>)?)\s+(\w+)\s*=\s*(.+)$', stmt)
        if var_match:
            var_name = var_match.group(2)
            value = self._convert_cpp_expr(var_match.group(3))
            return f'{var_name} = {value}'

        assign_match = re.match(r'(\w+(?:\.\w+)*)\s*=\s*(.+)$', stmt)
        if assign_match:
            target = assign_match.group(1)
            target = target.replace('this->', 'self.')
            value = self._convert_cpp_expr(assign_match.group(2))
            return f'{target} = {value}'

        return self._convert_cpp_expr(stmt)

    def _convert_cpp_expr(self, expr: str) -> str:
        if not expr:
            return ''

        expr = expr.strip()

        expr = expr.replace('this->', 'self.')
        expr = expr.replace('->', '.')
        expr = expr.replace('::', '.')
        expr = expr.replace('nullptr', 'None')
        expr = expr.replace('true', 'True')
        expr = expr.replace('false', 'False')
        expr = expr.replace('&&', ' and ')
        expr = expr.replace('||', ' or ')
        expr = expr.replace('!', 'not ')

        expr = re.sub(r'std::cout\s*<<\s*(.+?)(?:\s*<<\s*std::endl)?', r'print(\1)', expr)
        expr = re.sub(r'\.size\(\)', r'.__len__()', expr)
        expr = re.sub(r'\.push_back\(([^)]+)\)', r'.append(\1)', expr)
        expr = re.sub(r'\.empty\(\)', r'.__len__() == 0', expr)
        expr = re.sub(r'static_cast<\w+>\(([^)]+)\)', r'\1', expr)
        expr = re.sub(r'std::to_string\(([^)]+)\)', r'str(\1)', expr)
        expr = re.sub(r'std::stoi\(([^)]+)\)', r'int(\1)', expr)
        expr = re.sub(r'std::stof\(([^)]+)\)', r'float(\1)', expr)
        expr = re.sub(r'std::abs\(([^)]+)\)', r'abs(\1)', expr)
        expr = re.sub(r'std::pow\(([^,]+),\s*([^)]+)\)', r'\1 ** \2', expr)
        expr = re.sub(r'std::sqrt\(([^)]+)\)', r'(\1) ** 0.5', expr)
        expr = re.sub(r'std::min\(([^,]+),\s*([^)]+)\)', r'min(\1, \2)', expr)
        expr = re.sub(r'std::max\(([^,]+),\s*([^)]+)\)', r'max(\1, \2)', expr)

        expr = re.sub(r'std::vector<[^>]+>\{([^}]*)\}', r'[\1]', expr)
        expr = re.sub(r'std::unordered_map<[^>]+>\{([^}]*)\}', r'{\1}', expr)
        expr = re.sub(r'std::unordered_set<[^>]+>\{([^}]*)\}', r'{\1}', expr)
        expr = re.sub(r'std::make_tuple\(([^)]*)\)', r'(\1)', expr)

        expr = re.sub(r'"([^"]*)"', r"'\1'", expr)

        return expr

    def _generate_struct(self, struct: StructInfo) -> List[str]:
        lines = ['@dataclass']
        lines.append(f'class {struct.name}:')

        if not struct.fields:
            lines.append(f'{self.indent}pass')
        else:
            for fname, ftype in struct.fields:
                lines.append(f'{self.indent}{fname}: {ftype}')

        return lines

    def _generate_class(self, cls: ClassInfo) -> List[str]:
        lines = []

        if cls.bases:
            lines.append(f'class {cls.name}({", ".join(cls.bases)}):')
        else:
            lines.append(f'class {cls.name}:')

        if not cls.fields and not cls.methods and not cls.constructors:
            lines.append(f'{self.indent}pass')
            return lines

        for ctor in cls.constructors:
            lines.extend(self._generate_method(ctor, is_init=True))

        for method in cls.methods:
            lines.extend(self._generate_method(method))

        return lines

    def _generate_method(self, method: FunctionInfo, is_init: bool = False) -> List[str]:
        lines = []

        if method.is_static:
            lines.append(f'{self.indent}@staticmethod')

        params = ['self'] if not method.is_static else []
        for pname, ptype in method.params:
            if ptype and ptype != 'Any':
                params.append(f'{pname}: {ptype}')
            else:
                params.append(pname)

        ret_type = ''
        if not is_init and method.return_type and method.return_type != 'None':
            ret_type = f' -> {method.return_type}'

        method_name = '__init__' if is_init else method.name
        lines.append(f'{self.indent}def {method_name}({", ".join(params)}){ret_type}:')

        body_lines = method.body.split('\n')
        if body_lines and body_lines[0].strip():
            for bl in body_lines:
                lines.append(f'{self.indent}{self.indent}{bl}')
        else:
            lines.append(f'{self.indent}{self.indent}pass')

        return lines

    def _generate_function(self, func: FunctionInfo) -> List[str]:
        lines = []

        params = []
        for pname, ptype in func.params:
            if ptype and ptype != 'Any':
                params.append(f'{pname}: {ptype}')
            else:
                params.append(pname)

        ret_type = ''
        if func.return_type and func.return_type != 'None':
            ret_type = f' -> {func.return_type}'

        lines.append(f'def {func.name}({", ".join(params)}){ret_type}:')

        body_lines = func.body.split('\n')
        if body_lines and body_lines[0].strip():
            for bl in body_lines:
                lines.append(f'{self.indent}{bl}')
        else:
            lines.append(f'{self.indent}pass')

        return lines


def convert_python_to_cpp(source: str, module_name: str) -> Tuple[str, str]:
    """Convert Python to C++. Returns (cpp_content, header_content)."""
    converter = PythonToCppConverter()
    return converter.convert(source, module_name)


def convert_cpp_to_python(source: str, module_name: str) -> str:
    """Convert C++ to Python. Returns python content."""
    converter = CppToPythonConverter()
    return converter.convert(source, module_name)
