#!/usr/bin/env python3
# -*- coding: utf-8 -*-

import re, json
from pathlib import Path
from typing import List, Tuple, Dict

from pilot.job.impl.base_job import BaseJob

# from db.db_util import exec_delete, exec_insert

from pilot.job.base.program.cobol.tool_base.ToolBase import load_cobol_keywords_list

# --------------------------------------------------------------
# 正则（只匹配我们关心的东西）
# --------------------------------------------------------------
RE_CALL_1    = re.compile(
    r'''CALL\s+(?:'([^']+)'|"([^"]+)"|([A-Z0-9\-\_]+))''',
    re.IGNORECASE | re.VERBOSE)
RE_PERFORM = re.compile(r'\bPERFORM\s+([A-Z0-9\-\_]+)', re.IGNORECASE)

RE_CALL_2 = re.compile(
    r'''
    CALL                       # CALL 关键字
    \s+                        # 至少一个空格
    (?:'(?P<mod1>[^']+)'       # 单引号包围的模块名
        |"(?P<mod2>[^"]+)"     # 双引号包围的模块名
        |(?P<mod3>[A-Z0-9\-_]+) # 裸写的模块名（字母、数字、下划线、连字符）
    )
    (?:\s+USING                # 可选的 USING 子句
        (?P<args>               # 捕获所有参数（后面会再次拆分）
            (?:\s+
                (?:'[^']+'      # 单引号参数
                    |"[^"]+"    # 双引号参数
                    |[A-Z0-9\-_]+   # 裸写参数
                )
            )*                 # 0 或多次（即可以没有参数）
        )
    )?                         # USING 整块是可选的
    ''',
    re.IGNORECASE | re.VERBOSE
)


RE_ARG = re.compile(
    r'''
    (?:'(?P<a1>[^']+)'
        |"(?P<a2>[^"]+)"
        |(?P<a3>[A-Z0-9\-_]+)
    )
    ''',
    re.IGNORECASE | re.VERBOSE
)

# 関数定義行のパターン
FUNCTION_ENTRY_PATTERN = "SECTION."

# 関数終了行のパターン
FUNCTION_EXIT_PATTERN = "-EXT."

# 結合時の空白文字
CONTINUATION_SPACE = " "


class GetRelationByProcedureBaseJob(BaseJob):
    sub_method_list = []
    sub_module_list = []

    # --------------------------------------------------------------
    # 解析函数
    # --------------------------------------------------------------
    def extract_calls(self, cobol_code_list, cobol_keywords):
        """
        只返回 (caller, callee) 列表。
        - caller : 当前段落（section / paragraph）的名字
        - callee : CALL 或 PERFORM 后面的目标名称（已转为大写）
        """
        sub_method_list = []
        sub_module_list = []

        for raw in cobol_code_list:
            line = raw.rstrip()

            # 1) CALL
            for m in RE_CALL_1.finditer(line):
                call_line_info_list = self.parse_cobol_call(line)
                if len(call_line_info_list) > 0:
                    for call_line_info in call_line_info_list:
                        sub_module_list.append(call_line_info)

            # 2) PERFORM
            for m in RE_PERFORM.finditer(line):
                if ' TIMES' not in (line.strip().upper()):
                    callee = m.group(1).upper()
                    if callee not in cobol_keywords:
                        sub_method_list.append(callee)

        return sub_method_list, sub_module_list

    def parse_cobol_call(self, line: str) -> List[Dict[str, str]]:
        """
        把一行 COBOL CALL 语句解析成
        [{'sub_module_name': <module>, 'sub_module_dto_name': <arg>}, ...].

        参数
        ----
        line: str
            可能包含前后空格、注释或其它内容的原始行。

        返回
        ----
        List[Dict[str, str]]
            解析结果；如果行不是 CALL 语句则返回空列表。
        """
        result = []

        # 1️⃣ 匹配整个 CALL 语句
        m = RE_CALL_2.search(line)
        if not m:
            return result  # 不是 CALL，直接返回空

        # 2️⃣ 取出模块名（去掉引号），统一转大写
        module = (m.group('mod1') or m.group('mod2') or m.group('mod3')).upper()

        # 3️⃣ 拆分 USING 子句中的参数（如果有的话）
        args_str = m.group('args') or ''
        # findall 会返回每个捕获组的 tuple，需要手工挑选非空的那一个
        for arg_match in RE_ARG.finditer(args_str):
            arg = (arg_match.group('a1') or
                   arg_match.group('a2') or
                   arg_match.group('a3')).upper()
            if not arg.startswith('DFH'):
                result.append({
                    'sub_module_name': module,
                    'sub_module_dto_name': arg
                })

        # 如果没有 USING 参数，仍然返回一个只包含模块名的记录（可根据业务自行决定）
        if not result:
            result.append({
                'sub_module_name': module,
                'sub_module_dto_name': ''  # 空字符串表示“无参数”
            })

        return result

    # def db_operation(self, sub_module_list, sub_method_list):
    #
    #     if len(sub_method_list) > 0 or len(sub_module_list) > 0:
    #         module_name = Path(self.file_path).parent.name.split('_')[0]
    #         method_name = Path(self.file_path).name.split('.')[0]
    #         params_key = {
    #             'module_name': module_name,
    #             'method_name': method_name
    #         }
    #
    #         exec_delete('delete_module_method_relation_info', params_key)
    #
    #         if len(sub_method_list) > 0:
    #             for sub_method_info in sub_method_list:
    #                 insert_params = {
    #                     'module_name': module_name,
    #                     'method_name': method_name,
    #                     'sub_method_name':sub_method_info,
    #                     'sub_module_name': '',
    #                     'sub_module_parameter_name': ''
    #                 }
    #
    #                 exec_insert('insert_module_method_relation_info', insert_params)
    #
    #         if len(sub_module_list) > 0:
    #             for sub_module_info in sub_module_list:
    #                 insert_params = {
    #                     'module_name': module_name,
    #                     'method_name': method_name,
    #                     'sub_method_name':'',
    #                     'sub_module_name': sub_module_info.get('sub_module_name'),
    #                     'sub_module_parameter_name': sub_module_info.get('sub_module_dto_name'),
    #                 }
    #
    #                 exec_insert('insert_module_method_relation_info', insert_params)

    def merge_cobol_lines(self, lines: List[str], cobol_keywords) -> List[str]:
        result = []
        current_line = ""
        in_function_body = False  # SECTION. 以降、-EXT. 以前を対象
        after_function_exit = False  # -EXT. 以降の行を処理しない

        for line in lines:
            stripped = line.strip()

            # 関数定義行判定
            if not in_function_body and stripped.endswith(FUNCTION_ENTRY_PATTERN):
                if current_line:
                    result.append(current_line)
                    current_line = ""
                result.append(line)
                in_function_body = True
                continue

            # 関数終了行判定
            if in_function_body and stripped.endswith(FUNCTION_EXIT_PATTERN):
                if current_line:
                    result.append(current_line)
                    current_line = ""
                result.append(line)
                after_function_exit = True
                continue

            # -EXT. 以降の行：処理せずそのまま出力
            if after_function_exit:
                result.append(line)
                continue

            # 本体処理（SECTION. から -EXT. まで）
            if in_function_body:
                # 空行処理
                if not stripped:
                    if current_line:
                        result.append(current_line)
                        current_line = ""
                    result.append(line)
                    continue

                # キーワード開始行判定
                if self._starts_with_keyword(stripped, cobol_keywords):
                    if current_line:
                        result.append(current_line)
                    current_line = line
                else:
                    # 継続行：前行に結合
                    if current_line:
                        current_line = f"{current_line.rstrip()}{CONTINUATION_SPACE}{line.lstrip()}"
                    else:
                        current_line = line
            else:
                # SECTION. 以前の行は、処理せずそのまま出力
                result.append(line)

        # 最終行の追加
        if current_line:
            result.append(current_line)

        return result

    def _starts_with_keyword(self, line: str, cobol_keywords) -> bool:
        if not line:
            return False
        upper_line = line.upper()
        return any(
            upper_line.startswith(kw.upper()) for kw in cobol_keywords
        )


    def run(self):
        _lines = self.read_file_lines(self.file_path)
        cobol_keywords = load_cobol_keywords_list()
        merge_cobol_list = self.merge_cobol_lines(_lines, cobol_keywords)

        sub_method_list, sub_module_list = self.extract_calls(merge_cobol_list, cobol_keywords)

        _sub_method_list = []
        for sub_method_info in sub_method_list:
            _sub_method_info = {
                'method_name': Path(self.file_path).name.split('.')[0],
                'sub_method_name':sub_method_info
            }
            _sub_method_list.append(_sub_method_info)
        if len(sub_method_list) == 0:
            _sub_method_info = {
                'method_name': Path(self.file_path).name.split('.')[0],
                'sub_method_name':''
            }
            _sub_method_list.append(_sub_method_info)

        self.sub_method_list = _sub_method_list

        _sub_module_list = []
        for sub_module_info in sub_module_list:
            _sub_module_info = {
                'method_name': Path(self.file_path).name.split('.')[0],
                'sub_module_name':sub_module_info.get('sub_module_name'),
                'sub_module_dto_name':sub_module_info.get('sub_module_dto_name')
            }
            _sub_module_list.append(_sub_module_info)
        self.sub_module_list = _sub_module_list

        # self.db_operation(sub_module_list, sub_method_list)

        super().run()



