#include <ctype.h>
#include <stdbool.h>
#include <stdio.h>
#include <string.h>

// --- ANSI Escape Codes ---
#define RESET "\033[0m"
#define BOLD "\033[1m"
#define ITALIC "\033[3m"
#define STRIKETHROUGH "\033[9m"

#define FG_YELLOW "\033[33m"
#define FG_BLUE "\033[34m"
#define FG_CYAN "\033[36m"
#define FG_MAGENTA "\033[35m"

#define BG_YELLOW_FG_BLACK "\033[43;30m"

/**
 * @brief Checks if a given word is a programming language keyword.
 * * @param word The word to check.
 * @return int 1 if it's a keyword, 0 otherwise.
 */
int is_keyword(const char *word) {
  // A comprehensive list of keywords from various popular languages.
  const char *keywords[] = {
      // C keywords
      "int", "float", "double", "char", "void", "if", "else", "while", "for",
      "return", "struct", "printf",
      // Rust keywords
      "let", "fn", "impl", "match", "enum", "trait", "use", "mod", "pub",
      "const", "static",
      // Python keywords
      "def", "class", "import", "from", "as", "with", "try", "except",
      "finally", "lambda", "async", "await",
      // Java keywords
      "public", "private", "protected", "class", "interface", "extends",
      "implements", "new", "static", "final", "synchronized",
      // JavaScript keywords
      "var", "let", "const", "function", "async", "await", "if", "else",
      "switch", "case", "break", "continue", "return",
      // C++ keywords
      "namespace", "template", "typename", "class", "public", "private",
      "protected", "virtual", "override", "friend", "new",
      // Go keywords
      "package", "import", "func", "var", "const", "type", "interface",
      "struct", "go", "defer", "select",
      // Bash keywords
      "if", "then", "else", "elif", "fi", "case", "esac", "for", "while",
      "until", "do", "done", "function",
      // C# keywords
      "namespace", "using", "class", "interface", "public", "private",
      "protected", "static", "void", "new", "override"};

  for (size_t i = 0; i < sizeof(keywords) / sizeof(keywords[0]); i++) {
    if (strcmp(word, keywords[i]) == 0) {
      return 1;
    }
  }
  return 0;
}

/**
 * @brief Applies basic syntax highlighting to a string of code.
 * * @param code The code string to highlight.
 */
void highlight_code(const char *code) {
  const char *ptr = code;
  char buffer[4096];
  size_t index = 0;

  while (*ptr) {
    // Highlight keywords
    if (isalpha((unsigned char)*ptr) || *ptr == '_') {
      while (isalnum((unsigned char)*ptr) || *ptr == '_') {
        buffer[index++] = *ptr++;
      }
      buffer[index] = '\0';

      if (is_keyword(buffer)) {
        printf(FG_BLUE "%s" RESET FG_YELLOW, buffer);
      } else {
        printf("%s", buffer);
      }
      index = 0;
      // Highlight numbers
    } else if (isdigit((unsigned char)*ptr)) {
      while (isdigit((unsigned char)*ptr)) {
        buffer[index++] = *ptr++;
      }
      buffer[index] = '\0';
      printf(FG_CYAN "%s" RESET FG_YELLOW, buffer);
      index = 0;
      // Print other characters as-is
    } else {
      putchar(*ptr);
      ptr++;
    }
  }
}

/**
 * @brief Parses a Markdown string and prints it to the console with ANSI color
 * codes.
 *
 * This version supports a wide range of Markdown features, including:
 * - Headers (H1-H6)
 * - Bold (**, __) and Italic (*, _) text
 * - Strikethrough (~~) and Highlight (==)
 * - Blockquotes (>), Nested Ordered (1.) and Unordered lists (*, -, +)
 * - Inline code (`) and full code blocks (```) with syntax highlighting
 * - Links ([text](url)) and Horizontal rules (---, ***)
 * * @param markdown The raw Markdown string to parse.
 */
void parse_markdown_to_ansi(const char *markdown) {
  const char *ptr = markdown;
  bool is_start_of_line = true;

  while (*ptr) {
    // --- Code Blocks (```) ---
    if (is_start_of_line && strncmp(ptr, "```", 3) == 0) {
      ptr += 3;
      while (*ptr && *ptr != '\n')
        ptr++;
      if (*ptr)
        ptr++;

      const char *code_start = ptr;
      const char *code_end = strstr(code_start, "```");

      if (code_end) {
        char block_buffer[code_end - code_start + 1];
        strncpy(block_buffer, code_start, code_end - code_start);
        block_buffer[code_end - code_start] = '\0';

        printf(FG_YELLOW);
        highlight_code(block_buffer);
        printf(RESET);

        ptr = code_end + 3;
        if (*ptr == '\n')
          ptr++;
        is_start_of_line = true;
        continue;
      } else {
        printf(FG_YELLOW);
        highlight_code(code_start);
        printf(RESET);
        break;
      }
    }

    // --- Block-level Elements (checked at the start of a line) ---
    if (is_start_of_line) {
      const char *line_start_ptr = ptr;
      int indent_level = 0;
      while (*ptr == ' ') {
        indent_level++;
        ptr++;
      }

      bool block_processed = true;
      if (strncmp(ptr, "###### ", 7) == 0) {
        printf(BOLD FG_YELLOW);
        ptr += 7;
      } else if (strncmp(ptr, "##### ", 6) == 0) {
        printf(BOLD FG_YELLOW);
        ptr += 6;
      } else if (strncmp(ptr, "#### ", 5) == 0) {
        printf(BOLD FG_YELLOW);
        ptr += 5;
      } else if (strncmp(ptr, "### ", 4) == 0) {
        printf(BOLD FG_YELLOW);
        ptr += 4;
      } else if (strncmp(ptr, "## ", 3) == 0) {
        printf(BOLD FG_YELLOW);
        ptr += 3;
      } else if (strncmp(ptr, "# ", 2) == 0) {
        printf(BOLD FG_YELLOW);
        ptr += 2;
      } else if ((strncmp(ptr, "---", 3) == 0 || strncmp(ptr, "***", 3) == 0) &&
                 (*(ptr + 3) == '\n' || *(ptr + 3) == '\0')) {
        printf(FG_CYAN "───────────────────────────────────────────────────────"
                       "──────────" RESET "\n");
        ptr += 3;
        if (*ptr == '\n')
          ptr++;
        is_start_of_line = true;
        continue;
      } else if (strncmp(ptr, "> ", 2) == 0) {
        for (int i = 0; i < indent_level; i++)
          putchar(' ');
        printf(ITALIC FG_CYAN "▎ " RESET);
        ptr += 2;
        is_start_of_line = false;
        continue;
      } else if ((*ptr == '*' || *ptr == '-' || *ptr == '+') &&
                 *(ptr + 1) == ' ') {
        for (int i = 0; i < indent_level; i++)
          putchar(' ');
        printf(FG_MAGENTA "• " RESET);
        ptr += 2;
        is_start_of_line = false;
        continue;
      } else {
        const char *temp_ptr = ptr;
        while (isdigit((unsigned char)*temp_ptr))
          temp_ptr++;
        if (temp_ptr > ptr && *temp_ptr == '.' && *(temp_ptr + 1) == ' ') {
          for (int i = 0; i < indent_level; i++)
            putchar(' ');
          printf(FG_MAGENTA);
          fwrite(ptr, 1, (temp_ptr - ptr) + 1, stdout);
          printf(" " RESET);
          ptr = temp_ptr + 2;
          is_start_of_line = false;
          continue;
        } else {
          block_processed = false;
          ptr = line_start_ptr;
        }
      }
      if (block_processed) {
        while (*ptr && *ptr != '\n')
          putchar(*ptr++);
        printf(RESET "\n");
        if (*ptr == '\n')
          ptr++;
        is_start_of_line = true;
        continue;
      }
    }

    // --- Inline Elements (order is important) ---
    if (strncmp(ptr, "***", 3) == 0 || strncmp(ptr, "___", 3) == 0) {
      const char *marker = strncmp(ptr, "***", 3) == 0 ? "***" : "___";
      printf(BOLD ITALIC);
      ptr += 3;
      const char *end = strstr(ptr, marker);
      if (end) {
        fwrite(ptr, 1, end - ptr, stdout);
        ptr = end + 3;
      } else {
        fputs(ptr, stdout);
        ptr += strlen(ptr);
      }
      printf(RESET);
      continue;
    }
    if (strncmp(ptr, "**", 2) == 0 || strncmp(ptr, "__", 2) == 0) {
      const char *marker = strncmp(ptr, "**", 2) == 0 ? "**" : "__";
      printf(BOLD);
      ptr += 2;
      const char *end = strstr(ptr, marker);
      if (end) {
        fwrite(ptr, 1, end - ptr, stdout);
        ptr = end + 2;
      } else {
        fputs(ptr, stdout);
        ptr += strlen(ptr);
      }
      printf(RESET);
      continue;
    }
    if ((*ptr == '*' || *ptr == '_') && !isspace(*(ptr - 1)) &&
        !isspace(*(ptr + 1))) {
      char marker = *ptr;
      printf(ITALIC);
      ptr++;
      const char *start = ptr;
      while (*ptr && *ptr != marker)
        ptr++;
      if (*ptr == marker) {
        fwrite(start, 1, ptr - start, stdout);
        ptr++;
      } else {
        putchar(marker);
        ptr = start;
      }
      printf(RESET);
      continue;
    }
    if (strncmp(ptr, "~~", 2) == 0) {
      printf(STRIKETHROUGH);
      ptr += 2;
      const char *end = strstr(ptr, "~~");
      if (end) {
        fwrite(ptr, 1, end - ptr, stdout);
        ptr = end + 2;
      } else {
        fputs(ptr, stdout);
        ptr += strlen(ptr);
      }
      printf(RESET);
      continue;
    }
    if (strncmp(ptr, "==", 2) == 0) {
      printf(BG_YELLOW_FG_BLACK);
      ptr += 2;
      const char *end = strstr(ptr, "==");
      if (end) {
        fwrite(ptr, 1, end - ptr, stdout);
        ptr = end + 2;
      } else {
        fputs(ptr, stdout);
        ptr += strlen(ptr);
      }
      printf(RESET);
      continue;
    }
    if (*ptr == '`' && *(ptr + 1) != '`') {
      printf(FG_YELLOW);
      ptr++;
      const char *start = ptr;
      while (*ptr && *ptr != '`')
        ptr++;
      fwrite(start, 1, ptr - start, stdout);
      if (*ptr == '`')
        ptr++;
      printf(RESET);
      continue;
    }
    if (*ptr == '[') {
      const char *text_start = ptr + 1;
      const char *text_end = strchr(text_start, ']');
      if (text_end && *(text_end + 1) == '(') {
        const char *url_start = text_end + 2;
        const char *url_end = strchr(url_start, ')');
        if (url_end) {
          printf(FG_BLUE);
          fwrite(text_start, 1, text_end - text_start, stdout);
          printf(RESET " (");
          printf(ITALIC FG_CYAN);
          fwrite(url_start, 1, url_end - url_start, stdout);
          printf(RESET ")");
          ptr = url_end + 1;
          continue;
        }
      }
    }

    // --- Default Character ---
    if (*ptr == '\n') {
      is_start_of_line = true;
    } else if (!isspace((unsigned char)*ptr)) {
      is_start_of_line = false;
    }
    putchar(*ptr);
    ptr++;
  }
}
