"""
MikroTik RouterBoard Password Generator

Reverse-engineered password generation algorithm based on MAC address analysis.

IMPORTANT DISCOVERY: Two different MAC addresses generate the SAME password:
  MAC: 18:FD:74:F9:04:FC → Password: c8fd-8bff
  MAC: 18:FD:74:F9:04:90 → Password: c8fd-8bff
  
  The only difference is the last byte (FC vs 90), proving it's NOT used!

Pattern discovered:
  - Only uses first 5 MAC bytes (byte 5 is ignored)
  - Byte 0: MAC[0] XOR 0xD0
  - Byte 1: MAC[1] (direct copy)
  - Byte 2: NOT(MAC[2])
  - Byte 3: 0xFF (constant or derived)

WARNING: This is based on reverse engineering two MAC addresses that both
produce the same password. More data points would help verify the constants.
"""


class MikroTikPasswordGenerator:
    """Generate MikroTik RouterBoard passwords from MAC addresses."""

    @staticmethod
    def parse_mac(mac_address):
        """
        Parse a MAC address string into a list of bytes.
        
        Accepts formats like "18:FD:74:F9:04:FC" or "18-FD-74-F9-04-FC"
        
        Args:
            mac_address (str): MAC address string
            
        Returns:
            list: List of 6 integers (0-255)
            
        Raises:
            ValueError: If MAC address format is invalid
        """
        mac_address = mac_address.upper()
        
        # Split by colon or dash
        import re
        parts = re.split(r'[:-]', mac_address)
        
        if len(parts) != 6:
            raise ValueError(f"Invalid MAC address: {mac_address}. Expected 6 octets.")
        
        try:
            mac_bytes = [int(part, 16) for part in parts]
        except ValueError:
            raise ValueError(f"Invalid MAC address format: {mac_address}")
        
        return mac_bytes

    @staticmethod
    def bitwise_not(byte):
        """
        Bitwise NOT operation (inverts all bits in a byte).
        
        Args:
            byte (int): Byte value (0-255)
            
        Returns:
            int: NOT(byte) as a byte (0-255)
        """
        return byte ^ 0xFF

    @staticmethod
    def xor_op(byte, mask):
        """
        XOR operation on a byte with a mask.
        
        Args:
            byte (int): Byte value (0-255)
            mask (int): XOR mask (0-255)
            
        Returns:
            int: byte XOR mask (0-255)
        """
        return (byte ^ mask) & 0xFF

    @staticmethod
    def format_password(pwd_bytes):
        """
        Format password bytes into MikroTik format "XXXX-XXXX".
        
        Args:
            pwd_bytes (list): List of 4 bytes
            
        Returns:
            str: Formatted password like "c8fd-8bff"
        """
        if len(pwd_bytes) != 4:
            raise ValueError(f"Expected 4 password bytes, got {len(pwd_bytes)}")
        
        hex_string = ''.join(f'{byte:02x}' for byte in pwd_bytes)
        return f"{hex_string[:4]}-{hex_string[4:]}"

    @staticmethod
    def generate_password(mac_address):
        """
        Generate a password from a MAC address.
        
        Uses only the first 5 bytes of the MAC address (byte 5 is ignored).
        
        Algorithm:
          PWD[0] = MAC[0] XOR 0xD0
          PWD[1] = MAC[1]           (direct copy)
          PWD[2] = NOT(MAC[2])      (bitwise NOT)
          PWD[3] = 0xFF             (constant or derived)
        
        Args:
            mac_address (str): MAC address string (e.g., "18:FD:74:F9:04:FC")
            
        Returns:
            str: Generated password (e.g., "c8fd-8bff")
            
        Example:
            >>> gen = MikroTikPasswordGenerator()
            >>> pwd1 = gen.generate_password("18:FD:74:F9:04:FC")
            >>> pwd2 = gen.generate_password("18:FD:74:F9:04:90")
            >>> pwd1 == pwd2
            True
            'c8fd-8bff'
        """
        mac_bytes = MikroTikPasswordGenerator.parse_mac(mac_address)
        
        if len(mac_bytes) != 6:
            raise ValueError(f"Expected 6 MAC bytes, got {len(mac_bytes)}")
        
        # Extract the first 5 MAC bytes (byte 5 is ignored)
        b0, b1, b2, b3, b4 = mac_bytes[:5]
        
        # Generate password bytes based on discovered pattern
        pwd_byte_0 = MikroTikPasswordGenerator.xor_op(b0, 0xD0)
        pwd_byte_1 = b1  # Direct copy
        pwd_byte_2 = MikroTikPasswordGenerator.bitwise_not(b2)  # NOT operation
        pwd_byte_3 = 0xFF  # Constant (or possibly derived from b3)
        
        pwd_bytes = [pwd_byte_0, pwd_byte_1, pwd_byte_2, pwd_byte_3]
        
        # Format as hex string with dash
        return MikroTikPasswordGenerator.format_password(pwd_bytes)


def test():
    """
    Test the generator with the two known MAC/password pairs.
    Both MACs should generate the same password.
    """
    mac1 = "18:FD:74:F9:04:FC"
    mac2 = "18:FD:74:F9:04:90"
    expected = "c8fd-8bff"
    
    gen = MikroTikPasswordGenerator()
    generated1 = gen.generate_password(mac1)
    generated2 = gen.generate_password(mac2)
    
    print("Testing MikroTik Password Generator")
    print("=" * 45)
    print()
    print("Test 1: First MAC address")
    print(f"  MAC Address:        {mac1}")
    print(f"  Expected Password:  {expected}")
    print(f"  Generated Password: {generated1}")
    result1 = generated1 == expected
    print(f"  Result:             {'✓ PASS' if result1 else '✗ FAIL'}")
    print()
    print("Test 2: Second MAC address (last byte different)")
    print(f"  MAC Address:        {mac2}")
    print(f"  Expected Password:  {expected}")
    print(f"  Generated Password: {generated2}")
    result2 = generated2 == expected
    print(f"  Result:             {'✓ PASS' if result2 else '✗ FAIL'}")
    print()
    
    # Key insight: both should be the same
    print(f"Both generate same password: {'✓ YES' if generated1 == generated2 else '✗ NO'}")
    print(f"Proves last byte is ignored:  {'✓ CONFIRMED' if result1 and result2 else '✗ NOT CONFIRMED'}")
    
    return result1 and result2


def main():
    """Main entry point with example usage."""
    import sys
    
    print("MikroTik RouterBoard Password Generator")
    print("=" * 45)
    print()
    
    # Run test
    test_passed = test()
    print()
    
    # Example usage
    if len(sys.argv) > 1:
        mac_address = sys.argv[1]
        try:
            gen = MikroTikPasswordGenerator()
            password = gen.generate_password(mac_address)
            print(f"MAC:      {mac_address}")
            print(f"Password: {password}")
        except ValueError as e:
            print(f"Error: {e}")
            sys.exit(1)
    else:
        print("Usage:")
        print("  python mikrotik_password.py <MAC_ADDRESS>")
        print()
        print("Examples:")
        print("  python mikrotik_password.py 18:FD:74:F9:04:FC")
        print("  python mikrotik_password.py 18-FD-74-F9-04:FC")
        print()
        print("Algorithm:")
        print("  PWD[0] = MAC[0] XOR 0xD0")
        print("  PWD[1] = MAC[1]")
        print("  PWD[2] = NOT(MAC[2])")
        print("  PWD[3] = 0xFF")
        print()
        print("Note: Only first 5 MAC bytes are used (byte 5 is ignored)")
    
    sys.exit(0 if test_passed else 1)


if __name__ == "__main__":
    main()
