import re
from dataclasses import dataclass
from typing import List, Optional
from enum import Enum


class TokenType(Enum):
    KEYWORD = "KEYWORD"
    IDENTIFIER = "IDENTIFIER"
    NUMBER = "NUMBER"
    OPERATOR = "OPERATOR"
    DELIMITER = "DELIMITER"
    WHITESPACE = "WHITESPACE"
    COMMENT = "COMMENT"
    STRING = "STRING"
    EOF = "EOF"


@dataclass
class Token:
    type: TokenType
    value: str
    line: int
    column: int


class VerilogLexer:
    KEYWORDS = {
        'module', 'endmodule', 'input', 'output', 'inout',
        'wire', 'reg', 'integer', 'real', 'parameter',
        'begin', 'end', 'if', 'else', 'always', 'initial',
        'assign', 'case', 'default', 'for', 'while',
        'function', 'task', 'posedge', 'negedge', 'or', 'and'
    }

    OPERATORS = {
        '+', '-', '*', '/', '%', '<<', '>>', '&', '|', '^', '~',
        '==', '!=', '<', '>', '<=', '>=', '&&', '||', '!', '?', ':', '=', '<='
    }

    DELIMITERS = {'(', ')', '[', ']', '{', '}', ',', ';', '.', '=', ':', '@'}

    def __init__(self, text: str):
        self.text = text
        self.pos = 0
        self.line = 1
        self.column = 1
        self.tokens: List[Token] = []

    def error(self, message: str):
        raise SyntaxError(f"Lexer error at line {self.line}, column {self.column}: {message}")

    def peek(self, offset: int = 0) -> Optional[str]:
        pos = self.pos + offset
        return self.text[pos] if pos < len(self.text) else None

    def advance(self):
        if self.pos < len(self.text):
            if self.text[self.pos] == '\n':
                self.line += 1
                self.column = 1
            else:
                self.column += 1
            self.pos += 1

    def skip_whitespace(self):
        while self.peek() and self.peek() in ' \t\n\r':
            self.advance()

    def read_comment(self) -> Optional[Token]:
        if self.peek() == '/' and self.peek(1) == '/':
            start_pos, start_col = self.pos, self.column
            while self.peek() and self.peek() != '\n':
                self.advance()
            return Token(TokenType.COMMENT, '', self.line, start_col)
        elif self.peek() == '/' and self.peek(1) == '*':
            start_line, start_col = self.line, self.column
            self.advance()
            self.advance()
            while self.pos < len(self.text):
                if self.peek() == '*' and self.peek(1) == '/':
                    self.advance()
                    self.advance()
                    return Token(TokenType.COMMENT, '', start_line, start_col)
                self.advance()
            self.error("Unclosed comment")
        return None

    def read_string(self) -> Token:
        quote = self.peek()
        start_col = self.column
        self.advance()
        value = ""
        while self.peek() and self.peek() != quote:
            if self.peek() == '\\':
                self.advance()
                if self.peek():
                    value += self.peek()
                    self.advance()
            else:
                value += self.peek()
                self.advance()
        if not self.peek():
            self.error("Unclosed string")
        self.advance()
        return Token(TokenType.STRING, value, self.line, start_col)

    def read_number(self) -> Token:
        start_col = self.column
        value = ""
        while self.peek() and (self.peek().isdigit() or self.peek() in "abcdefxXzZ_"):
            value += self.peek()
            self.advance()
        return Token(TokenType.NUMBER, value, self.line, start_col)

    def read_identifier(self) -> Token:
        start_col = self.column
        value = ""
        while self.peek() and (self.peek().isalnum() or self.peek() in "_$"):
            value += self.peek()
            self.advance()
        token_type = TokenType.KEYWORD if value.lower() in self.KEYWORDS else TokenType.IDENTIFIER
        return Token(token_type, value, self.line, start_col)

    def read_operator(self) -> Token:
        start_col = self.column
        potential_ops = []
        for length in [3, 2, 1]:
            candidate = self.text[self.pos:self.pos+length]
            if candidate in self.OPERATORS:
                potential_ops.append(candidate)
        if potential_ops:
            op = max(potential_ops, key=len)
            for _ in range(len(op)):
                self.advance()
            return Token(TokenType.OPERATOR, op, self.line, start_col)
        return None

    def tokenize(self) -> List[Token]:
        self.tokens = []
        while self.pos < len(self.text):
            self.skip_whitespace()
            if self.pos >= len(self.text):
                break

            current = self.peek()

            comment_token = self.read_comment()
            if comment_token:
                continue

            if current in '"\'':
                self.tokens.append(self.read_string())
            elif current.isdigit():
                self.tokens.append(self.read_number())
            elif current.isalpha() or current in "_$":
                self.tokens.append(self.read_identifier())
            elif current in self.DELIMITERS:
                start_col = self.column
                self.tokens.append(Token(TokenType.DELIMITER, current, self.line, start_col))
                self.advance()
            else:
                op_token = self.read_operator()
                if op_token:
                    self.tokens.append(op_token)
                else:
                    self.error(f"Unexpected character: {current}")

        self.tokens.append(Token(TokenType.EOF, "", self.line, self.column))
        return self.tokens
