package ai.decompile.analysis.service.parser;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import lombok.extern.log4j.Log4j2;
import org.springframework.stereotype.Component;

@Log4j2
@Component
public class TurboDumpParser implements LibrarySymbolParser {
  private static final String FORMAT_NAME = "TURBO_DUMP";

  private static final Map<String, String> CLASS_CATEGORY_MAP = new HashMap<>();

  static {
    CLASS_CATEGORY_MAP.put("TAppDictionary", "Core/Application");
    CLASS_CATEGORY_MAP.put("TApplication", "Core/Application");
    CLASS_CATEGORY_MAP.put("TModule", "Core/Application");
    CLASS_CATEGORY_MAP.put("TStatus", "Core/Application");
    CLASS_CATEGORY_MAP.put("TXCompatibility", "Core/Application");
    CLASS_CATEGORY_MAP.put("TFrameWindow", "UI/Window");
    CLASS_CATEGORY_MAP.put("TWindow", "UI/Window");
    CLASS_CATEGORY_MAP.put("TDialog", "UI/Window");
    CLASS_CATEGORY_MAP.put("TCommonDialog", "UI/Dialog");
    CLASS_CATEGORY_MAP.put("TChooseColorDialog", "UI/Dialog");
    CLASS_CATEGORY_MAP.put("TChooseFontDialog", "UI/Dialog");
    CLASS_CATEGORY_MAP.put("TButtonGadget", "UI/Control");
    CLASS_CATEGORY_MAP.put("TButtonGadgetEnabler", "UI/Control");
    CLASS_CATEGORY_MAP.put("TGadget", "UI/Control");
    CLASS_CATEGORY_MAP.put("TControlGadget", "UI/Control");
    CLASS_CATEGORY_MAP.put("TControlBar", "UI/Control");
    CLASS_CATEGORY_MAP.put("TBorderGadget", "UI/Control");
    CLASS_CATEGORY_MAP.put("TBitmapGadget", "UI/Control");
    CLASS_CATEGORY_MAP.put("TBitmap", "Graphics/Bitmap");
    CLASS_CATEGORY_MAP.put("TDib", "Graphics/Bitmap");
    CLASS_CATEGORY_MAP.put("TDibDC", "Graphics/Bitmap");
    CLASS_CATEGORY_MAP.put("TPalette", "Graphics/Palette");
    CLASS_CATEGORY_MAP.put("TIcon", "Graphics/Icon");
    CLASS_CATEGORY_MAP.put("TBrush", "Graphics/Drawing");
    CLASS_CATEGORY_MAP.put("THatch8x8Brush", "Graphics/Drawing");
    CLASS_CATEGORY_MAP.put("TPen", "Graphics/Drawing");
    CLASS_CATEGORY_MAP.put("TFont", "Graphics/Font");
    CLASS_CATEGORY_MAP.put("TDC", "Graphics/DC");
    CLASS_CATEGORY_MAP.put("TCreatedDC", "Graphics/DC");
    CLASS_CATEGORY_MAP.put("TIC", "Graphics/DC");
    CLASS_CATEGORY_MAP.put("TColor", "Graphics/Color");
    CLASS_CATEGORY_MAP.put("TCursor", "UI/Cursor");
    CLASS_CATEGORY_MAP.put("TClipboard", "UI/Clipboard");
    CLASS_CATEGORY_MAP.put("TClipboardViewer", "UI/Clipboard");
    CLASS_CATEGORY_MAP.put("TBitSet", "Core/DataTypes");
    CLASS_CATEGORY_MAP.put("TCharSet", "Core/DataTypes");
    CLASS_CATEGORY_MAP.put("TCelArray", "UI/Control");
    CLASS_CATEGORY_MAP.put("TEventHandler", "Core/EventSystem");
    CLASS_CATEGORY_MAP.put("TResponseTableEntry", "Core/EventSystem");
  }

  @Override
  public String getFormatName() {
    return FORMAT_NAME;
  }

  @Override
  public boolean canParse(String rawContent) {
    if (Objects.isNull(rawContent) || rawContent.isBlank()) {
      return false;
    }

    return rawContent.contains("Turbo Dump") && rawContent.contains("MSLIBR");
  }

  @Override
  public List<ParsedSymbol> parse(String rawContent) {
    if (Objects.isNull(rawContent) || rawContent.isBlank()) {
      return List.of();
    }

    var text = rawContent.replace("\r\n", "\n");
    var symbols = new ArrayList<ParsedSymbol>();
    var lines = text.split("\n");

    StringBuilder currentBlock = new StringBuilder();
    var inBlock = false;

    for (var line : lines) {
      if (line.contains("THEADR  ")) {
        inBlock = true;
        currentBlock = new StringBuilder();
        currentBlock.append(line).append("\n");
      } else if (inBlock) {
        if (line.contains("MODEND")) {
          currentBlock.append(line).append("\n");
          var symbol = parseBlock(currentBlock.toString());
          if (Objects.nonNull(symbol)) {
            symbols.add(symbol);
          }

          inBlock = false;
          currentBlock = new StringBuilder();
        } else {
          currentBlock.append(line).append("\n");
        }
      }
    }

    log.info("TurboDumpParser extracted {} symbols", symbols.size());
    return symbols;
  }

  private ParsedSymbol parseBlock(String block) {
    var lines = block.split("\n");

    String symbolName = null;
    String moduleName = null;
    String internalName = null;
    int ordinal = -1;

    for (var line : lines) {
      var trimmed = line.trim();

      if (trimmed.contains("THEADR  ")) {
        symbolName = extractAfter(extractAfterHexPrefix(trimmed), "THEADR  ");
      } else if (trimmed.startsWith("Module Name:")) {
        moduleName = extractAfter(trimmed, "Module Name:");
      } else if (trimmed.startsWith("Internal Name:")) {
        internalName = extractAfter(trimmed, "Internal Name:");
      } else if (trimmed.startsWith("ordinal:")) {
        try {
          ordinal = Integer.parseInt(extractAfter(trimmed, "ordinal:"));
        } catch (NumberFormatException e) {
          log.debug("Failed to parse ordinal from line: '{}'", trimmed);
        }
      }
    }

    if (ordinal < 0 || Objects.isNull(symbolName) || symbolName.isBlank()) {
      return null;
    }

    var effectiveName =
        Objects.nonNull(internalName) && !internalName.isBlank() ? internalName : symbolName;
    var effectiveModule = Objects.nonNull(moduleName) ? moduleName : "unknown";

    var type = classifySymbolType(effectiveName);
    var className = extractClassName(effectiveName);
    var params = extractParameters(effectiveName);
    var category = classifyCategory(className, effectiveName);

    return new ParsedSymbol(
        effectiveModule, className, effectiveName, ordinal, type, params, category);
  }

  private String extractAfterHexPrefix(String text) {
    if (text.length() >= 7 && text.substring(0, 6).matches("[0-9A-Fa-f]{6}")) {
      return text.substring(6).trim();
    }

    return text;
  }

  private String extractAfter(String text, String prefix) {
    var idx = text.indexOf(prefix);
    if (idx < 0) {
      return "";
    }

    return text.substring(idx + prefix.length()).trim();
  }

  private String classifySymbolType(String name) {
    if (name.contains("::~")
        || (name.contains("::") && name.substring(name.lastIndexOf("::") + 2).startsWith("~"))) {
      return "DESTRUCTOR";
    }

    if (name.contains("::")) {
      var colonIdx = name.lastIndexOf("::");
      var methodPart = name.substring(colonIdx + 2);
      var className = name.substring(0, colonIdx);
      var methodName =
          methodPart.contains("(") ? methodPart.substring(0, methodPart.indexOf('(')) : methodPart;

      if (methodName.equals(className)) {
        return "CONSTRUCTOR";
      }

      return "METHOD";
    }

    return "FUNCTION";
  }

  private String extractClassName(String name) {
    var colonIdx = name.indexOf("::");
    if (colonIdx < 0) {
      return null;
    }

    return name.substring(0, colonIdx).trim();
  }

  private String extractParameters(String name) {
    var parenStart = name.indexOf('(');
    if (parenStart < 0) {
      return null;
    }

    var parenEnd = name.lastIndexOf(')');
    if (parenEnd <= parenStart) {
      return null;
    }

    var params = name.substring(parenStart + 1, parenEnd).trim();
    if (params.isEmpty()) {
      return null;
    }

    return params;
  }

  private String classifyCategory(String className, String symbolName) {
    if (Objects.nonNull(className)) {
      var category = CLASS_CATEGORY_MAP.get(className);
      if (Objects.nonNull(category)) {
        return category;
      }

      if (className.startsWith("TFrame")) {
        return "UI/Window";
      }

      if (className.startsWith("TButton") || className.startsWith("TControl")) {
        return "UI/Control";
      }

      if (className.startsWith("TBrush") || className.startsWith("TPen")) {
        return "Graphics/Drawing";
      }

      if (className.startsWith("TBitmap") || className.startsWith("TPalette")) {
        return "Graphics/Bitmap";
      }

      if (className.startsWith("TDialog")) {
        return "UI/Dialog";
      }

      if (className.startsWith("TDC") || className.startsWith("TIC")) {
        return "Graphics/DC";
      }
    }

    if (Objects.nonNull(symbolName)) {
      if (symbolName.startsWith("_Owl") || symbolName.startsWith("GETTASK")) {
        return "Core/Application";
      }

      if (symbolName.startsWith("NColors") || symbolName.startsWith("NBits")) {
        return "Graphics/Utility";
      }
    }

    return "Core/Other";
  }
}
