#!/usr/bin/env python3    
import sys    
import re    
import requests    
import markdown    
from rich.console import Console    
from rich.markdown import Markdown    
from markdown2 import markdown as markdown_to_html    
from pygments import highlight    
from pygments.lexers import get_lexer_by_name    
from pygments.formatters import TerminalFormatter    

ANSI_CODES = {    
    'bold': '\033[1m', 'italic': '\033[3m', 'underline': '\033[4m', 'reset': '\033[0m',    
    'bold_italic': '\033[1m\033[3m', 'bold_underline': '\033[1m\033[4m',    
    'italic_underline': '\033[3m\033[4m', 'bold_italic_underline': '\033[1m\033[3m\033[4m',    
    'strikethrough': '\033[9m', 'italic_bold_underline': '\033[3m\033[1m\033[4m',    
    'double_underline': '\033[21m', 'inverse': '\033[7m', 'hidden': '\033[8m',    
    'fraktur': '\033[20m', 'normal': '\033[22m', 'blink': '\033[5m', 'reverse': '\033[7m',    
    'faint': '\033[2m', 'yellow': '\033[33m', 'blue': '\033[34m', 'green': '\033[32m'    
}    

def apply_ansi_style(text, *styles):    
    return f"{''.join(ANSI_CODES.get(style, '') for style in styles)}{text}{ANSI_CODES['reset']}"    

def highlight_code(code, lang):    
    try:    
        lexer = get_lexer_by_name(lang, stripall=True)    
        return highlight(code, lexer, TerminalFormatter())    
    except:    
        return apply_ansi_style(code, 'italic')    

def render_markdown(text):    
    text = re.sub(r'^(#{1,6})\s*(.*)', lambda m: apply_ansi_style(m.group(2), 'bold_underline'), text, flags=re.M)    
    text = re.sub(r'\*\*\*(.*?)\*\*\*', lambda m: apply_ansi_style(m.group(1), 'bold_italic'), text)    
    text = re.sub(r'\*\*(.*?)\*\*', lambda m: apply_ansi_style(m.group(1), 'bold'), text)    
    text = re.sub(r'\*(.*?)\*', lambda m: apply_ansi_style(m.group(1), 'italic'), text)    
    text = re.sub(r'__(.*?)__', lambda m: apply_ansi_style(m.group(1), 'underline'), text)    
    text = re.sub(r'~~(.*?)~~', lambda m: apply_ansi_style(m.group(1), 'strikethrough'), text)    
    text = re.sub(r'^\> (.*)', lambda m: apply_ansi_style(m.group(1), 'italic_blue'), text, flags=re.M)    
    text = re.sub(r'^[*-+]\s+(.*)', lambda m: f"- {apply_ansi_style(m.group(1), 'green')}", text, flags=re.M)    
    text = re.sub(r'^\d+\.\s+(.*)', lambda m: f"1. {apply_ansi_style(m.group(1), 'yellow')}", text, flags=re.M)    
    text = re.sub(r'`([^`]+)`', lambda m: apply_ansi_style(m.group(1), 'inverse'), text)    
    text = re.sub(r'([^]+)([^)]+)', lambda m: apply_ansi_style(f"{m.group(1)} ({m.group(2)})", 'blue'), text)    
    text = re.sub(r' (.*)', lambda m: f"[ ] {apply_ansi_style(m.group(1), 'faint')}", text)    
    text = re.sub(r'x (.*)', lambda m: f"[✔] {apply_ansi_style(m.group(1), 'green')}", text)    

    code_blocks = re.findall(r'```(\w+)?\n(.*?)```', text, re.S)    
    for lang, code in code_blocks:    
        highlighted = highlight_code(code, lang if lang else "plaintext")    
        text = text.replace(f"```{lang}\n{code}```", highlighted)    

    tables = re.findall(r'\|(.+)\|\n\|[-\s|]+\|\n((?:\|.+\|\n?)+)', text)    
    for header, rows in tables:    
        header_cells = header.split("|")    
        row_cells = [row.split("|") for row in rows.strip().split("\n")]    
        formatted_table = apply_ansi_style("| " + " | ".join(header_cells) + " |", 'bold_underline')    
        for row in row_cells:    
            formatted_table += "\n| " + " | ".join(row) + " |"    
        text = text.replace(f"|{header}|\n|{'-' * len(header)}|\n{rows}", formatted_table)    

    return text    

def fetch_github_raw(url):    
    try:    
        response = requests.get(url)    
        response.raise_for_status()    
        return response.text    
    except requests.exceptions.RequestException as e:    
        print(f"Error fetching file: {e}")    
        sys.exit(1)    

def view_markdown(content, word_limit=None, output_html=False):    
    content = render_markdown(content)    
    if output_html:    
        content = markdown_to_html(content, extras=["tables", "fenced-code-blocks", "strike", "task_list"])    

    words = content.split()    
    if word_limit:    
        content = ' '.join(words[:word_limit])    

    console = Console()    
    markdown = Markdown(content)    
    console.print(markdown)    

def get_file_content(filename_or_url):    
    if filename_or_url.startswith('https://raw.githubusercontent.com'):    
        return fetch_github_raw(filename_or_url)    
    else:    
        try:    
            with open(filename_or_url, 'r') as file:    
                return file.read()    
        except FileNotFoundError:    
            print(f"File {filename_or_url} not found.")    
            sys.exit(1)    

def convert_to_html(content):    
    return markdown_to_html(content, extras=["tables", "fenced-code-blocks", "strike", "task_list"])    

if __name__ == '__main__':    
    if len(sys.argv) < 2:    
        print("Usage: view <filename>.md or <GitHub raw link> [word_limit] [--html]")    
        sys.exit(1)    

    filename_or_url = sys.argv[1]    
    word_limit = None    
    output_html = False    

    if len(sys.argv) == 3 and sys.argv[2].isdigit():    
        word_limit = int(sys.argv[2])    
    elif len(sys.argv) > 3 and sys.argv[2] == '--html':    
        output_html = True    
        if len(sys.argv) == 4 and sys.argv[3].isdigit():    
            word_limit = int(sys.argv[3])    

    content = get_file_content(filename_or_url)    
    view_markdown(content, word_limit, output_html)