import sys
import re
import discord
from discord.ext import commands
import asyncio
import threading
from datetime import timedelta

# --- TOKEN TYPES ---
TT_INT = 'INT'
TT_STRING = 'STRING'
TT_IDENTIFIER = 'IDENTIFIER'
TT_COLON = 'COLON'
TT_LPAREN = 'LPAREN'
TT_RPAREN = 'RPAREN'
TT_DOT = 'DOT'
TT_EQUALS = 'EQUALS'
TT_COMMA = 'COMMA'
TT_LBRACKET = 'LBRACKET'
TT_RBRACKET = 'RBRACKET'
TT_PLUS = 'PLUS'
TT_MINUS = 'MINUS'
TT_BOOL = 'BOOL'
TT_SEMICOLON = 'SEMICOLON'

# Comparison Operators
TT_EE = 'EE'
TT_LTE = 'LTE'
TT_GTE = 'GTE'
TT_LT = 'LT'
TT_GT = 'GT'

class Token:
    def __init__(self, type_, value=None):
        self.type = type_
        self.value = value

    def __repr__(self):
        if self.value is not None:
            return f'{self.type}:{self.value}'
        return f'{self.type}'

# --- LEXER ---
class Lexer:
    def __init__(self, text):
        # Handle comments
        text = re.sub(r'~~.*?~~', '', text, flags=re.DOTALL)
        text = re.sub(r'~~.*', '', text)
        self.text = text
        self.pos = -1
        self.current_char = None
        self.advance()

    def advance(self):
        self.pos += 1
        if self.pos < len(self.text):
            self.current_char = self.text[self.pos]
        else:
            self.current_char = None

    def peek(self):
        peek_pos = self.pos + 1
        if peek_pos < len(self.text):
            return self.text[peek_pos]
        return None

    def make_tokens(self):
        tokens = []
        while self.current_char is not None:
            if self.current_char in ' \t\n\r':
                self.advance()
            elif self.current_char.isdigit():
                tokens.append(self.make_number())
            elif self.current_char.isalpha():
                tokens.append(self.make_identifier())
            elif self.current_char == '"' or self.current_char == "'":
                tokens.append(self.make_string())
            elif self.current_char == '+':
                tokens.append(Token(TT_PLUS))
                self.advance()
            elif self.current_char == '-':
                tokens.append(Token(TT_MINUS))
                self.advance()
            elif self.current_char == ';':
                tokens.append(Token(TT_SEMICOLON))
                self.advance()
            elif self.current_char == '=':
                if self.peek() == '=':
                    self.advance()
                    tokens.append(Token(TT_EE))
                    self.advance()
                elif self.peek() == '>':
                    self.advance()
                    tokens.append(Token(TT_GTE))
                    self.advance()
                else:
                    tokens.append(Token(TT_EQUALS))
                    self.advance()
            elif self.current_char == '<':
                if self.peek() == '=':
                    self.advance()
                    tokens.append(Token(TT_LTE))
                    self.advance()
                else:
                    tokens.append(Token(TT_LT))
                    self.advance()
            elif self.current_char == '>':
                if self.peek() == '=':
                    self.advance()
                    tokens.append(Token(TT_GTE))
                    self.advance()
                else:
                    tokens.append(Token(TT_GT))
                    self.advance()
            elif self.current_char == ':':
                tokens.append(Token(TT_COLON))
                self.advance()
            elif self.current_char == '(':
                tokens.append(Token(TT_LPAREN))
                self.advance()
            elif self.current_char == ')':
                tokens.append(Token(TT_RPAREN))
                self.advance()
            elif self.current_char == '[':
                tokens.append(Token(TT_LBRACKET))
                self.advance()
            elif self.current_char == ']':
                tokens.append(Token(TT_RBRACKET))
                self.advance()
            elif self.current_char == '.':
                tokens.append(Token(TT_DOT))
                self.advance()
            elif self.current_char == ',':
                tokens.append(Token(TT_COMMA))
                self.advance()
            else:
                self.advance()
        return tokens, None

    def make_number(self):
        num_str = ''
        while self.current_char is not None and self.current_char.isdigit():
            num_str += self.current_char
            self.advance()
        return Token(TT_INT, int(num_str))

    def make_string(self):
        string = ''
        quote_type = self.current_char
        self.advance()
        while self.current_char is not None and self.current_char != quote_type:
            string += self.current_char
            self.advance()
        self.advance()
        return Token(TT_STRING, string)

    def make_identifier(self):
        id_str = ''
        while self.current_char is not None and (self.current_char.isalnum() or self.current_char == '_'):
            id_str += self.current_char
            self.advance()
        if id_str == 'true':
            return Token(TT_BOOL, True)
        if id_str == 'false':
            return Token(TT_BOOL, False)
        return Token(TT_IDENTIFIER, id_str)

# --- NODES ---
class CondNode:
    def __init__(self, mode, condition_tokens, body):
        self.mode = mode
        self.condition_tokens = condition_tokens
        self.body = body

class DefineNode:
    def __init__(self, dtype, name, args=None, body=None):
        self.dtype = dtype
        self.name = name
        self.args = args
        self.body = body

class SetNode:
    def __init__(self, library, name, action, expression, sub_action=None):
        self.library = library
        self.name = name
        self.action = action
        self.expression = expression
        self.sub_action = sub_action

class LogNode:
    def __init__(self, lib, name, index=None, raw_val=None):
        self.lib = lib
        self.name = name
        self.index = index
        self.raw_val = raw_val

class FuncExecuteNode:
    def __init__(self, name, args):
        self.name = name
        self.args = args

# --- PARSER ---
class Parser:
    def __init__(self, tokens):
        self.tokens = tokens
        self.tok_idx = -1
        self.current_tok = None
        self.last_tok_idx = -1
        self.advance()

    def advance(self):
        self.tok_idx += 1
        if self.tok_idx < len(self.tokens):
            self.current_tok = self.tokens[self.tok_idx]
        else:
            self.current_tok = None

    def get_block(self):
        body = []
        while self.current_tok is not None:
            if self.current_tok.type == TT_COLON:
                if self.tok_idx + 1 < len(self.tokens):
                    if self.tokens[self.tok_idx + 1].value == 'end':
                        self.advance()
                        self.advance()
                        break
            stmt = self.statement()
            if stmt is not None:
                body.append(stmt)
            else:
                self.advance()
        return body

    def statement(self):
        if self.current_tok is None:
            return None

        if self.tok_idx == self.last_tok_idx:
            self.advance()
        self.last_tok_idx = self.tok_idx

        # --- COND ---
        if self.current_tok.value == 'cond':
            self.advance()
            self.advance()
            mode = self.current_tok.value
            self.advance()
            expr = []
            while self.current_tok is not None and self.current_tok.type != TT_RPAREN:
                expr.append(self.current_tok)
                self.advance()
            if self.current_tok is not None:
                self.advance()
                self.advance()
            return CondNode(mode, expr, self.get_block())

        # --- DEFINE ---
        if self.current_tok.value == 'define':
            self.advance()
            self.advance()
            self.advance()
            self.advance()
            dtype = self.current_tok.value
            self.advance()
            self.advance()
            name = self.current_tok.value
            self.advance()
            self.advance()
            if dtype == 'Func':
                self.advance()
                self.advance()
                args = []
                while self.current_tok is not None and self.current_tok.type != TT_RBRACKET:
                    if self.current_tok.type == TT_IDENTIFIER:
                        args.append(self.current_tok.value)
                    self.advance()
                if self.current_tok is not None:
                    self.advance()
                    self.advance()
                return DefineNode(dtype, name, args, self.get_block())
            return DefineNode(dtype, name)

        # --- LIBRARIES ---
        if self.current_tok.value in ('vars', 'arrs', 'funcs', 'intes', 'command'):
            lib = self.current_tok.value
            self.advance()
            self.advance()
            name = self.current_tok.value
            self.advance()
            self.advance()
            action = self.current_tok.value
            self.advance()

            # Handle command:GetChannel:Reply chaining
            sub_action = None
            if self.current_tok and self.current_tok.type == TT_COLON:
                self.advance()
                sub_action = self.current_tok.value
                self.advance()

            self.advance() # (

            if action == "OnCommandRecieve":
                cmd_name = self.current_tok.value
                self.advance()
                self.advance()
                msg_var = self.current_tok.value
                self.advance()
                self.advance()
                self.advance()
                self.advance()
                self.advance()
                args_map = {}
                while self.current_tok is not None and self.current_tok.type != TT_RBRACKET:
                    if self.current_tok.type == TT_IDENTIFIER:
                        arg_n = self.current_tok.value
                        self.advance()
                        self.advance()
                        arg_t = self.current_tok.value
                        self.advance()
                        args_map[arg_n] = arg_t
                    if self.current_tok is not None and self.current_tok.type == TT_COMMA:
                        self.advance()
                self.advance()
                self.advance()
                self.advance()
                return DefineNode("DiscordCommand", cmd_name, args={'msg': msg_var, 'args': args_map}, body=self.get_block())

            if action == "OnReady":
                self.advance()
                self.advance()
                return DefineNode("DiscordReady", name, body=self.get_block())

            expr = []
            while self.current_tok is not None and self.current_tok.type != TT_RPAREN:
                expr.append(self.current_tok)
                self.advance()
                if self.current_tok is not None and self.current_tok.type == TT_COMMA:
                    self.advance()
            if self.current_tok is not None:
                self.advance()
            
            if action == 'Execute':
                return FuncExecuteNode(name, expr)
            
            return SetNode(lib, name, action, expr, sub_action=sub_action)

        # --- LOG ---
        if self.current_tok.value == 'log':
            self.advance()
            self.advance()
            self.advance()
            self.advance()
            if self.current_tok is not None and self.current_tok.value in ('vars', 'arrs'):
                lib_type = self.current_tok.value
                self.advance()
                self.advance()
                var_name = self.current_tok.value
                self.advance()
                idx = None
                if self.current_tok is not None and self.current_tok.type == TT_LBRACKET:
                    self.advance()
                    idx = self.current_tok.value
                    self.advance()
                    self.advance()
                self.advance()
                return LogNode(lib_type, var_name, index=idx)
            else:
                raw = self.current_tok.value if self.current_tok else "None"
                self.advance()
                self.advance()
                return LogNode(None, None, raw_val=raw)

        return None

    def parse(self):
        ast = []
        while self.current_tok is not None:
            stmt = self.statement()
            if stmt is not None:
                ast.append(stmt)
            else:
                self.advance()
        return ast

# --- INTERPRETER ---
class Interpreter:
    def __init__(self):
        self.vars = {}
        self.arrs = {}
        self.funcs = {}
        self.temp_vars = {}
        self.intes = {}
        self.bot_token = None

    def resolve_value(self, tokens):
        if not tokens:
            return None
        if not isinstance(tokens, list):
            if tokens.type in (TT_INT, TT_STRING, TT_BOOL):
                return tokens.value
            if tokens.value in self.temp_vars:
                return self.temp_vars[tokens.value]
            if tokens.value in self.vars:
                return self.vars[tokens.value]
            return tokens.value
        
        if len(tokens) >= 3 and tokens[1].type == TT_DOT:
            lib = tokens[0].value
            var = tokens[2].value
            if lib == 'vars': return self.temp_vars.get(var, self.vars.get(var, 0))
            if lib == 'arrs': return self.arrs.get(var, [])
            if lib == 'intes': return self.intes.get(var)
        
        return self.resolve_value(tokens[0])

    def evaluate_expression(self, tokens):
        if not tokens:
            return None
        for i in range(len(tokens)):
            tok = tokens[i]
            if tok.type in (TT_EE, TT_LTE, TT_GTE, TT_LT, TT_GT, TT_PLUS, TT_MINUS):
                v1 = self.resolve_value(tokens[:i])
                v2 = self.resolve_value(tokens[i+1:])
                try:
                    if tok.type == TT_EE: return v1 == v2
                    if tok.type == TT_LTE: return v1 <= v2
                    if tok.type == TT_GTE: return v1 >= v2
                    if tok.type == TT_LT: return v1 < v2
                    if tok.type == TT_GT: return v1 > v2
                    if tok.type == TT_PLUS: return v1 + v2
                    if tok.type == TT_MINUS: return v1 - v2
                except:
                    return None
        return self.resolve_value(tokens)

    def visit(self, node):
        try:
            if isinstance(node, CondNode):
                if node.mode == 'if':
                    if self.evaluate_expression(node.condition_tokens):
                        for s in node.body: self.visit(s)
                elif node.mode == 'during':
                    while self.evaluate_expression(node.condition_tokens):
                        for s in node.body: self.visit(s)

            elif isinstance(node, DefineNode):
                if node.dtype == 'Var': self.vars[node.name] = None
                elif node.dtype == 'Arr': self.arrs[node.name] = []
                elif node.dtype == 'Func': self.funcs[node.name] = (node.args, node.body)
                elif node.dtype == 'Inte': self.intes[node.name] = None
                elif node.dtype == "DiscordCommand":
                    bot = self.vars.get('bot')
                    if bot:
                        @bot.command(name=node.name)
                        async def discord_callback(ctx, *args):
                            self.temp_vars[node.args['msg']] = ctx
                            for s in node.body: self.visit(s)
                            self.temp_vars.clear()
                elif node.dtype == "DiscordReady":
                    bot = self.vars.get('bot')
                    if bot:
                        @bot.event
                        async def on_ready():
                            for s in node.body: self.visit(s)

            elif isinstance(node, SetNode):
                if node.library == 'command':
                    self.handle_command_chain(node)
                    return

                val = self.evaluate_expression(node.expression)
                if node.library == 'vars':
                    if node.name == 'bot':
                        if node.action == 'GetBotFromToken':
                            self.vars[node.name] = commands.Bot(command_prefix="!", intents=discord.Intents.all())
                            self.bot_token = val
                        elif node.action == 'SetPrefix':
                            if self.vars.get('bot'): self.vars['bot'].command_prefix = val
                    else:
                        self.vars[node.name] = val
                elif node.library == 'intes':
                    if node.action == 'StartBot':
                        if self.bot_token and self.bot_token != 'YOUR_TOKEN':
                            bot = self.vars.get('bot')
                            if bot:
                                # Create the thread
                                bot_thread = threading.Thread(target=lambda: bot.run(self.bot_token))
                                bot_thread.daemon = False # Ensure it's not a daemon
                                bot_thread.start()
                                
                                # KEEP-ALIVE: Prevent the interpreter from exiting
                                print("[SILICORD]: Bot started. Press Ctrl+C to stop.")
                                try:
                                    while bot_thread.is_alive():
                                        bot_thread.join(timeout=1.0)
                                except KeyboardInterrupt:
                                    print("\n[SILICORD]: Shutting down...")
                                    sys.exit(0)
                        else:
                            print("[SILICORD ERROR]: Bot Token is missing.")
                    elif node.action in ('Kick', 'Ban', 'Timeout'):
                        self.handle_moderation(node.action, node.expression)

            elif isinstance(node, LogNode):
                if node.raw_val is not None:
                    print(f"[SILICON CONSOLE]: {node.raw_val}")
                else:
                    target = self.vars.get(node.name, self.arrs.get(node.name))
                    print(f"[SILICON CONSOLE]: {target}")

            elif isinstance(node, FuncExecuteNode):
                args_def, body = self.funcs.get(node.name, ([], []))
                for s in body: self.visit(s)

        except Exception as e:
            print(f"[SILICON RUNTIME ERROR]: {e}")

    def handle_command_chain(self, node):
        # Access the context from temp_vars (usually stored as 'cmd' or whatever the user named it)
        ctx = self.temp_vars.get(node.name)
        if not ctx: return

        if node.action == "GetChannel":
            text = self.evaluate_expression([node.expression[0]])
            bot = self.vars.get('bot')
            
            if node.sub_action == "Reply":
                coro = ctx.reply(text)
                asyncio.run_coroutine_threadsafe(coro, bot.loop)
            elif node.sub_action == "SendMsg":
                coro = ctx.send(text)
                asyncio.run_coroutine_threadsafe(coro, bot.loop)

    def handle_moderation(self, action, tokens):
        bot = self.vars.get('bot')
        user_obj = self.resolve_value(tokens[0])
        if not hasattr(user_obj, 'id'): return
        coro = None
        if action == 'Kick': coro = user_obj.kick()
        elif action == 'Ban': coro = user_obj.ban()
        elif action == 'Timeout':
            mins = self.resolve_value(tokens[1]) if len(tokens) > 1 else 10
            coro = user_obj.timeout(timedelta(minutes=mins))
        if coro: asyncio.run_coroutine_threadsafe(coro, bot.loop)

def run_file(filename):
    with open(filename, 'r') as f:
        script = f.read()
    lexer = Lexer(script)
    tokens, error = lexer.make_tokens()
    if error: return
    parser = Parser(tokens)
    ast = parser.parse()
    interpreter = Interpreter()
    for node in ast: interpreter.visit(node)

def main_cli():
    if len(sys.argv) > 1:
        run_file(sys.argv[1])
    else:
        print("Silicon v1.1.6")

if __name__ == "__main__":
    main_cli()